您的位置:首页 > 其它

向下之旅(四):进程管理(二)

2016-03-11 15:50 197 查看
  许多其他的操作系统提供了产生(spawn)进程的机制,首先在新的地址空间里创建进程,读入可执行文件,最后开始执行。Unix则是通过两个单独的函数:fork()和exec()。首先,fork()通过拷贝当前进程创建一个新的子进程。子进程和父进程的区别仅仅在于PID(每个进程唯一)、PPID(父进程的进程号,被创建的子进程的PPID号则为被拷贝进程的PID号)和某些资源和统计量(例如挂起的信号),exec()函数负责读取可执行文件并将其载入地址空间中开始运行。

  传统的fork()系统调用直接把所有的资源复制给新创建的进程。这种实现过于简单并且效率低下,因为有可能拷贝的数据并没有用上。Linux的fork()使用写时拷贝页实现。即只有在需要写入的时候,数据才会复制,在页根本不会被写入的情况下(例如fork()后立即执行exec()),就无需复制了。fork()的实际开销就是复制父进程的页表以及给子进程创建唯一的进程描述符。一般情况下,进程创建后悔马上运行一个可执行的文件,这种优化可以避免拷贝大量根本就不会被使用的数据(地址空间常常包含数十兆的数据)。Linux通过clone()系统调用实现fork()。这个调用通过一系列的参数标志来指明父、子进程需要的共享资源。fork()、vfork()、和_clone()库函数都根据各自的需要的参数标志去调用clone()。然后在由clone()去调用do_fork()。vfork()和fork()的区别是vfork()不拷贝父进程的页表项。

  Microsoft Windows或是Sun Solaris等操作系统对于线程的支持是在其系统的内核中提供了专门支持线程的机制(这些系统常常把线程称为轻量级进程)。例如一个包含有四个线程的进程,在这种系统中通常会有一个包含指向四个不同线程的指针的进程描述符。线程本身再去描述它独占的资源。而Linux仅仅创建四个进程并分配四个普通的task_struct结构。建立这四个进程时指定它们共享的资源就行了。(实际上是父子进程共享地址空间、文件系统资源、文件描述符和信号处理程序)。

  传递给clone()的参数标志决定了新创建进程的行为方式和父子进程之间共享的资源种类。下图为clone()用到的参数标志以及他们的作用,这些是在<linux/sched.h>中定义的。



  内核线程——独立运行在内核空间的标准进程。与普通的进程的区别在于内核线程没有独立的地址空间(实际上它的mm指针被设置为NULL)。它们只在内核空间中运行。可以被调度,也可以被抢占。内核线程也只能由其他的内核线程创建:

  int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)

  新的任务也是通过向普通的clone()系统调用传递特定的flags参数创建的。一般情况下,内核线程会将它在创建时得到的函数永远的执行下去。

  进程终结时,内核必须释放它所占有的资源,并告知父进程。进程的析构发生在它调用exit()之后,然后通过do_exit()完成一些操作。释放掉与该进程相关联的所有资源(假设进程是这些资源的唯一使用者)。进程不可运行(此时也无地址空间)并处于TASK_ZOMBIE状态。它占用的所有资源就是内核栈、thread_info、task_struct结构。此时进程存在的唯一目的就是向它的父进程提供信息。父进程检索到信息后,或者通知内核那是无关的信息后,由进程所持有的剩余内存被释放,归还给系统使用,释放进程描述符。如果此时父进程提前退出时,系统会为它和它的所有兄弟重新寻找一个父进程,或者以init进程作为它们的父进程,这样就不会出现找不到父进程而一直处于僵死状态的危险了。

  存放和表示进程:task_struct thread_info

  创建进程: clone()和fork()

  参考自:《Linux Kernel Development》.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: