现在有如下程序输出父子进程pid和ppid,后面几个部分都围绕此程序:
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main()
{
int n=0;
char*s = NULL;
pid_t pid = fork();
if(pid==-1)
{
exit(0);
}
if(pid==0)
{
n=3;
s="child";
}
else
{
n=6;
s = "parent";
}
for(int i=0;i<n;i++)
{
printf("s=%s,pid=%d,ppid=%d\n",s,getpid(),getppid());
//getpid()当前进程id号,getppid获得父进程的id号
}
}
关于命令提示符:
每当父进程结束,bash会打印一行命令提示符提示进程结束让我们继续输入,子进程结束没有单独的提示符:
子进程输出3次,父进程输出6次,结束后可以看到输出了这行提示
但如果将程序改为子进程输出6次,父进程输出3次:可以看到,父进程输出完后产生了提示符,此时子进程还没结束,在其后面继续输出,子进程结束后没有命令提示符。
所有进程之父——bash
通过获取pid和ppid,可以发现,虽然每次父进程的pid号都不一样,但其父进程id号都是3311,3311即为bash的id号(我的电脑中),终端中运行的第一个进程就是bash,它是所有新进程的父进程。
全新进程的产生有两步:1.fork(bash赋值自己)2.exec(替换成需要的进程)
(ps -f的父进程也是bash)
孤儿进程
当子进程输出6次,父进程输出3次时,可以发现,当父进程结束(输出命令提示符)之后,子进程的ppid改变了,不再是之前父进程的pid
这时候(父进程先结束,没有父进程)的子进程被称为孤儿进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。
孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
系统将为孤儿进程找一个新的父进程,早期为pid为1的init进程,但是由于目前内核发生变化,使用的不是这个了。
僵尸进程
僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。
子输出3次,父输出6次,当子进程结束后其详情中会有一个标识<defunct>,已经死了的。
这是因为子进程先于父进程结束,且父进程没有获取子进程的退出码。
退出码:exit(0) return 0,这个0就是退出码,退出码会被写入自己的PCB中,让父进程知道子进程的状态:是正常结束还是异常结束。PCB不会因进程结束而删除,它会一直保留到父进程获取它。
僵尸进程导致的问题:
因为僵尸进程永远不会再执行了,但是其PCB没删。
1.内存空间没有释放,如果长期这样消耗,会逐渐耗完内存空间;
2.pid号一直被占用,无法复用,软件层的资源也被占。
处理方法:
wait()获取子进程的退出码,处理僵尸进程。
加在父进程执行的位置:
它把退出码左移了8位存放,其他位置存其他的内容
孤儿进程被其他进程收养,就是为了获取这个进程的退出码,这样孤儿/僵尸进程也可以被释放;
系统进程都处理好了,不会产生僵尸进程。
wait()会阻塞一下进程,使子进程运行完了再执行父进程。