进程创建:
①使用fork函数创建一个进程,创建的新进程被称为子进程。
#include
pid_t fork(void);
fork函数调用成功,返回两次:
返回值为0, 代表当前进程为子进程;
返回值为非负数,代表当前进程为父进程(返回新子进程的ID);
调用失败,返回值为-1.
②vfork函数也可以创建进程
区别:
1 、vfork直接使用父进程存储空间,不拷贝。
2 、 vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。
fork:
#include
#include
#include
int main()
{
pid_t pid;
pid_t pid2;
pid_t retpid;
pid=getpid();
printf("before fork:pid=%d\n",pid);
retpid=fork();
pid2=getpid();
printf("after fork:pid=%d\n",pid2);
if(pid==pid2){
printf("this is father print:retpid = %d\n",retpid);
}
else{
printf("this is child print,retpid = %d,child pid=%d\n",retpid,getpid());
}
return 0;
}
vfork:
#include
#include
#include
#include
int main()
{
pid_t pid;
int cnt = 0;
pid = vfork();
if(pid > 0){
while(1){
printf("cnt = %d\n",cnt);
printf("this is father print:retpid = %d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is child print:retpid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt = 3){
exit(0);
}
}
}
return 0;
}
运行结果:
before fork:pid=3082
after fork:pid=3082
this is father print:retpid =3083
after fork:pid=3083
this is child print,retpid = 0,child pid=3083
进程创建时发生什么?
fork调用之后,代码段共享,数据段copy。
不修改变量时,共享变量。修改子进程数据,父进程不改变。
现在很多实现并不执行父进程数据段、栈和堆的完全复制。作为替代,使用了写时复制(Copy-On-Write,COW)技术。
创建子进程的目的
①一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的----父进程等待客户端的服务请求。当这种请求达到时,父进程调用fork,使子进程处理此请求。父进程继续等待下一个服务请求到达。
②一个进程要执行一个不同的程序。这对shell是常见的情况。这种情况下,子进程从fork返回后立即调用exec
进程退出
正常退出:
1.Main函数调用return
2.进程调用exit(),标准c库
3.进程调用_exit()或者_Exit(),属于系统调用
补充:
4.进程最后一个线程返回
5.最后一个线程调用pthread_exit
异常退出:
1.调用abort
2.当进程收到某些信号时,如CTRL+C
3.最后一个线程对取消(cancellation)请求做出响应
对于三个终止(exit、_exit和_Exit),实现这个的方法是,将其退出状态(exit status)作为参数传递给函数。在异常终止时,内核(不是进程本身)产生一个指示其异常终止原因的异常终止状态(termination status)。在任意情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态。
父进程等待子进程退出并收集子进程的退出状态
子进程退出状态不被收集,变成僵尸(zombie)进程(僵死进程)
查看ps -aux|grep …,能看到z+(zombie僵死进程)
#include
#include
pid_t wait(int *status);//地址
pid_t waitpid(pid_t pid,int *status,int options);
//参数3一般用:WNOHANG()不阻塞
status参数:是一个整型数指针
非空:子进程退出状态放在它所指向的地址中
空:不关心退出状态
exit正常终止子进程返回的状态,则为真。这种情况需要执行宏WEXITSTATUS(status),取子进程传送给exit、_exit或_Exit参数的低8位。如果其所有子进程都还在运行,则阻塞;如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回;如果它没有任何子进程,则立即出错返回.
waitpid()有一个选项(options)可以使其不阻塞
#include
#include
#include
#include
int main()
{
pid_t pid;
int cnt = 0;
int status = 10;
pid = fork();
if(pid > 0){
wait(&status);
printf("child quit! child status = %d\n",WEXITSTATUS(status));
while(1){
printf("cnt = %d\n",cnt);
printf("this is father print:retpid = %d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is child print:retpid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt = 5){
exit(3);
}
}
}
return 0;
}
结果如下:
this is child print:retpid = 3116
this is child print:retpid = 3116
this is child print:retpid = 3116
this is child print:retpid = 3116
this is child print:retpid = 3116
child quit! child status = 3
cnt = 0
this is father print:retpid = 3115
cnt = 0
孤儿进程
父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程
Linux避免系统存在过多的孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程