进程创建
过程创建是通过fork()系统调用实现的。 新创建的进程称为子进程,启动该进程的进程(或执行开始时的进程)称为父进程。
fork()系统调用返回三个值之一 -
- 表示错误的负值,即创建子进程失败。
- 子进程则返回一个零。
- 父进程则返回正值。 该值是新创建的子进程的进程ID。
1 | #include <stdio.h> |
编译执行:
1 | $ gcc test.c -o test |
子进程监视
孤儿进程
如果父进程早于子进程完成其任务,然后退出,会发生什么情况? 现在谁将是子进程的父进程? 子进程的父进程是初始进程,它是启动所有任务的第一个进程。(init 1 ,被系统所收留)
1 |
|
编译执行结果:
1 | root 2877 2674 0 09:42 pts/0 00:00:00 ./fork |
刚开始 创建了子进程 2878 ,其父进程为 2877 ,然而父进程等待3秒后退出,而子进程等待10秒,子进程变成孤儿进程;
监视子进程
为了避免父进程优先退出,子进程变成孤儿进程这种情况,父进程监视等待子进程
监视子进程的系统调用方式 -
- wait()
wait()系统调用暂停当前进程的执行并无限期地等待,直到其中一个子进程终止。 - waitpid()
waitpid()系统调用暂停当前进程的执行并无限期地等待,直到指定的子项(按照pid值)终止。 - waitid()
1 |
|
wait 函数等待子进程调用完毕后退出;
wait()系统调用有限制,例如它只能等到下一个子进程退出。 如果需要等待一个特定的子进程,那么使用wait()是不可能的,但是可以使用waitpid()系统调用。
其他进程
僵尸进程
有两个进程,即父进程和子进程。 父进程负责等待子进程,然后清理进程表中的子进程入口。 如果父进程没有准备好等待子进程,同时子进程就完成工作并退出呢? 这种情况时,子进程将成为僵尸进程。 当然,在父进程准备好之后,僵尸进程就会被清除。
1 |
|
编译执行结果:
1 | 父进程: pid is 4867 and ppid is 2674 |
守护进程
内核守护进程通常以内核守护进程(ksoftirqd,kblockd,kswapd等),打印守护进程(cupsd,lpd等),文件服务守护进程(smbd,nmbd等)的字母“d” ,电子邮件守护进程(sendmail,popd,smtpd等),远程登录和命令执行守护进程(sshd,in.telnetd等),引导和配置守护进程(dhcpd等),管理数据库守护进程(ypbind,ypserv等) ,udevd等),init进程(init),cron守护进程,atd守护进程等。
创建守护进程大概需要五步:
第1步 - 创建一个子进程。 现在我们有两个进程 - 父进程和子进程。通常流程是:SHELL -> 父进程 -> 子进程
第2步 - 通过退出终止父进程。 子进程现在成为孤儿进程,由初始(init)进程接管。
现在,这个流程层次是:初始(init)进程 -> 子进程。
第3步 - 如果调用进程不是进程组头,则调用setsid()系统调用会创建一个新的会话。 现在调用进程成为新会话的组头。 这个进程将是这个新的进程组和这个新的进程中唯一的进程。
第4步 - 将进程组ID和会话ID设置为调用进程的PID。
第5步 - 关闭终端和外壳现在与应用程序断开连接的过程的默认文件描述符(标准输入,标准输出和标准错误)。
1 |
|