linux驻留进程是怎么实现的,Linux内核设计与实现阅读笔记——进程管理

前边说的主要是用户级线程,现在我们接着来说说内核级线程。内核线程和用户级线程的区别在于内核线程没有独立的地址空间(实际上它的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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值