关闭

Linux内核设计与实现阅读笔记——进程管理

发表于:2011-7-11 10:25

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Hanyan225    来源:51Testing软件测试网采编

  前边说的主要是用户级线程,现在我们接着来说说内核级线程。内核线程和用户级线程的区别在于内核线程没有独立的地址空间(实际上它的mm指针被设置为NULL).它也可以被调度也可以被抢占。内核线程也只能由其他内核线程创建。方法如下:int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags).新的任务也是通过像普通的clone()系统调用传递特定的flags参数而创建的。上面函数返回时,父进程退出,并返回一个子线程task_struct的指针。子进程开始运行fn指向的函数,arg是运行时需要用到的参数。一个特殊的clone标志CLONE_KERNEL定义了内核线程常用到参数标志:CLONE_FS, CLONE_FILES, CLONE_SIGHAND.大部分的内核线程把这个标志传递给它们的flags参数。

  我虽有才,还是不如书上说的好啊,讲了那么多的创建,出生,突然来点终结的的话, 多少有点感伤啊。但感伤归感伤,进程终归是要终结的。一个进程终结时必须释放它所占用的资源并把这一消息告诉其父进程。进程终止的方式有很多种,进程的析构发生在它调用exit()之后,即可能显示地调用这个系统调用,也可能隐式地从某个程序的主函数返回。当进程接受到它即不能处理也不能忽略的信号或异常时,它还可能被动地终结。但话说回来,不管进程怎么终结,该任务大部分都要靠do_exit()来完成,它定义在kernel/exit.c中,具体的工作如下所示:

  1、将tast_struct中的标志成员设置为PF_EXITING.

  2、如果BSD的进程记账功能是开启的,要调用acct_process来输出记账信息。

  3、调用__exit_mm()函数放弃进程占用的mm_struct,如果没有别的进程使用它们即没被共享,就彻底释放它们。

  4、调用sem_exit()函数。如果进程排队等候IPC信号,它则离开队列。

  5、调用__exit_files(), __exit_fs(), __exit_namespace()和exit_sighand()以分别递减文件描述符,文件系统数据,进程名字空间和信号处理函数的引用计数。当引用计数的值为0时,就代表没有进程在使用这些资源,此时就释放。

  6、把存放在task_struct的exit_code成员中的任务退出代码置为exit()提供的代码中,或者去完成任何其他由内核机制制定的退出动作。

  7、调用exit_notify()向父进程发送信号,将子进程的父进程重新设置为线程组中的其他线程或init进程,并把进程状态设为TASK_ZOMBIE.

  8、最后,调用schedule()切换到其他进程。

  经过上面的步骤,与进程相关的资源都被释放掉了,它以不能够再运行且处于TASK_ZOMBLE状态。现在它占用的所有资源就是保存threadk_info的内核栈和保存tast_struct结构的那一小片slab。此时进程存在的唯一目的就是向它的父进程提供信息。

  僵死的进程是不能再运行的。但系统仍然保留它的进程描述符,这样就有办法在子进程终结时仍可以获得它的信息。在父进程获得已终结的子进程的信息后,子进程的task_struct结构才被释放。

  熟悉linux系统中子进程相关知识的我们都知道在linux中有一系列wait()函数,这些函数都是基于系统调用wait4()实现的。它的动作就是挂起调用它的进程直到其中的一个子进程退出,此时函数会返回该退出子进程的PID.调用该函数时提供的指针会包含子函数退出时的退出代码。最终释放进程描述符时,会调用release_task(),完成的工作如下:

  1、调用free_uid()来减少该进程拥有者的进程使用计数。

  2、调用unhash_process()从pidhash上删除该进程,同时也要从task_list中删除该进程。

  3、如果这个进程正在被ptrace追踪,将追踪进程的父进程重设为其最初的父进程并将它从ptrace_list上删除。

  4、最后,调用put_task_struct释放进程内核栈和thread_info结构所占的页,并释放task_struct所占的slab高速缓存。

  至此,进程描述符和所有进程独享的资源就全部释放掉了。

  最后,我们讨论进程相关的最后一个问题:前边的一切看似很完美,很美好,美好让人还怕,不是么?哪里出问题了,父进程创建子进程,然后子进程退出处释放占用的资源并告诉父进程自己的PID以及退出状态。问题就出在这里,子进程一定能保证在父进程前边退出么,这是没办法保证的,所以必须要有机制来保证子进程在这种情况下能找到一个新的父进程。否则的话,这些成为孤儿的进程就会在退出时永远处于僵死状态,白白的耗费内存。解决这个问题的办法,就是给子进程在当前线程组内找一个线程作为父亲,如果这样也不行(运气太背了,不是)。在do_exit()会调用notify_present(),该函数会通过forget_original_parent来执行寻父过程,具体我就不讲了,讲到这个详细的地步,还不自己看看,我没办法了、

  一旦系统给进程成功地找到和设置了新的父进程,就不会再有出现驻留僵死进程的危险了,init进程会例行调用wait()来等待子进程,清除所有与其相关的僵死进程。

33/3<123
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号