Linux学习之系统编程3(进程及wait函数)

写在前面:

我的Linux的学习之路非常坎坷。第一次学习Linux是在大一下的开学没多久,结果因为不会安装VMware就无疾而终了,可以说是没开始就失败了。第二次学习Linux是在大一下快放暑假(那个时候刚刚过完考试周),我没什么事做就又重拾Linux,不服输的我选择再战Linux,这一次学习还算顺利,虽然中间有些小插曲但是不影响整体学习进度, 我看着B站上的视频一点点学习Linux,基本上把Linux的基础指令学完了。学完之后我又遇到问题了,视频基本上到这就结束了,而我却不知道下一步该学什么,于是就没怎么碰Linux,结果没过多长时间我就把学的Linux指令忘的一干二净。现在是我第三次学习Linux,我决定重新开始学Linux,同时为了让自己学习的效果更好,我选择以写blog的形式逼迫自己每天把学习到的Linux知识整理下来。这也就是我写这个系列blog的原因。


wait&waitpid函数

1

我们先来看wait函数

传入参数:

一个int类型的指针,各位童鞋看名字应该就可以猜出来是表示状态信息的。没错,wstate就是表示回收的子进程 信息,我们可以调用一些宏函数去判断子进程的信息(详细的后面我在说)。当然,你也可以传NULL,也不会报错。

返回值:
  • 成功,回收进程的pid
  • 失败,-1,设置errno
函数的作用
  1. 阻塞等待子进程退出
  2. 清理子进程残留在内核的 pcb 资源
  3. 通过传出参数,得到子进程结束状态
利用宏函数获取子进程信息
  • WIFEXITED(status)——>为真,子进程正常终止。再次调用WSTATUS(status)——>得到子进程退出值
  • WIFSIGNALED(status)——>为真,子进程被信号终止。调用WTERMSIG(status)——>得到子进程异常终止的的信号编号。

一个进程终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的 PCB 还保留着,内
核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程
终止的信号是哪个。这个进程的父进程可以调用wait或者waitpid获取这些信息,然后彻底清除掉
这个进程。我们知道一个进程的退出状态可以在 shell 中用特殊变量$?查看,因为 shell 是它的父
进程,当它终止时,shell 调用 wait 或者waitpid得到它的退出状态,同时彻底清除掉这个进程。

举个栗子

源代码:

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

int main()
{
        pid_t pid,wpid;
        int state;
        pid=fork();
        if(pid==0)
        {
                printf("I'm child,my pid is %d\n",getpid());
                sleep(3);
                printf("child go to die\n");
        }
        else if(pid>0)
        {
                wpid=wait(&state);
                if(wpid>0)
                        printf("i am parent,wait child id is %d,wait  successfully\n",wpid);
                else
                        perror("wait error");
        }
        return 0;
}

效果:
2


接着看waitpid函数

3

传入参数
  • pid,有四种可能的值:
    • <-1:等待回收任何组id(gid)等于该值的绝对值的子进程。(manpage原文:meaning wait for any child process whose process group ID is equal to the absolute value of pid.)
    • -1:等待回收任意一个子进程。(manage原文:meaning wait for any child process.)
    • 0:等待回收任意一个组id和该进程组id一致的子进程。(manpage原文:meaning wait for any child process whose process group ID is equal to that of the calling process at the time of the call to waitpid())
    • <0:等待回收指定pid的子进程(这一种是用的最多的)。(manpage原文:meaning wait for the child whose process ID is equal to the value of pid.)
  • status:(传出) 回收进程的状态。
  • options:一般默认是阻塞,即一直等待直到回收一个子进程为止。也可以指定为WNOHANG 指定回收方式为,非阻塞。
返回值:
  • >0: 表成功回收的子进程 pid
  • 0 : 函数调用时, 参 3 指定了 WNOHANG, 并且,没有子进程结束。
  • -1: 失败。errno
举个栗子

我们演示一个小demo,之前我们循环创建过5个子进程,我们这次就来回收指定子进程(下面以回收第三个子进程为例)。

错误演示

源代码:

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

