Linux kernel development Chapter2 进程管理

 

1 进程管理相关代码
  1.1 thread_info结构,在文件<asm/thread_info.h>中定义
      struct thread_info {
           struct task_struct *task;
           struct exec_domain *exec_domain;
           unsigned long flags;
           __u32 cpu;
           __s32 preempt_count;
           mm_segment_t addr_limit;
           u8 supervisor_stack[0];
        };
        每个任务的thread_info结构在其内核栈顶端分配,其task域指向该任务的事件task_struct结构。
   1.2 任务队列(task list)
      双向循环链表,链表每一项都是一个task_struct(定义在include/linux/sched.h中),该进程描述符包含了一个仅进程的所有信息。
   1.3 slab
       linux通过slab机制分配task_struct结构,以达到对象复用和缓存着色的目的。
   1.4 pid
       linux通过pid(pid_t类型,integer)来标示每一个任务进程。为了与老版本unix和linux兼容,pid最大值为32767.如果需要更多进程,可以通过
修改/proc/sys/kernel/pid_max来提高上限。
   1.5 current宏
        current宏用于查找当前正在运行进程的进程描述符。不同的硬件体系结构的实现不同。
        在x86架构中,寄存器不多,不能使用专门的寄存器指向当前进程的task_struct.它的方式是通过在内核栈顶端创建thread_info结构,间接的
查找task_struct结构:
         (1)通过屏蔽栈指针(esp)的后13个有效位获得thread_info结构,在current_thread_info()函数完成:
                   movl $-8192, %eax               ;(-8192)十进制 = (...110 000 000 000 000)二进制
                   andl %esp, %eax
          (2)从thread_info结构的task域中获得task_struct的地址:
                   current_thread_info()->task;
         而在PowerPc机构上,使用了一个专门的寄存器来保存指向当前task_struct结构的地址。

   1.6 set_task_state(task, state)函数
         用于调整指定的进程状态。set_current_state(state)和set_task_state(current,state)含义相同。

2 进程状态
  。TASK_RUNNING, 进程可执行;进程或者正在执行,或者在运行队列中等待执行;
  。TASK_INTERRUPTIBLE,进程被阻塞,等待某些条件的达成。此外可以被进程唤醒并执行;
  。TASK_UNINTERRUPTIBLE, 与可终端状态完全相同,除了它不可以被进程唤醒,并因此使用较少;
  。TASK_ZOMBIE,进程已结束,但是父进程还未调用wait4()系统调用。为了让父进程能够获知它的消息,子进程的进程描述符仍然被保留;
  。Task_STOPPED,进程停止执行,由于接受到信号引起,如SIGSTOP,SIGTSTP等。在调式期间,任何信号都会使进程进入该状态。

 

3 进程创建
  unix的进程创建很特别。许多别的操作系统产生进程都是在新的地址空间创建进程,读入可执行文件,然后开始执行。而
unix则是使用fork()函数拷贝当前进程创建一个子进程,子进程与父进程的区别仅仅在于pid和ppid(父进程id)和某些
资源和统计量,然后调用exec()函数读取可执行文件并将其载入地址空间开始运行。
  两种方法达到的效果是类似的。
  3.1 写时拷贝
     传统的flok()系统调用将所有资源复制给新创建的进程,此实现效率低下。linux的fork()采用了写时拷贝,一种
推迟甚至免除拷贝数据的技术。
     调用fork()函数时,父子进程共享同一个资源拷贝而并不复制。只有在需要对资源进行写入时,数据才会被复制,以使
父子进程拥有不同的拷贝。当资源根本不会被写入的情况下(例如fork()之后立即调用exec()),他们就根本无须复制了,
使得fork()的实际开销就是赋值父进程的也表以及给子进程创建进程描述符,避免了大量无用拷贝,提高效率。
  3.2 fork()
     fork()函数通过调用系统调用clone(),clone()再调用do_fork(),do_fork()再调用copy_process(),最
后是进程开始运行。
     clone()系统调用通过一系列的参数标志来知名父子进程需要共享的资源。fork(),vfork(),__clone()等库函数
都根据各自需要的参数标志去调用clone(),然后根据上面的调用函数链完成创建工作。
     下面树copy_process()函数的工作:
      。 调用dupy_task_struct()为新进城创建一个内核栈,thread_info和task_struct,此时父子进程拥有相同值;
      。 检查当前用户所拥有的进程数目没有超过分配的资源限制;
      。 区分父子进程,子进程描述符内的许多成员被清0或设为初始值;
      。 设置子进程的状态为TASK_UNINTERRUPTIBLE以保证它不会投入运行;
      。 调用copy_flags()更新task_struct的flags成员。超级用户权限标志PF_SUPERPRIV标志清0,表明进程还没有
调用exec()函数的PF_FORKNOEXEC标志被设置;
      。 调用get_pid()为新进程获取一个有效的pid;
      。 根据传递给clone()的参数标志,拷贝或共享打开的文件,文件系统信息,信号处理函数,进程地址空间和命名空间等。
      。 让父子进程平分剩余的时间片;
      。 完成扫尾工作并返回一个指向子进程的指针。
   在do_fork()函数中,如果调用copy_process()函数成功返回,则内核唤醒子进程并选择父子进程中的子进程先运行。因为
一般子进程都会马上调用exec()函数,从而充分利用写时拷贝。避免附近城向地址空间写入数据而导致的不必要拷贝。

   3.3 vfork()
      vfork()和fork()功能类似,除了不拷贝父进程的页表项。子进程作为父进程的一个单独的线程在它的空间里运行,父进程
被阻塞,直到子进程退出或执行exec(),子进程不能向地址空间写入。
       在原来没有写时拷贝时,效果很好。现在,意义不是很大。

你可能感兴趣的:(Linux kernel development Chapter2 进程管理)