Linux下的系统编程——进程的执行与回收(八)

news2024/11/25 6:44:30

前言:

前面我们对进程已经有了一个初步的了解与认识,现在让我们学习一下进程中一些函数的具体使用,比如exec可以执行一些指定的程序,wait / waitpid可以回收子进程,什么是孤儿进程,什么是僵尸进程,下面让我们一起对这些进行中的操作进行学习吧

目录

一、exec函数族

1.execlp:

  2.execl:

3.execvp

4.exec函数族的一般规律:

二、回收子进程

1.孤儿进程:

2 .僵尸进程:

​编辑

3.wait:

4.waitpid

 5.waitpid回收多个子进程


一、exec函数族

将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变换核丕换壳
 

1.execlp:

    int execlp(const char *file, const char *arg, ...);        借助 PATH 环境变量找寻待执行程序

        参1: 程序名

        参2: argv0

        参3: argv1

        ...: argvN

        哨兵:NULL

该函数通常用来调用系统程序。如: ls、date、cp、cat等命令。
 

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main(int argc,char *argv[])
{
	int i;
	pid_t pid;        //创建子进程

	if(pid == -1){
		perror("fork error");
		exit(1);
	}else if(pid == 0){        //子进程
		//execlp("ls","-l","-d","-h",NULL);//错误写法

         /************************************/
		 execlp("ls","ls","-l","-h",NULL);    
         /************************************/

		perror("exec error");
		exit(1);
	}
	else if(pid > 0){        //父进程
		sleep(1);
		printf("I'm parent : %d\n",getpid());
	}
	return 0;
}

 

 date命令的实现:

execlp("date","date",NULL);

 

  2.execl:

 int execl(const char *path, const char *arg, ...);    自己指定待执行程序路径。(路径+程序名)

#include <stdio.h>


int main(int argc, char **argv)
{
	
	printf("Hello, %s!\n", argv[1]);
	
	printf("Hello, world!\n");
	
	return 0;
}

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main(int argc,char *argv[])
{
	int i;
	pid_t pid;     //创建子进程

	if(pid == -1){
		perror("fork error");
		exit(1);
	}else if(pid == 0){    //子进程
		//execlp("ls","-l","-d","-h",NULL);
		//execlp("date","date",NULL);

         /************************************/
		 execl("./a.out","./a.out","linux",NULL);
         /************************************/

		perror("exec error");
		exit(1);
	}
	else if(pid > 0){    //父进程
		sleep(1);
		printf("I'm parent : %d\n",getpid());
	}
	return 0;
}

3.execvp

加载一个进程,使用自定义环境变量env


int execvp(const char*file, const char *argv[]);

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main(int argc,char *argv[])
{
	int i;
	pid_t pid;            创建子进程

	if(pid == -1){
		perror("fork error");
		exit(1);
	}else if(pid == 0){        //子进程
		//execlp("ls","-l","-d","-h",NULL);
		//execlp("date","date",NULL);
		//execl("./a.out","./a.out","linux",NULL);
		
        /************************************/
		char *argv[] = {"date",NULL};
		execvp("date",argv);
        /************************************/

		perror("exec error");
		exit(1);
	}
	else if(pid > 0){         //父进程
		sleep(1);
		printf("I'm parent : %d\n",getpid());
	}
	return 0;
}

4.exec函数族的一般规律:

        l:命令行参数列表

        p:使用PATH环境变量

        v:使用命令行参数数组

        exec函数一旦调试成功即执行新的程序,不返回。只要失败才返回,错误值-1。所以通常我们直接在exec函数调用后调用 perror()和exit()。无需if判断。· 

二、回收子进程

1.孤儿进程:

父进程死亡子进程进孤儿院

        孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。
模拟孤儿进程:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid;
    pid = fork();

    if (pid == 0) {
        while (1) {
            printf("I am child, my parent pid = %d\n", getppid());
            sleep(1);
        }
    } else if (pid > 0) {
        printf("I am parent, my pid is = %d\n", getpid());
        sleep(9);
        printf("------------parent going to die------------\n");
    } else {
        perror("fork");
        return 1;
    }

    return 0;
}

 查看进程状态:ps ajx

 进程孤儿院:

  1   2035   2035   2035 ?            -1 Ss    1001   0:00 /lib/systemd/systemd --user

 解决方法:

                 杀死子进程:     kill -9 4871

2 .僵尸进程:

子进程死亡,父进程一直不管 

        僵尸进程:进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(zombie)进程。(死亡以后没有回收)

        特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。