int main()
{
	pid_t pid,wpid;
	int i;
	for(i=0;i<5;i++)
	{
		if(fork()==0)
		{
			if(2==i)
				pid=getpid();
			break;
		}
	}
	if(5==i)
	{
		sleep(5);
		printf("--------in parent , before waitpid, pid= %d\n", pid);
		wpid(pid,NULL,0);
		printf("I'm parent, wait a child finish : %d \n", wpid);
	}
	else
	{
		sleep(i);
		printf("I'm %dth child, pid= %d\n", i+1, getpid());
	}
	return 0;
}				

效果:
3

错误分析:
上面的代码看似非常对,我们用pid来获取第三个子进程的id,然后让父进程使用waitpid来回收。但是,再仔细看,我们是在子进程把第三个子进程的id赋值给变量pid,但是由于父子进程间遵循的是"读时共享,写时复制",所以对于父进程中变量pid还是没有变,还是0.

正确演示

源代码:

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

int main()
{
        pid_t pid,wpid,tpid;
        int i;
        for(i=0;i<5;i++)
        {
                pid=fork();
                if(0==pid)
                        break;
                if(2==i)
                        tpid=pid;
        }

        if(5==i)
        {
                sleep(5);
                wpid=waitpid(tpid,NULL,0);
                printf("tpid=%d\n",tpid);
                if(wpid==-1)
                {
                        perror("waitpid error");
                        exit(1);
                }
                printf("i am parent,wait child id is %d\n",wpid);
        }
        else
        {
                sleep(i);
                printf("I am %dth child,my pid is %d\n",i+1,getpid());
        }
        return 0;
}

效果:
4

waitpid回收多个子进程

首先我们要无论是wait还是waitpid每一次调用都只能回收一个子进程
源代码:

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

int main()
{
        int i;
        pid_t pid,wpid;
        for(i=0;i<5;i++)
        {
                pid=fork();
                if(!pid)
                        break;
        }

        if(5==i)
        {
                while((wpid=wait(NULL))!=-1)
                {
                        printf("wait child pid is %d\n",wpid);
                }
        }
        else
        {
                sleep(i);
                printf("i am %dth child,my pid is %d\n",i+1,getpid());
        }
        return 0;
}

效果:
5

利用宏函数判断子进程返回状态

源代码:

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

int main()
{
        pid_t pid,wpid;
        int state;
        pid=fork();
        if(pid==0)
        {
                printf("i am child,my pid is %d\n",getpid());
                sleep(15);
                printf("i am going to die\n");
        }
        else
        {
                wpid=wait(&state);
                if(WIFEXITED(state))
                        printf("i am parent,my child was terminal normally, child's pid is %d\n",wpid);
                else if(WIFSIGNALED(state))
                        printf("my child was terminaled with signal %d,child's pid is %d\n",WTERMSIG(state),wpid);
        }
        return 0;
}

效果:
自然终止
6
被信号终止
7


写在最后

个人亲身经验:我们学习的一系列Linux命令,一定要自己亲手去敲。不要只是看别人敲代码,不要只是停留在眼睛看,脑袋以为自己懂了,等你实际上手去敲会发现许许多多的这样那样的问题。正可谓“键盘敲烂,月薪过万


