创建进程
众所周知linux由unix发展而来,所以本文先就unix进程先论为快,unix的进程创建很特别,许多其它的操作系统都提供了产生(spawn)进程的机制:首先在新的地址空间里创建进程,读入可执行文件,最后开始执行。unix采用与众不同的实现方式:
它把上述步骤分解到两个单独的函数中去执行fork()和exec(),调用fork()拷贝当前进程创建一个子进程(PID,某些资源量和统计量不一样)
写时拷贝
传统的fork()系统调用直接把所有的资源复制给新创建的进程,实际上这种实现过于简单并且效率底下,因为它拷贝的数据也许并不共享,linux的fork使用写时拷贝,只有在写入时,数据才会拷贝
fork
linux通过clone系统调用实现fork,fork,vforkg和__clone库函数根据各自需要的参数标志去调用clone,然后由clone去调用do_fork
do_fork完成创建中的大部分工作,它定义在kernel/fork.c文件中,该函数调用copy_process函数,然后让进程开始运行。copy_process完成的工作很有意思:
1.调用dup_task_struct为进程创建一个内核栈,thread_info结构和task_struct,此时父进程和子进程完全相同
2.检查并确保新创建子进程后,资源合法
3.进程描述符内的许多成员要被清0或初始化
4.子进程状态被设为TASK_UNINTERRUPTIBLE,以确保不会投入运行
5.copy_process调用copu_flags以更新task_struct的flags成员,降权
6.调用alloc_pid为新进程分配一个有效的pid
7.根据传递给clone的参数标志,copy_process拷贝或共享打开的文件,文件系统信息
8.返回一个指向子进程的指针
终结进程
一般来说,进程的析构时自身引起的。它发生在进程调用exit系统调用时,既可能显式调用系统调用,也可能隐式地从某个程序地主函数返回(c语言编译器会在main()函数后放置调用exit),不管怎么终结的,大部分都依靠do_exit它定义在kernel/exit.c中,工作:
1.将task_struct中的标志成员设为PF_EXITING
2.调用del_timer_sync删除任一核内定时器
3.调用acct_update_integrals输出记账信息
4.调用exit_mm释放mm_struct
5.调用sem_exit。如果进程排队等候IPC信号,它则离开队列
6.调用exit_files和exit_fs分别递减文件描述符和文件系统数据的引用计数,如果将为0则释放
7.设置退出代码
8.调用exit_notify向父进程发射信号,寻找养父
9.do_exit调用schedule切换到新的进程
孤儿进程
一个父进程退出,它的子进程还在运行,那么那些子进程将成为孤儿进程,孤儿进程将被init进程所收养,init进程pid为1是所有进程的祖宗,并由init进程对他们状态进行收集工作
僵尸进程
一个进程使用fork创建子进程,如果子进程退出,而父进程并未调用(进程调用exit后并不会完全删除,还会残留进程描述符,既没有资源也无法响应,处于僵死状态)wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍保存在系统中,这种进程称之为僵尸进程
如何查看?
利用ps即可查看进程信息,标为z的就是僵尸进程
如何处理?
改写父进程,在子进程死后为他收尸
Kill父进程,父进程死后子进程过继给init进程,init负责清理僵尸进程
调度进程
I/O消耗型和处理器消耗型
进程可被分为I/O消耗型和处理器消耗型:
前者进程的大部分事件用来提交I/O请求或等待I/O请求,因此运行状况并不固定
相反,后者是大多数时间用于执行代码,对于这种我们一般是提高运行时间,降低调度频率,当然划分并不角度,如X window服务器具有二向性
进程优先级
linux采用两种不同的优先级范围。第一种是nice值,值越小越优先,从-20到+19
第二种是实时优先级,从0到99它与nice值相反,越大越优先
未完待续......
标签:fork,调用,struct,init,exit,linux,进程 From: https://blog.csdn.net/KKKK250/article/details/136969407