模拟僵尸进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid;
    pid = fork();

    if (pid == 0) {
        printf("---child, my parent= %d, going to sleep 10s\n", getppid());
        sleep(10);
        printf("-------------child die--------------\n");
    } else if (pid > 0) {
        while (1) {
            printf("I am parent, pid = %d, myson = %d\n", getpid(), pid);
            sleep(1);
        }
    } else {
        perror("fork");
        return 1;
    }

    return 0;
}

  查看进程状态:ps ajx

 解决方法:

        杀死父进程:   kill -9 4770

*3.wait:

    wait函数:    回收子进程退出资源, 阻塞回收任意一个。

    pid_t wait(int *status)

    

    参数:(传出) 回收进程的状态。

    返回值:成功: 回收进程的pid

                   失败: -1, errno

    函数作用1:    阻塞等待子进程退出

    函数作用2:    清理子进程残留在内核的 pcb 资源

    函数作用3:    通过传出参数,得到子进程结束状态

    
    获取子进程正常终止值

        WIFEXITED(status) --》 为真 --》调用 WEXITSTATUS(status) --》 得到 子进程 退出值。

    获取导致子进程异常终止信号

        WIFSIGNALED(status) --》 为真 --》调用 WTERMSIG(status) --》 得到 导致子进程异常终止的信号编号。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    pid_t pid, wpid;
    int status;

    pid = fork();
    if (pid == 0) {
        printf("---child, my id= %d, going to sleep 10s\n", getpid());
        sleep(10);
        printf("-------------child die--------------\n");
        return 73;
    } else if (pid > 0) {
        //wpid = wait(NULL);          // 不关心子进程结束原因
        wpid = wait(&status);       // 如果子进程未终止,父进程阻塞在这个函数上
        if (wpid == -1) {
            perror("wait error");
            exit(1);
        }
        if (WIFEXITED(status)) {        //为真,说明子进程正常终止. 
            printf("child exit with %d\n", WEXITSTATUS(status));

        }
        if (WIFSIGNALED(status)) {      //为真,说明子进程是被信号终止.

            printf("child kill with signal %d\n", WTERMSIG(status));
        }

        printf("------------parent wait finish: %d\n", wpid);
    } else {
        perror("fork");
        return 1;
    }

    return 0;
}

正常终止:

被信号终止:

*4.waitpid

waitpid函数:    指定某一个进程进行回收。可以设置非阻塞。          

    waitpid(-1, &status, 0) == wait(&status);

    pid_t waitpid(pid_t pid, int *status, int options)

    

参数:
        pid:指定回收某一个子进程pid

            > 0: 待回收的子进程pid

            -1:任意子进程

            0:同组的子进程。

        status:(传出) 回收进程的状态。

        options:WNOHANG 指定回收方式为,非阻塞。

  

 返回值:

        > 0 : 表成功回收的子进程 pid

        0 : 函数调用时, 参3 指定了WNOHANG, 并且,没有子进程结束。

        -1: 失败。errno

回收任意子进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
	int i;
	pid_t pid,wpid;
	for(i = 0;i < 5;i++){
		if(fork()==0)       //循环期间,子进程不fork
			break;
	}
	if(i == 5){           //父进程
        
        //wait(NULL);//一次wait/waitpid函数调用,只能回收一个子进程
        /*****************************************/
		wpid = waitpid(-1,NULL,WNOHANG);//回收任意子进程,没有结束的子进程,父进程直接返回0
        /****************************************/

		if(wpid == -1)
		{
			perror("waitpid error");
			exit(1);
		}
		printf("I'm parent ,wait a child finish :%d\n",wpid);
	}else{            //子进程,从break跳出
		sleep(i);
		printf("I'm %dth child\n",i+1);
	}
	return 0;
}


回收指定进程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
	int i;
	pid_t pid,wpid,tmpid;
	for(i = 0;i < 5;i++){
		pid = fork();
		if(pid == 0){       //循环期间,子进程不fork
			break;
		}
        if(i == 2){
           	tmpid = pid;
			printf("*************pid= %d***************\n",pid);
		}
	}
	if(i == 5){           //父进程,从表达式2跳出
        sleep(5);		  //设置睡眠,等所有子进程结束后再回收

        //wait(NULL);	   //一次wait/waitpid函数调用,只能回收一个子进程
        //wpid = waitpid(-1,NULL,WNOHANG);     //回收任意子进程,没有结束的子进程,父进程直接返回0
		printf("I am parent , before waitpid , pid = %d\n",tmpid);

        /********将前面sleep(5)屏蔽***************/
		//wpid = waitpid(tmpid,NULL,0);         //指定一个进程回收,阻塞回收
        /****************************************/
        
        /*****************************************/
		wpid = waitpid(tmpid,NULL,WNOHANG);     //指定一个进程回收,不阻塞
        /****************************************/

		if(wpid == -1)
		{
			perror("waitpid error");
			exit(1);
		}
		printf("I'm parent ,wait a child finish :%d\n",wpid);	//wpid回收的是真正的子进程id
	}else{            //子进程,从break跳出
		sleep(i);
		printf("I'm %dth child,pid = %d\n",i+1,getpid());
	}
	return 0;
}