如果你觉得我写的题解还不错的,请各位王子公主移步到我的其他题解看看

  1. 数据结构与算法部分(还在更新中):
  • C++ STL总结 - 基于算法竞赛(强力推荐
  • 动态规划——01背包问题
  • 动态规划——完全背包问题
  • 动态规划——多重背包问题
  • 动态规划——分组背包问题
  • 动态规划——最长上升子序列(LIS)
  • 二叉树的中序遍历(三种方法)
  • 最长回文子串
  • 最短路算法——Dijkstra(C++实现)
  • 最短路算法———Bellman_Ford算法(C++实现)
  • 最短路算法———SPFA算法(C++实现)
  • 最小生成树算法———prim算法(C++实现)
  • 最小生成树算法———Kruskal算法(C++实现)
  • 染色法判断二分图(C++实现)
  1. Linux部分(还在更新中):
  • Linux学习之初识Linux
  • Linux学习之命令行基础操作
  • Linux学习之基础命令(适合小白)
  • Linux学习之权限管理和用户管理
  • Linux学习之制作静态库和动态库
  • Linux学习之makefile
  • Linux学习之系统编程1(关于读写系统函数)

✨🎉总结

“种一颗树最好的是十年前,其次就是现在”
所以,
“让我们一起努力吧,去奔赴更高更远的山海”
在这里插入图片描述
如果有错误❌,欢迎指正哟😋

🎉如果觉得收获满满,可以动动小手,点点赞👍,支持一下哟🎉

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringFrameWork

SpringFrameWork简介 介绍springFrameWork框架 Spring Framework是一个为企业级应用程序开发提供全面基础设施支持的开源框架&#xff0c;通过集成IoC、DI和AOP等技术&#xff0c;使得应用程序的开发更加灵活、可维护和可扩展。Spring MVC、SpringBoot、Spring Cloud、Spring D…

java实现大文件分片上传

背景&#xff1a; 公司后台管理系统有个需求&#xff0c;需要上传体积比较大的文件&#xff1a;500M&#xff0d;1024M&#xff1b;此时普通的文件上传显然有些吃力了&#xff0c;加上我司服务器配置本就不高&#xff0c;带宽也不大&#xff0c;所以必须考虑多线程异步上传来提…

数据结构与算法python版本之线性结构之队列Quene

什么是队列&#xff1f; 队列是一种有次序的数据集合&#xff0c;其特征是&#xff1a;新数据项的添加总发生在一端&#xff08;通常称为“尾rear”端&#xff09;&#xff0c;而现存数据项的移除总发生在另一端&#xff08;通常称为“首front”端&#xff09;&#xff1b;当数…

缓存数据一致性策略如何分类?

一、概述 数据库与缓存数据一致性问题&#xff0c;一直以来都是大家比较关注的问题。针对一致性的解决方案也是非常多&#xff0c;以下主要针对方案的梳理与分类&#xff1a; 数据库数据与缓存数据一致性的方案&#xff0c;可以从不同的角度来分类&#xff0c;比如&#xff1…

稳定币记录

稳定币&#xff1a; 稳定币&#xff08;Stablecoin&#xff09;是一种加密货币&#xff0c;其设计目的是维持相对稳定的价值&#xff0c;通常与某种法定货币&#xff08;如美元、欧元&#xff09;或其他资产&#xff08;如黄金&#xff09;挂钩。稳定币通过将加密货币与相应的…

Flink-【时间语义、窗口、水位线】

1. 时间语义 1.1 事件时间&#xff1a;数据产生的事件&#xff08;机器时间&#xff09;&#xff1b; 1.2 处理时间&#xff1a;数据处理的时间&#xff08;系统时间&#xff09;。 &#x1f330;&#xff1a;可乐 可乐的生产日期 事件时间&#xff08;可乐产生的时间&…

计算机毕业设计 SpringBoot的停车场管理系统 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

逻辑回归算法到底能做什么

逻辑回归&#xff08;Logistic Regression&#xff09;是一种广义的线性回归分析模型&#xff0c;常用于数据挖掘、疾病自动诊断、经济预测等领域。它根据给定的自变量数据集来估计事件的发生概率。变量的范围在0和1之间&#xff0c;通常用于二分类问题&#xff0c;最终输出的预…

Opencv(C++)学习之cv::calcHist 任意bin数量进行直方图计算

**背景&#xff1a;**当前网上常见的直方图使用方法都是默认使用256的范围&#xff0c;而对于使用特定范围的直方图方法讲的不够清楚。仔细研究后总结如下&#xff1a; 1、常见使用方法&#xff0c;直接对灰度图按256个Bin进行计算。 Mat mHistUn; int channels[1] { 0 }; {…

键盘数字键打不出来怎么解锁?收藏好这4个简单方法!

“我在使用电脑进行办公时&#xff0c;突然发现我电脑键盘的数字键无法输入&#xff0c;这该怎么办呢&#xff1f;我应该如何解锁呢&#xff1f;请给我出出主意吧&#xff01;” 在日常使用电脑时&#xff0c;很多用户都需要使用键盘输入文字。但有时候部分用户也会遇到键盘数字…

你知道vue中key的原理吗?说说你对它的理解

一、Key是什么 开始之前&#xff0c;我们先还原两个实际工作场景 当我们在使用v-for时&#xff0c;需要给单元加上key <ul><li v-for"item in items" :key"item.id">...</li> </ul>用new Date()生成的时间戳作为key&#xff0c…

Docker 网络管理

一、Docker网络简介 Docker网络是容器化应用程序的重要组成部分&#xff0c;它使得容器之间可以互相通信和连接&#xff0c;同时也提供了容器与外部环境之间的隔离和连接。 二、Docker网络网络模式 Docker 提供了多种网络模式&#xff0c;可以通过docker network ls 命令查看…

springboot实现ChatGPT式调用(一次调用,持续返回)

下边实现了一个持续返回100以内随机数的接口&#xff0c;在接口超时之前会每隔1秒返回一个随机数 GetMapping(value "/getRandomNum", produces MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter getRandomNum() {SseEmitter emitter new SseEmitter();Th…

五、Spring AOP面向切面编程(基于注解方式实现和细节)

本章概要 Spring AOP底层技术组成初步实现获取通知细节信息切点表达式语法重用&#xff08;提取&#xff09;切点表达式环绕通知切面优先级设置CGLib动态代理生效注解实现小结 5.5.1 Spring AOP 底层技术组成 动态代理&#xff08;InvocationHandler&#xff09;&#xff1a;…

SQL Server 权限管理

CSDN 成就一亿技术人&#xff01; 2024年 第一篇 难度指数&#xff1a;* * CSDN 成就一亿技术人&#xff01; 目录 1. 权限管理 什么是权限管理&#xff1f; SQL server的安全机制 服务器级角色 数据库级角色 对象级角色 2. 创建用户 赋予权限 最重要的一步骤 1. 权限…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《计及源荷不确定性的综合能源系统日前-日内协调优化调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主的专栏栏目《论文与完整程序》 这个标题指的是一个综合能源系统&#xff08;包括多种能源资源和负荷需求&#xff09;&#xff0c;在考虑到源&#xff08;能源供给&#xff09;和荷&#…

代码随想录算法训练营Day16 | 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

LeetCode 654 最大二叉树 本题思路&#xff1a;我们可以看到每次其实这个找最大值&#xff0c;然后创建节点的过程就是一个二叉树的前序遍历的过程。所以&#xff0c;我们可以递归来完成它。 先创找到数组中&#xff0c;最大的值的下标&#xff0c;然后创建根节点然后根据下标…

【零基础入门TypeScript】TypeScript - 基本语法

目录 你的第一个 TypeScript 代码 编译并执行 TypeScript 程序 编译器标志 TypeScript 中的标识符 TypeScript ─ 关键字 空格和换行符 TypeScript 区分大小写 分号是可选的 TypeScript 中的注释 TypeScript 和面向对象 语法定义了一组编写程序的规则。每种语言规范都…

Linux系统:引导过程与服务控制

目录 一、linux系统引导过程 1、引导过程介绍 1.1 引导过程总览图 1.2 引导过程详解 1.3 系统初始化进程 1.4 Ststemd单元类型 1.5 运行级别所对应的Systemd目标 二、排除启动类故障 1、修复MBR扇区故障 1.1 故障原因 1.2 故障现象 1.3 解决思路 1.4 详细操作步骤…

密码学:带密钥的消息摘要算法一数字签名算法

文章目录 前言手写签名和数字签名前置知识点&#xff1a;消息摘要算法数字签名算法数字签名算法的由来数字签名算法在实际运用的过程附加&#xff1a;签名和摘要值的解释 数字签名算法的家谱数字签名算法的消息传递模型经典数字签名算法-RSA实现 数字签名标准算法-DSA实现 圆曲…