欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux内核的进程管理 博客分类: Linux c/c++Linux kernel linux多进程系统编程内核 

程序员文章站 2024-03-20 08:41:16
...

1,进程的概念

    进程就是处于执行期的程序,包括代码段,打开的文件,挂起的信号,内核内部数据,处理器状态,内存地址空间,一个或多个执行线程,数据段等。Linux内核对进程和线程并不做特殊的区分。

    内核把进程也叫做任务,进程描述符的数据结构类型为task_struct,在32位机器上有1.7KB。

    struct task_struct {
         volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
          struct thread_info *thread_info;
          unsigned long flags; /* per process flags, defined below */
          unsigned long ptrace;

          int prio, static_prio;
          struct list_head run_list;
          prio_array_t *array;

          struct mm_struct *mm, *active_mm;

         /* task state */
          struct linux_binfmt *binfmt;
          pid_t pid;
          pid_t tgid;
          struct task_struct *real_parent; /* real parent process (when being debugged) */
          struct task_struct *parent; /* parent process */
          struct list_head children; /* list of my children */
          struct list_head sibling; /* linkage in my parent's children list */
          struct task_struct *group_leader; /* threadgroup leader */
          struct timespec start_time;
         /* process credentials */
          uid_t uid,euid,suid,fsuid;
          gid_t gid,egid,sgid,fsgid;
          struct user_struct *user;
          unsigned short used_math;
          char comm[16];
         /* CPU-specific state of this task */
          struct thread_struct thread;
         /* filesystem information */
          struct fs_struct *fs;
         /* open file information */
          struct files_struct *files;
         /* signal handlers */
          struct signal_struct *signal;
          struct sighand_struct *sighand;

          sigset_t blocked, real_blocked;
          struct sigpending pending;
          sigset_t *notifier_mask;

          struct backing_dev_info *backing_dev_info;

          struct io_context *io_context;
    };

    进程描述符的state描述当前进程的状态:

    TASK_RUNNING:正在执行,或在运行队列中

    TASK_INTERRUPTIBLE:可中断的睡眠,阻塞状态;等待某些条件的成立,或接收到信号提前被唤醒。

    TASK_UNINTERRUPTIBLE:不可中断的睡眠,不对信号做出响应。

    TASK_TRACED:被其他进程跟踪的进程

    TASK_STOPPED:停止,通常在接收到SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU等信号。

    TASK_ZOMBIE:僵死状态,进程执行结束,父进程还没有发布waitpid()系统调用

    TASK_DEAD:最终状态

 
    Linux内核的进程管理
            
    
    博客分类: Linux c/c++Linux kernel linux多进程系统编程内核 
 2,进程创建

    fork()通过写时拷贝当前进程创建一个子进程,写时拷贝是一种推迟甚至免除拷贝数据的技术,内核并不复制整个进程地址空间,而是让父进程与子进程共享同一个拷贝,只有在需要写入的时候,数据才会复制,从而使各个进程拥有各自的拷贝。fork()只复制父进程的页表以及给子进程创建唯一的进程描述符,子进程与父进程的区别仅仅在于PID和某些资源和统计量的不同。exec()函数负责读取可执行文件并将其载入地址空间。

从内核的角度说,它没有线程这个概念,Linux把所有的线程当作进程来实现,内核没有特别的算法或定义特别的数据结构来表示线程,线程仅仅视为一个与其他进程共享某些资源的进程,每个线程拥有自己的task_struct,所以在内核看来,它就是一个普通的进程。

    创建线程:

    clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)

    创建进程:

     clone(SIGCHLD, 0)

3,CFS公平调度

    CFS的出发点基于一个简单的理念:进程调度的效果应如同系统具备一个理想中的完美多任务处理器,每一个进程将能获得1/N的处理器时间,N是指可运行进程的数量。在任何可测量的周期内,我们给予N个进程中每一个进程同样多的运行时间。

    CFS的做法是允许每一个进程运行一段时间,循环轮转,选择运行最少的进程作为下一个运行的进程,而不再采用分配给每一个进程时间片的做法了。CFS在所有可运行的进程总数基础上计算出一个进程应该运行多久,nice值在CFS中被作为进程获得的处理器运行比的权重。不是完善的公平,只是近乎完美,在多进程的环境下,降低了调度延迟带来的不公平性。

    CFS虚拟运行时间来记录一个程序运行了多长时间及它还应该运行多久,CFS调度算法的核心就是选择具有最小虚拟运行时间的进程,它使用红黑树来组成可运行进程队列。

4,调度器入口

    进程调度的主要入口点是函数schedule(),它是其他内核模块调用进程调度器的入口

5,进程睡眠

    进程睡眠有多种原因,无法读取IO更多的数据,无法获取信号量,互斥锁或某个硬件事件。进程把自己标记成休眠状态,从可执行进程的红黑树中移出,放入等待队列,然后调用schedule()函数选择和执行一个其他进程。唤醒的过程刚好相反,进程被置为可执行,从等待队列中移到调度器的红黑树中。休眠通过等待队列进行处理,等待队列是由等待某些事件发生的进程组成的简单链表,内核用wait_queue_head_t来表示等待队列:

    struct __wait_queue_head{

        spinlock_t lock;

        struct list_head task_list;

    };

    typedef struct __wait_queue_head_t wait_queue_head_t;

    等待队列链表的元素类型为wait_queue_t;

    struct __wait_queue{

        unsigned int flags;

        struct task_sruct *task;

        wait_queue_func_t func;

        struct list_head task_list;

    };

    typedef struct __wait_queue wait_queue_t;

 

    DEFINE_WAIT(wait);

    /*初始化一个wait_queue_t类型的变量,并用当前进程的描述符和唤醒函数

    autoremove_wake_function()的地址初始化。*/

    add_wait_queue(q, &wait); //将wait加入到等待队列q中

    while(!condition){

        prepare_to_wait(&q, &wait, TASK_INTERRUPTIBLE);

        if(signal_pending(current))

            /*处理信号*

        schedule();

    }

6,唤醒

    唤醒操作通过函数wake_up()进行,它会唤醒等待队列的所有进程,将进程设置为TASK_RUNNING状态,调用enqueue_task()将此进程放入到红黑树中。

    void wake_up(wait_queue_head_t *q)

    {

        struct list_head *tmp;

        wait_queue_t *curr;

        list_for_each(tmp, &q->task_list){

            curr = list_entry(tmp, wait_queue_t, task_list);

            if(curr->func(curr, TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE, 0, NULL) && curr->flags)

                break;

        }

    }

  • Linux内核的进程管理
            
    
    博客分类: Linux c/c++Linux kernel linux多进程系统编程内核 
  • 大小: 96 KB