注意:

        一次wait/waitpid调用只能回收一个子进程,无法回收他孙子辈的进程,多次清理需要while

 5.waitpid回收多个子进程

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
	int i;
	pid_t pid,wpid;
	for(i = 0;i < 5;i++){
		pid = fork();
		if(pid == 0){       //循环期间,子进程不fork
			break;
		}
	}
	if(i == 5){    //父进程
		/**********使用阻塞回收子进程********/

		while((wpid = waitpid(-1,NULL,0))){
			printf("wait child %d\n",wpid);
		}

		/***********************************/
	}else{        //子进程
		sleep(i);
		printf("I'm %dth child ,pid =%d\n",i+1,getpid());
	}

   	return 0;
}

结束一个回收一个

之后返回-1,表示没有失败了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>

int main(int argc,char *argv[])
{
	int i;
	pid_t pid,wpid;
	for(i = 0;i < 5;i++){
		pid = fork();
		if(pid == 0){       //循环期间,子进程不fork
			break;
		}
	}
	if(i == 5){
		/*********使用阻塞回收子进程***********/
		/*
		while((wpid = waitpid(-1,NULL,0))){
			printf("wait child %d\n",wpid);
		}
		*/
		/***********************************/
		
        /*******使用非阻塞方式回收子进程******/

		while((wpid = waitpid(-1,NULL,WNOHANG)) != -1){
			if(wpid > 0){	
				printf("wait child %d\n",wpid);
			}else if(wpid == 0){
				sleep(1);
				continue;
			}
        /************************************/

		}

	}else{
		sleep(i);
		printf("I'm %dth child ,pid =%d\n",i+1,getpid());
	}

   	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/979086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

性能测试实现天罗地网对各个中间件实现监控

名师出高徒&#xff0c;我亲自带你出征&#xff0c;直捣黄龙。高手都是顶峰相见&#xff01;将军有剑 不斩苍蝇&#xff0c;将军赶路&#xff0c;不追小兔。赶紧上车 带你入行就是高手。

【ccf-csp题解】第1次csp认证-第三题-命令行选项-题解

题目描述 思路讲解 本题是一个简单的字符串模拟题&#xff0c;这种题目是csp认证第三题的常客 大致思路是用两个bool数组记录某一个选项&#xff08;0--25下标对应小写字母a--z&#xff09;&#xff0c;第一个数组中无参选项为true&#xff0c;第二个数组中有参选项为true&a…

【算法系列篇】分治-归并

文章目录 前言什么是归并算法1. 排序数组1.1 题目要求1.2 做题思路1.3 Java代码实现 2. 数组中逆序对2.1 题目要求2.2 做题思路2.3 Java代码实现 3. 计算右侧小于当前元素的个数3.1 题目要求3.2 做题思路3.3 Java代码实现 4. 翻转对4.1 题目要求4.2 做题思路4.3 Java代码实现 总…

金融信创,软件规划需关注自主安全及生态建设

软件信创化&#xff0c;就是信息技术软件应用创新发展的意思&#xff08;简称为“信创”&#xff09;。 相信在中国&#xff0c;企业对于“信创化”这个概念并不陌生。「国强则民强」&#xff0c;今年来中国经济的快速发展&#xff0c;受到了各大欧美强国的“卡脖子”操作的影…

CNN(七):ResNeXt-50算法的思考

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 在进行ResNeXt-50实战练习时&#xff0c;我也跟其他学员一样有这个疑惑&#xff0c;如下图所示&#xff1a; 反复查看代码&#xff0c;仍然有…

探讨前后端分离开发的优势、实践以及如何实现更好的用户体验?

随着互联网技术的迅猛发展&#xff0c;前后端分离开发已经成为现代软件开发的一种重要趋势。这种开发模式将前端和后端的开发工作分开&#xff0c;通过清晰的接口协议进行通信&#xff0c;旨在优化开发流程、提升团队协作效率&#xff0c;并最终改善用户体验。本文将深入探讨前…

Elasticsearch——Docker单机部署安装

文章目录 1 简介2 Docker安装与配置2.1 安装Docker2.2 配置Docker镜像加速器2.3 调整Docker资源限制 3 准备Elasticsearch Docker镜像3.1 下载Elasticsearch镜像3.2 自定义镜像配置3.3执行Docker Compose 4 运行Elasticsearch容器4.1 创建Elasticsearch容器4.2 修改配置文件4.3…

Mac 搭建本地服务器

文章目录 一、启动服务器二、添加文件到本地服务三、手机/其他电脑 访问本机服务器 MacOS 自带Apatch 服务器。所以我这里选择Apatch服务器搭建 一、启动服务器 在safari中输入 http://127.0.0.1/ &#xff0c;如果页面出现 it works&#xff0c;则代表访问成功。启动服务器 …

开开心心带你学习MySQL数据库之第五篇

&#x1f63a;欢迎来到我的博客, 记得点赞&#x1f44d;收藏⭐️留言✍️&#x1f431; &#x1f409;做为一个怪兽&#xff0c;我的目标是少消灭一个奥特曼&#x1f409; &#x1f4d6;希望我写的博客对你有所帮助,如有不足,请指正&#x1f4d6; chatgpt 是否能够代替程序猿?…

AI时代:探索机器学习与深度学习的融合之旅

文章目录 1. 机器学习和深度学习简介1.1 机器学习1.2 深度学习 2. 为什么融合是必要的&#xff1f;2.1 数据增强2.2 模型融合 3. 深入分析&#xff1a;案例研究3.1 传统机器学习方法3.2 深度学习方法3.3 融合方法 4. 未来展望结论 &#x1f389;欢迎来到AIGC人工智能专栏~AI时代…

【力扣每日一题】2023.9.6 最深叶节点的最近公共祖先

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一棵二叉树&#xff0c;让我们找出所有最深叶子节点的最近公共祖先。 我们一步一步剖析&#xff0c;我们先找出最深叶子节点&…

SpringBoot Admin监控平台《一》平台搭建及基础介绍

一、SpringBoot Admin简介 1.1.简介 Spring Boot Admin是一个管理和监控Spring Boot应用程序的开源项目&#xff0c;在对单一应用服务监控的同时也提供了集群监控方案&#xff0c;支持通过eureka、consul、zookeeper等注册中心的方式实现多服务监控与管理。Spring Boot Admin…

npm修改为国内镜像

npm config get registry 查看下载地址&#xff0c;默认是https://registry.npmjs.org/ 这是国外的地址&#xff0c;npm下载很慢 npm config set registry https://registry.npmmirror.com/ 使用此指令&#xff0c;修改为淘宝镜像&#xff0c;输入指令后检查是否修改成功 …

02-Tomcat打破双亲委派机制

Tomcat 如果使用默认的双亲委派类加载机制行不行&#xff1f; 我们思考一下&#xff1a;Tomcat是个web容器&#xff0c; 那么它要解决什么问题&#xff1a; 一个web容器可能需要部署两个应用程序&#xff0c;不同的应用程序可能会依赖同一个第三方类库的不同版本&#xff0c;…

配电网智能软开关(sop)规划模型matlab

目录 1 主要内容 2 部分程序 3 程序结果 1 主要内容 该程序参考文献《基于改进灵敏度分析的有源配电网智能软开关优化配置》&#xff0c;采用二阶锥算法&#xff0c;以改进的IEEE33节点配电系统模型作为分析对象&#xff0c;以联络开关位置作为sop安装备选位置&#xff0c;以…

最适合运动的耳机类型是什么、最适合运动的耳机推荐

运动和音乐天生就是绝佳的组合&#xff0c;因此对于许多运动爱好者来说&#xff0c;耳机一直都是必备的装备。通过音乐&#xff0c;我们可以带着节奏感去探索世界&#xff0c;让我们的运动过程更加有动力。 一款优秀的运动耳机需要具备舒适的佩戴感、稳固性和防水性能。作为一位…

实践和项目:解决实际问题时,选择合适的数据结构和算法

文章目录 选择合适的数据结构数组链表栈队列树图哈希表 选择合适的算法实践和项目 &#x1f389;欢迎来到数据结构学习专栏~实践和项目&#xff1a;解决实际问题时&#xff0c;选择合适的数据结构和算法 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT…

水处理行业污水处理厂电能质量监测与治理系统解决方案-安科瑞黄安南

摘要&#xff1a;在水处理行业供配电系统中&#xff0c;涉及曝气风机、提升泵、污泥脱水设备等感性负荷设备&#xff0c;导致异步电动机产生较多无功功率和大量的谐波&#xff0c;使部分设备表现出轻载或不满载运行状况降低功率因数&#xff0c;以及谐波对配电系统、负载产生较…

leetcode 234. 回文链表

2023.9.5 本题先将链表的节点值移到数组中&#xff0c;再用双指针去判断该数组是否为回文的即可。 代码如下&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* …

ST表(转载自其它博主)

文章目录 一、简介二、Leetcode题目补充1. 2023/9/6 更新 一、简介 ST表&#xff1a;https://zhuanlan.zhihu.com/p/123360481 二、Leetcode题目补充 1. 2023/9/6 更新 239. 滑动窗口最大值 class Solution {public int[] maxSlidingWindow(int[] nums, int k) {//记录数…