Linux内核的进程管理

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:最终状态

 
   
 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;

        }

    }

猜你喜欢

转载自leilianjie.iteye.com/blog/2320592