Linux--进程创建(fork)-退出--孤儿进程

进程创建:
①使用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进程收留孤儿进程,变成孤儿进程的父进程

你可能感兴趣的:(Linux,linux,算法,运维)