头歌 进程管理之二(wait、exec、system的使用)
第1关:进程等待
任务描述
通过上一个实训的学习,我们学会了使用fork创建子进程,在使用fork创建子进程的时候,子进程和父进程的执行顺序是无法预知的。本关我们将介绍如何使得fork创建出来的子进程先执行,随后父进程再执行。
本关任务:学会在多进程中,学会进程的等待处理。
相关知识
在上一个实训的学习,我们知道使用fork可以创建一个进程,并且创建的子进程其执行顺序可能在父进程前,也可能在父进程后。因此,这是由于fork创建的进程是由操作系统调度器来决定执行顺序的。如果,当子进程在父进程前结束,则内核会把子进程设置为一个特殊的状态。这种状态的进程叫做僵死进程(zombie)。尽管子进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存。但是仍然保留了一些信息(如进程号pid退出状态 运行时间等)。只有父进程获取了子进程的这些信息后,子进程才会彻底的被销毁,否则一直保持僵死状态。如果系统中产生大量的僵尸进程,将导致系统没有可用的进程号,从而导致系统不能创建新的进程。
Linux处理僵死进程的方法之一是使用进程等待的系统调用wait和waitpid来使得父进程获取子进程的终止信息。
wait函数和waitpid函数是系统调用函数,因此使用man 2 函数名来查看其使用方法。
wait函数使用方法
wait函数的具体的说明如下:
需要的头文件如下:
#include <sys/types.h>
#include <sys/wait.h>
wait函数格式如下:
pid_t wait(int *status);
参数说明:
参数status是一个整数指针,当子进程结束时,将子进程的结束状态字存放在该指针指向的缓存区。利用这个状态字,需要时可以使用一些由 Linux 系统定义的宏来了解子程序结束的原因。这些宏的定义与作用如下:
函数返回值说明: 调用成功时,返回值为被置于等待状态的进程的 pid;执行失败返回-1并设置错误代码errno。
案例演示1:
编写一个程序,使用fork函数与wait函数结合创建一个新进程,使得新创建的子进程在父进程前执行。详细代码如下所示:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{pid_t pid;pid = fork();if(pid == -1){//创建进程失败printf("创建进程失败(%s)!\n", strerror(errno));return -1;}else if(pid == 0){//子进程sleep(2);printf("This is child process\n");exit(1);}else{//父进程int status;if(wait(&status) != -1){if(WIFEXITED(status))printf("子进程正常退出,退出代码:%d\n", WEXITSTATUS(status));}printf("This is parent process\n");exit(0);}
}
将以上代码保存为waitProcess.c文件,编译执行。可以看到执行waitProcess程序后,尽管子进程使用sleep睡眠了2秒,还是子进程先执行,然后父进程才执行。这就是wait的作用,使得父进程一直等待子进程执行结束后才继续执行。
waitpid函数使用方法
waitpid函数的具体的说明如下:
需要的头文件如下:
#include <sys/types.h>
#include <sys/wait.h>
waitpid函数格式如下:
pid_t waitpid(pid_t pid, int *status, int options);
参数说明: pid:用于指定所等待的进程。其取值和相应的含义如下所示:
参数option则用于指定进程所做操作。其取值和相应的含义如下所示:
参数status是一个整数指针,当子进程结束时,将子进程的结束状态字存放在该指针指向的缓存区。
函数返回值说明: 调用成功时,返回收集到的子进程的进程pid;当设置选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;执行失败返回-1并设置错误代码errno。
案例演示1:
编写一个程序,使用fork函数与waitpid函数结合创建一个新进程,使得新创建的子进程在父进程前执行。详细代码如下所示:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{pid_t pid;pid = fork();if(pid == -1){//创建进程失败printf("创建进程失败(%s)!\n", strerror(errno));return -1;}else if(pid == 0){//子进程sleep(2);printf("This is child process\n");exit(1);}else{//父进程int status;if(waitpid(-1, &status, 0) != -1){if(WIFEXITED(status))printf("子进程正常退出,退出代码:%d\n", WEXITSTATUS(status));}printf("This is parent process\n");exit(0);}
}
将以上代码保存为waitpidProcess.c文件,编译执行。可以看到执行waitpidProcess程序后,尽管子进程使用sleep睡眠了2秒,还是子进程先执行,然后父进程才执行。waitpid函数可以实现与wait函数相同的功能。
编程要求
本关的编程任务是补全右侧代码片段中Begin至End中间的代码,具体要求如下:
补全waitProcess函数,等待子进程结束,并且返回子进程的退出的代码。
测试说明
本关的测试需要用户在右侧代码页中补全代码,然后点击评测按钮,平台会自动验证用户是否按照要求去检测结果。
开始你的任务吧,祝你成功!
解答:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>/************************* 返回值: 调用成功且子进程正常退出返回退出代码,否则返回-1
*************************/
int waitProcess()
{int status = -1;/********** BEGIN **********/pid_t pid;pid = fork();if(pid == -1){//创建进程失败return -1;}else if(pid == 0){//子进程sleep(2);printf("This is child process\n");exit(1);}else{//父进程int status;if(waitpid(-1, &status, 0) != -1){if(WIFEXITED(status))return WEXITSTATUS(status);}exit(0);}/********** END **********/return status;
}
第2关:进程创建操作-exec函数族
任务描述
在上一个实训中我们学习使用fork函数创建新进程,本关我们将介绍如何另一种创建新进程的库函数。
本关任务:学会使用C语言在Linux系统中使用exec族的系统调用创建一个新的进程。
相关知识
在上一个实训中提到,fork函数创建的子进程可以通过调用exec函数族来正确退出。其原理是,使用exec函数族可以执行一个新的程序,并且以新的子进程来完全替换原有的进程地址空间。
exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
通常exec函数族用来与fork函数结合一起使用。使用fork函数创建一个子进程,然后在子进程中使用exec函数族来执行一个新的程序。当在由fork创建的子进程中使用exec函数族来执行新程序时,子进程的地址空间会被新执行的程序完全覆盖,并且此时fork的父进程与子进程地址空间被分离开,也就是使用exec函数族创建的新程序不会对fork的父进程造成任何影响。
exec函数族是库函数,因此使用man 3 exec来查看其使用方法。
使用exec函数族创建进程
exec函数族的具体的说明如下:
需要的头文件如下:
#include <unistd.h>
函数族格式如下:
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
参数说明:
1、函数名中含有字母l的函数,其参数个数不定。其参数由所调用程序的命令行参数列表组成,最后一个NULL表示结束。
2、函数名中含所有字母v的函数,则是使用一个字符串数组指针argv指向参数列表,这一字符串数组和含有l的函数中的参数列表完全相同,也同样以NULL结束。
3、函数名中含有字母p的函数可以自动在环境变量PATH指定的路径中搜索要执行的程序。因此它的第一个参数为file表示可执行函数的文件名。而其他函数则需要用户在参数列表中指定该程序路径,其第一个参数path 是路径名。路径的指定可以是绝对路径,也可一个是相对路径。但出于对系统安全的考虑,建议使用绝对路径而尽量避免使用相对路径。
4、函数名中含有字母e的函数,比其他函数多含有一个参数envp。该参数是字符串数组指针,用于制定环境变量。调用这两个函数时,可以由用户自行设定子进程的环境变量,存放在参数envp所指向的字符串数组中。这个字符串数组也必须由NULL结束。其他函数则是接收当前环境。
函数返回值说明: 只有当函数执行失败时,exec函数族才会返回-1并设置错误代码errno。当执行成功时,exec函数族是不会返回任何值。
案例演示1:
编写一个程序,使用fork函数与exec函数族结合创建一个新进程,并在子进程中执行touch testFile命令创建一个testFile文件,在父进程中返回进程ID。详细代码如下所示:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{pid_t pid;pid = fork();if(pid == -1){//创建进程失败printf("创建进程失败(%s)!\n", strerror(errno));return -1;}else if(pid == 0){//子进程if(execlp("touch", "touch", "testFile", NULL) < 0){//执行execlp函数失败exit(-1);}}else{//父进程printf("当前进程为父进程:pid(%d),ppid(%d)\n", getpid(), getppid());}//如果执行execlp成功,则以下代码只会被父进程执行exit(0);
}
将以上代码保存为execlProcess.c文件,编译执行。可以看到执行execlProcess程序后,在当前目录下创建了一个名为testFile的文件。
编程要求
本关的编程任务是补全右侧代码片段中Begin至End中间的代码,具体要求如下:
补全execlProcess函数,使用fork函数创建进程,并在子进程中调用创建一个名为testDir的目录,在父进程中输出"Parent Process"字符串。
测试说明
本关的测试需要用户在右侧代码页中补全代码,然后点击评测按钮,平台会自动验证用户是否按照要求去检测结果。
开始你的任务吧,祝你成功!
解答:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>/************************* 提示: 在子进程中如果执行exec函数失败要使用exit函数正确退出子进程
*************************/
int execlProcess()
{pid_t pid = vfork();if(pid == -1){printf("创建子进程失败(%s)\n", strerror(errno));return -1;}else if(pid == 0){//子进程/********** BEGIN **********/if(execlp("touch", "touch", "testFile", NULL) < 0){//执行execlp函数失败exit(-1);}/********** END **********/}else{//父进程/********** BEGIN **********/printf("Parent Process");/********** END **********/}
}
第3关:进程创建操作-system
任务描述
在上一关中我们学习使用exec函数族执行新的程序,本关我们将介绍如何另一种执行新的程序的库函数。
本关任务:学会使用C语言在Linux系统中使用system库函数来执行一个新的程序。
相关知识
在上一关中我们学习了如何使用vfork结合exec函数族来执行一个新的程序。在Linux系统中提供了另一个库函数来实现执行一个新的程序。那就是system函数。
system函数是一个和操作系统相关紧密的函数。用户可以使用它来在用户自己的程序中调用系统提供的各种命令。因此,system函数使用起来是很方便的,用户可以在自己的程序中很方便地实现一些功能。
执行系统的命令行,其实也是调用程序创建一个进程来实现的。实际上,system函数的实现正是通过调用fork、exec和waitpid函数来完成的。详细的实现思路是:首先使用fork创建一个新的进程,并且在子进程中通过调用exec函数族来执行一个新程序,在父进程中通过waitpid函数等待子进程的结束,同时也获取子进程退出代码。
system函数是库函数,因此使用man 3 system来查看其使用方法。
使用system函数执行程序一个新程序
system函数的具体的说明如下:
需要的头文件如下:
#include <stdlib.h>
函数格式如下:
int system(const char *command);
参数说明:
command:需要被执行的命令;
函数返回值说明:
执行成功,返回值是执行命令得到的返回状态,如同wait的返回值一样。执行失败时返回的值分为以下几种情况:执行system函数时,它将调用fork、exec和waitpid函数。其中任意一个调用失败都可以使得system函数的调用失败。如果调用fork函数出错,则返回值为-1,errno被设置为相应错误;如果调用exec时失败,则表示shell无法执行所设命令,返回值为shell操作的返回值;如果调用waitpid函数失败,则返回值也为-1,errno被置为相应值。
案例演示1:
编写一个程序,使用system函数来执行touch testFile命令创建一个testFile文件。详细代码如下所示:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{int ret = system("touch testFile");if(ret == -1){printf("执行 touch testFile 命令失败(%s)\n", strerror(errno));return -1;}return 0;
}
将以上代码保存为system.c文件,编译执行。可以看到执行system程序后,在当前目录下创建了一个名为testFile的文件。
编程要求
本关的编程任务是补全右侧代码片段中Begin至End中间的代码,具体要求如下:
补全createProcess函数,使用system函数创建一个名为testDir的目录((使用system('mkdir testDir')创建目录) 调用成功返回命令的状态码,失败返回-1)。
测试说明
本关的测试需要用户在右侧代码页中补全代码,然后点击评测按钮,平台会自动验证用户是否按照要求去检测结果。
开始你的任务吧,祝你成功!
解答:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>/************************* 返回值: 调用成功返回命令的状态码,失败返回-1
*************************/
int createProcess()
{int ret = -1;/********** BEGIN **********/ret=system("mkdir testDir");if(ret == -1){return -1;}/********** END **********/return ret;
}
相关文章:

头歌 进程管理之二(wait、exec、system的使用)
第1关:进程等待 任务描述 通过上一个实训的学习,我们学会了使用fork创建子进程,在使用fork创建子进程的时候,子进程和父进程的执行顺序是无法预知的。本关我们将介绍如何使得fork创建出来的子进程先执行,随后父进程再…...

详解日志格式配置:XML 与 Spring Boot 配置文件格式
详解日志格式配置:XML 与 Spring Boot 配置文件格式 日志是现代应用程序中不可或缺的一部分,通过定制化日志格式和颜色,开发人员可以更方便地调试和监控应用。本文将深入讲解如何在 XML 配置文件 和 Spring Boot 配置文件 中设置日志格式&am…...

JDK21新特性
目录 虚拟线程(JEP 444): 顺序集合(JEP 431): 字符串模板(JEP 430): 模式匹配的增强(JEP 440、441以及443): 结构化并发和作用域值…...

SqlDataAdapter
SqlDataAdapter 是 .NET Framework 和 .NET Core 中提供的一个数据适配器类,属于 System.Data.SqlClient 命名空间(或在 .NET 6 中属于 Microsoft.Data.SqlClient 命名空间)。它的作用是充当数据源(如 SQL Server 数据库ÿ…...

AI赋能:构建安全可信的智能电子档案库
在档案的政策与法规上,《中华人民共和国档案法》2020年修订新增,对电子档案的合法要件、地位和作用、安全管理要求和信息化系统建设等方面作出了明确规定,保障数字资源的安全保存和有效利用。 日前,国家档案局令第22号公布《电子…...

分类预测 | PSO-PNN粒子群优化概率神经网络多特征分类预测
分类预测 | PSO-PNN粒子群优化概率神经网络多特征分类预测 目录 分类预测 | PSO-PNN粒子群优化概率神经网络多特征分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现PSO-PNN粒子群优化概率神经网络多特征分类预测,运行环境Matlab2018b及以…...

AcWing 3416. 时间显示
文章目录 前言代码思路 前言 这种我最担心的就是一些语法格式忘掉了。还有 int ,long long 的数据范围我记得不是很清楚,印象中 int 是 20 亿左右,long long 不行就得用数组来存这个数字了。int,long long数据类型及数值范围。好像没记错,记…...

【软考速通笔记】系统架构设计师⑲——专业英语
文章目录 一、前言二、常用名词三、架构风格四、非功能需求五、应用架构六、软件架构重用 一、前言 笔记目录大纲请查阅:【软考速通笔记】系统架构设计师——导读 二、常用名词 名词翻译architecture架构system系统design设计requirements需求components组件constr…...

java注解(二):注解的解析以及应用场景、用注解和反射模拟junit框架代码演示
目录 1、什么是注解的解析? 2、解析注解的案例 1、自定义一个注解 2、在类和方法上使用自己定义的注解 3、解析注解 3、模拟Junit框架案例 1、自定义一个MyTest注解 2、定义一个测试类,使用自定义的注解 3、写一个启动类 本文章主要讲解什么是注…...

C# 命名空间(Namespace)
文章目录 前言一、命名空间的定义与使用基础(一)定义语法与规则(二)调用命名空间内元素 二、using 关键字三、嵌套命名空间 前言 命名空间(Namespace)在于提供一种清晰、高效的方式,将一组名称与…...

几个Linux系统安装体验: centos7系统服务版
本文介绍CentOS7服务版本的安装。 前言 当前国产操作系统版本众多,但根据笔者多年的实践经验得到的认知,最好能抓住底层逻辑,上下打通打透,拉齐表现和本质,就能在纷扰版本中看得清清楚楚,明明白白…...

ViT学习笔记(一) 基本的原理和框架结构
原论文地址:https://arxiv.org/pdf/2010.11929 首先大致通读一下原论文,这是很有必要的,但不必完全读懂,因为会有高手给我们解读,比如: 【Transformer系列】深入浅出理解ViT(Vision Transformer)模型-CSD…...

fedora下Jetbrains系列IDE窗口中文乱码解决方法
可以看到窗口右部分的中文内容为小方块。 进入 Settings - Appearance & Behavior - Appearance - Use custom font : Note Sans Mono CJK SC ,设置后如下图:...

nginx根据报文里字段转发至不同地址
nginx接收到post请求.请求报文里是一个json字符串,字符串里有个字段id。 根据id不同,转发到不同地址。 如果idaaa,转发到www.aaa.com.test 如果idbbb,转发到www.bbb.com.test 如何配置,请提供一个nginx.conf 要在 Nginx 中根据 POST 请求的 JSON 负载中的…...

使用 html/css 实现 educoder 顶部导航栏的步骤和方法
要使用HTML和CSS实现一个类似于Educoder网站的顶部导航栏,我们可以设计一个响应式、简洁且功能齐全的导航栏。Educoder的顶部导航栏通常包括网站的logo、主要导航项(如首页、课程、讨论等)、以及用户操作按钮(如登录、注册等&…...

EasyExcel导出列表
通过easyexcel导出列表数据 根据列表内容自适应宽高。 文件名冲突,修改文件名递增设置。 依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>${easyexcel.version}</version&…...

【unity小技巧】分享vscode如何开启unity断点调试模式,并进行unity断点调试(2024年最新的方法,实测有效)
文章目录 前言一、前置条件1、已安装Visual Studio Code,并且unity首选项>外部工具>外部脚本编辑器选择为Visual Studio Code [版本号],2、在Visual Studio Code扩展中搜索Unity,并安装3、同时注意这个插件下面的描述,需要根…...

【JavaScript】Object.keys() 和 Object.values() 的使用示例和相关的简单应用
值是数字的情况: let n 124; Object.keys(n) // [] Object.values(n) // []值是字符串的情况: let s "abc"; Object.keys(s) // [0, 1, 2] Object.values(s) // [a, b, c]值是数组的情况:(常用) let ar…...

SwiftUI 列表(或 Form)子项中的 Picker 引起导航无法跳转的原因及解决
概述 在 SwiftUI 的界面布局中,列表(List)和 Form 是我们秃头码农们司空见惯的选择。不过大家是否知道:如果将 Picker 之类的视图嵌入到列表或 Form 的子项中会导致导航操作无法被触发。 从上图可以看到:当在 List 的…...

基于ZYNQ-7000系列的FPGA学习笔记8——呼吸灯
基于ZYNQ-7000系列的FPGA学习笔记8——呼吸灯 1. 实验要求2. 功能分析3. 模块设计4. 波形图5.代码编写6. 代码仿真7. 添加约束文件并分析综合 上期内容,我们学习了按键控制蜂鸣器,这一期我们开始学习呼吸灯 1. 实验要求 控制领航者核心板上的led&#x…...

探索 Python 应用的分层依赖:解决 UOS 环境中的 libvirt-python 安装问题
探索 Python 应用的分层依赖:解决 UOS 环境中的 libvirt-python 安装问题 背景Python 版本升级 问题描述原因分析与解决方案 Python 应用的分层依赖:安装与部署的视角libvirt-python的分层依赖尝试的解决方案 使用编译好的 .whl 文件"嫁接"整个…...

OpenCV-平滑图像
二维卷积(图像滤波) 与一维信号一样,图像也可以通过各种低通滤波器(LPF)、高通滤波器(HPF)等进行过滤。LPF 有助于消除噪音、模糊图像等。HPF 滤波器有助于在图像中找到边缘。 opencv 提供了函数 **cv.filter2D()**&…...

解决跨域问题方案
跨域问题在前后端分离架构下尤为常见,是每个 Web 开发者都会遇到的核心问题。本文将通过原理解析、场景剖析、解决方案详解以及最佳实践等多个维度,帮助开发者全面理解并有效应对跨域问题。 目录 **一、跨域的本质****1. 同源策略****2. 同源策略的限制范…...

云计算介绍_3(计算虚拟化——cpu虚拟化、内存虚拟化、io虚拟化、常见集群策略、华为FC)
计算虚拟化 1.计算虚拟化介绍1.1 计算虚拟化 分类(cpu虚拟化、内存虚拟化、IO虚拟化)1.2 cpu虚拟化1.3 内存虚拟化1.4 IO虚拟化1.5 常见的集群的策略1.6 华为FC 1.计算虚拟化介绍 1.1 计算虚拟化 分类(cpu虚拟化、内存虚拟化、IO虚拟化&#…...

软件工程复习记录
基本概念 软件工程三要素:方法、工具、过程 软件开发方法:软件开发所遵循的办法和步骤,以保证所得到的运行系统和支持的文档满足质量要求。 软件开发过程管理 软件生命周期:可行性研究、需求分析、概要设计、详细设计、编码、测…...

俩Nim游戏
1.给定n堆石子,每堆石子有xi快,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。 问如果两人都采用最优策略ÿ…...

基于超级电容和电池的新能源汽车能量管理系统simulink建模与仿真
目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 超级电容特性 4.2 电池特性 5.完整工程文件 1.课题概述 基于超级电容和电池的新能源汽车能量管理系统simulink建模与仿真。分析不同车速对应的电池,超级电容充放电变化情况。 2.系统仿…...

数据结构——图(遍历,最小生成树,最短路径)
目录 一.图的基本概念 二.图的存储结构 1.邻接矩阵 2.邻接表 三.图的遍历 1.图的广度优先遍历 2.图的深度优先遍历 四.最小生成树 1.Kruskal算法 2.Prim算法 五.最短路径 1.单源最短路径--Dijkstra算法 2.单源最短路径--Bellman-Ford算法 3.多源最短路径--Floyd-…...

002-NoSQL介绍
目录 一、NoSQL 简介 二、NoSQL 特性 三、NoSQL 的工作原理 四、NoSQL 有哪些类型 五、NoSQL数据库与关系型数据库的区别 六、常见的非关系型数据库NOSQL分类 一、NoSQL 简介 NoSQL,全称为Not Only SQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是…...

qt-everywher交叉编译e-src-5.15.2
简化配置的方式: 你完全可以通过直接配置 安装目录、编译链 和 目标架构 来完成交叉编译,而不需要修改 mkspecs 配置。以下是如何通过简化配置来进行交叉编译 Qt 的步骤。 准备交叉编译工具链 首先,确保你已经安装了交叉编译工具链ÿ…...