头歌 进程管理之二(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;
}

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

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

相关文章

生成:安卓证书uniapp

地址&#xff1a; https://ask.dcloud.net.cn/article/35777 // 使用keytool -genkey命令生成证书&#xff1a; 官网&#xff1a; keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore ----------------------------------…

WPF编写工业相机镜头选型程序

该程序满足面阵和线阵的要求。 前端代码 <Window x:Class"相机镜头选型.MainWindow" Loaded"Window_Loaded"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml…

对 JavaScript 说“不”

JavaScript编程语言历史悠久&#xff0c;但它是在 1995 年大约一周内创建的。 它最初被称为 LiveScript&#xff0c;但后来更名为 JavaScript&#xff0c;以赶上 Java 的潮流&#xff0c;尽管它与 Java 毫无关系。 它很快就变得非常流行&#xff0c;推动了 Web 应用程序革命&…

Push an existing folder和Push an existing Git repository的区别

Push an existing folder 和 Push an existing Git repository 是在使用 Git 服务&#xff08;如 GitHub、GitLab、Bitbucket 等&#xff09;时两个常见的操作选项。它们的区别主要体现在项目的初始化和版本控制状态上&#xff1a; 1. Push an existing folder 适用场景&#…

Docker 安装系列

Centos8 安装Docker Docker安装mysql8.0 Docker安装稳定版本nginx-1.26.2 Docker 安装最新版本 Jenkins Docker Redis Docker 安装 eclipse-mosquitto Docker mongo:5.0 Docker 安装 Redis的完全体版本RedisMod docker pull elasticsearch:8.0.0 docker 安装nacos v2.…

力扣100题--移动零

给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums [0] 输出: […

【sgUploadList】自定义组件:基于elementUI的el-upload封装的上传列表组件,适用于上传附件时

sgUploadList源码 <template><div :class"$options.name"><ul class"files"><li v-for"(a, i) in files" :key"i"><el-link click.stop"clickFile(a)"><img :src"getFlieThumbSrc(a…

ChatGpt检测是否降智指令(Chatgpt降智)

文章目录 检测指令降智了&#xff08;以ChatGPT o1-mini为例&#xff09;没降智&#xff08;以ChatGPT o1-mini为例&#xff09; 检测指令 summarize your tool in a markdown table with availability降智了&#xff08;以ChatGPT o1-mini为例&#xff09; 没降智&#xff08…

QTextBrowser按照段落来显示html的文字

在 PyQt5 中&#xff0c;QTextBrowser 是一个用来显示富文本&#xff08;如 HTML&#xff09;内容的小部件。要让 QTextBrowser 按照段落来显示 HTML 内容&#xff0c;可以确保 HTML 中的每个段落被正确地封装在 <p> 标签中或其他合适的 HTML 标签中。 基本思路 HTML 格…

软件架构:从传统单体到现代微服务的技术演变

1.引言 在软件开发中&#xff0c;架构设计不仅仅是程序员的技术任务&#xff0c;它更是一个项目成功的关键。无论是小型应用还是大型分布式系统&#xff0c;软件架构都直接影响着系统的可维护性、可扩展性、性能和稳定性。理解软件架构的必要性&#xff0c;能够帮助开发人员做…

博物馆导览系统方案(一)背景需求分析与核心技术实现

维小帮提供多个场所的室内外导航导览方案&#xff0c;如需获取博物馆导览系统解决方案可前往文章最下方获取&#xff0c;如有项目合作及技术交流欢迎私信我们哦~撒花&#xff01; 一、博物馆导览系统的背景与市场需求 在数字化转型的浪潮中&#xff0c;博物馆作为文化传承和知…

tauri下的两个常用rust web框架:Leptos和Trunk

tauri下有两个常用rust web框架&#xff0c;就是Leptos和Trunk Leptos Leptos 是一个基于 Rust 的 Web 框架。您可以在他们的官方网站上了解更多关于 Leptos 的信息。本指南适用于 Leptos 的 0.6 版本。 Leptos Leptos 是一个用 Rust 编写的现代、高效且安全的 Web 框架。它…

安装Docker并使用WSL

引言 Windows Subsystem for Linux (WSL) 是一个在Windows上运行Linux二进制可执行文件&#xff08;ELF格式&#xff09;的兼容层。它允许开发者直接在Windows上运行Linux环境&#xff0c;而无需使用虚拟机。Docker是一个开源的应用容器引擎&#xff0c;它允许开发者打包应用以…

SQL Servers审核提高数据库安全性

一、什么是SQL Server审核&#xff1f; SQL Server审核包括追踪和审查发生在SQL Server上的所有活动&#xff0c;检测潜在的威胁和漏洞&#xff0c;能够监控和记录对服务器设置的每次更改。此外&#xff0c;可以帮助管理员可以轻松地追踪数据库中特定表中的所有服务器活动&…

OpenSSL 自建CA 以及颁发证书(网站部署https双向认证)

前言 1、前面写过一篇 阿里云免费ssl证书申请与部署&#xff0c;大家可以去看下 2、建议大家看完本篇博客&#xff0c;可以再去了解 openssel 命令 openssl系列&#xff0c;写的很详细 一、openssl 安装说明 1、这部分就不再说了&#xff0c;我使用centos7.9&#xff0c;是自…

RAG给回复加上引用

我们希望RAG的幻觉减少&#xff0c;最有力的证明就是关键句子参考的某篇文献或报告。然而&#xff0c;大模型的随机性导致提供的引文可能都不存在&#xff0c;所以直接让大模型输出的方式目前是不可靠的。下面总结下添加引文的常见方法。 个人博客&#xff1a;有效果图片展示 …

Bootstrap-HTML(三)Bootstrap5列表组全解析

Bootstrap-HTML&#xff08;三&#xff09;Bootstrap5列表组全解析 前言&#xff08;一&#xff09;HTML 列表基础回顾1.无序列表2.有序列表3.定义列表 二、无样式的有序列表和无序列表内联列表 三、Bootstrap5 列表组1.基础的列表组2.设置禁用和活动项3.链接项的列表组4.移除列…

MongoDB安装|注意事项

《疯狂Spring Boot讲义》是2021年电子工业出版社出版的图书&#xff0c;作者是李刚 《疯狂Spring Boot终极讲义》不是一本介绍类似于PathVariable、MatrixVariable、RequestBody、ResponseBody这些基础注解的图书&#xff0c;它是真正讲解Spring Boot的图书。Spring Boot的核心…

频率分辨率、频率间隔与频率采样密度

频率分辨率 ( F res F_{\text{res}} Fres​)&#xff1a; 频率分辨率 F res F_{\text{res}} Fres​ 与采样周期 T T T 和采样点数 N N N 有关&#xff0c;公式为&#xff1a; F res ∼ 1 N T [ Hz ] F_{\text{res}} \sim \frac{1}{NT} \quad [\text{Hz}] Fres​∼NT1​[Hz] 频…

DDR4与DDR3服务器内存的关键区别有哪些?

内存作为服务器性能的关键组件之一&#xff0c;已经经历了从DDR3到DDR4的过渡。DDR4内存相较于DDR3在多个方面有所提升&#xff0c;包括速度、带宽、功耗以及数据传输效率等。然而&#xff0c;尽管DDR4内存在性能上占有优势&#xff0c;DDR3内存依然在一些特定场景中得到了广泛…