• 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果:

    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()不阻塞
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    结果如下:
    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进程收留孤儿进程,变成孤儿进程的父进程

  • 相关阅读:
    长尾词-长尾词软件-长尾词挖掘工具免费
    Spring 常见面试题总结 | JavaGuide
    软件工程师备考
    Jenkins权限控制与job编排
    9篇分布式机器学习系统经典论文;深度学习硬件的黄金十年|AI系统前沿动态...
    ESP32 使用 LVGL 的简单介绍(ESP32 for Arduino)
    通过安装Element UI/Plus来学习vue之如何创建项目、搭建vue脚手架、npm下载等
    苹果电脑为什么无法删除U盘的文件?mac怎么删除移动硬盘里的文件
    【最有效】解决anaconda的虚拟环境重复目录问题
    数据分析-利用gpt进行电商平台用户细分专题分析(RFM模型)
  • 原文地址:https://blog.csdn.net/weixin_48208102/article/details/132684188