uc_09_创建新进程 exec() system()

1  什么是创建新进程(夺舍)

        在前面文章中,我们学习了fork()函数用来创建子进程

        子进程是父进程的副本,复制父进程除代码段以外的其他数据,代码段数据和父进程共享。

        子进程的PID与父进程不同:

        

        而创建新进程则不同。

        与fork()不同,exec函数族不是创建调用进程的子进程,而是创建一个新的进程去掉调用进程自身。

        新进程会用自己的全部地址空间,覆盖调用进程的地址空间。

        新进程的PID与调用进程相同(子进程变身后,父子关系不变):

        

2  创建新进程(夺舍)

        exec不是一个函数,而是一堆函数(6个),称为exec函数族。它们的功能是相同的,用法也相近,只是参数的形式和数量略有不同。建议只熟练用第1个即可。

        #include <unistd.h>

        int execl   (const char* path,  const char* arg, ...);

        int execlp (const char* file,    const char* arg, ...); 

        int execle (const char* path,  const char* arg, ...,  char* const envp[]);

        int execv  (const char* path,  char* const argv[]);

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

        int execve(const char* path,  char* const argv[],   char* const envp[]);

                功能:让新进程取代原本的旧进程

                path:可执行文件的路径

                arg:命令行参数

                ...:不定长参数(可变长参数),就像printf()

                envp:旧进程为新进程指定的环境变量,不指定则从调用进程复制。

                            变相地在向新进程传递数据!

                l:list,新进程的命令行参数以字符指针列表(const char* arg, ...)的形式传入,列表以

                     指针结束,别忘了写NULL

               v:vector,新进程的命令行参数以字符指针数组(char* const argv[])的形式传入,数组以

                     空指针结束。

                p:path,若第一个参数中不包含"/"完整路径,则将其视为文件名,并根据PATH环境变

                      量搜索该文件。

                e:environment,新进程的环境变量以字符指针数组(char* const envp[])的形式传入,

                      数组以空指针结束,不指定环境变量则从调用进程复制。

        其实6个exec函数只有execve是真正的系统调用,其它5个是对evecve的简单包装:

        

        调用exec函数不仅改变调用进程的地址空间和进程映像,调用进程的一些属性也发生了变化(归零、默认、失效):

        -任何处于阻塞状态的信号都会丢失

        -被设置为捕获的信号会还原为默认操作

        -有关线程属性的设置会还原为缺省值

        -有关进程的统计信息会复位

        -与进程内存相关的任何数据都会丢失,包括内存映射文件(局部变量等)

        -标准库在用户空间维护的一切数据结构(如通过atexit或on_exit函数注册的退出处理函数)

          都会丢失

        但有些属性会被新进程继承下来,如PID,PPID,实际用户ID,实际组ID,优先级,文件描述符等。

        注意,如果新进程创建成功,exec函数是不会返回的,因为成功的exec调用会以跳转到新进程的入口地址作为结束,而刚刚运行的代码是不会存在于新进程的地址空间中的(旧进程已死,没得返回;新进程没调用,也就返不给新进程)。但如果进程创建失败,exec函数会返回-1

//new.c  变身的目标
#include<stdio.h>
#include<unistd.h>int main(int argc,char* argv[],char* envp[]){printf("PID : %d\n",getpid());printf("命令行参数:\n");for(char** pp = argv;*pp;pp++){printf("%s\n",*pp);}printf("环境变量:\n");for(char** pp = envp;*pp;pp++){printf("%s\n",*pp);}printf("---------------------\n");return 0;
}
//编译执行为new,作为变身的目标
//exec.c  创建新进程(bash的子进程exec变身成new进程)
#include<stdio.h>
#include<unistd.h>int main(void){printf("%d进程:我要变身了\n",getpid());/*if(execl("./new","new","hello","123",NULL) == -1){ //第一个参数已定位,故第二perror("execl");                                 //个new无需再./return -1;}*//*if(execl("/bin/ls","ls","-i","-a","-l",NULL) == -1){ //变身成ls命令perror("execl");                                //命令的本质就是可执行程序return -1; }*//*if(execlp("lsSSSS","ls","-a","-i",NULL) == -1){  //报错perror("execlp");return -1;}*///演示execve(),定义2个char* []//指定新进程的环境变量,变相在新旧进程间传递数据!char* envp[] = {"NAME=laozhang","AGE=18","FOOD=guobaorou",NULL};/*if(execle("./new","new","hello","123",NULL,envp) == -1){perror("execle");return -1;}*/char* argv[] = {"new","hello","123",NULL};if(execve("./new",argv,envp) == -1){perror("execve");return -1;}printf("%d进程:变身完成了\n",getpid());//不会执行,因为前面已经进入new进程了return 0;                             //本进程已被抛弃,代码自然不被执行
}//编译执行

        调用exec函数固然可以创建出新的进程,但是新进程会取代原来的进程。如果既想创建新的进程,同时又希望原来的进程继续存在, 则可以考虑fork() + exec()模式,即在fork产生的子进程里调用exec函数,新进程取代了子进程,但父进程依然存在:

        

//forkexec.c  fork() + exec()模式
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>int main(void){//创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}//子进程代码,exec变身if(pid == 0){if(execl("./new","new","hello","123",NULL) == -1){perror("execl");return -1;}//return 0; //可以注释掉,因为已变身成new进程了,这里压根不执行}               //即使变身失败,也return -1; 了//父进程代码,收尸int s;//用来输出子进程的终止状态if(waitpid(-1,&s,0) == -1){ //-1任意PID,0阻塞perror("waitpid");return -1;}if(WIFEXITED(s)){printf("正常终止:%d\n",WEXITSTATUS(s));}else{printf("异常终止:%d\n",WTERMSIG(s));}//创建第二个子进程pid = fork();if(pid == -1){perror("fork");return -1;}//子进程代码if(pid == 0){if(execl("/bin/ls","ls","-i","-l",NULL) == -1){perror("execl");return -1;}//return 0;}//父进程代码if(waitpid(-1,&s,0) == -1){perror("waitpid");return -1;}if(WIFEXITED(s)){ //宏,判断进程死因printf("正常终止:%d\n",WEXITSTATUS(s));}else{printf("异常终止:%d\n",WTERMSIG(s));}return 0;
}
//编译执行

3  system() 最优

        system()   ==   fork()   +   exec ()   +   waitpid()

        使用system()函数而不用vfork() + exec ()的好处是,system函数针对各种错误和信号都做了必要的处理,而且system是标准库函数,可跨平台使用,各种报错措施也完备。

        #include <stdlib.h>

        int system(const char* command);

                功能:执行shell命令

                command:shell命令行字符串

                返回值:成功返回command进程的终止状态,失败返回-1 

        system()函数执行command参数所表示的命令行,并返回命令进程的终止状态。

        若command参数取NULL,返回非0表示shell可用,返回0表示shell不可用。

       

        在system()函数内部调用了vfork()   exec ()   和waitpid()等函数:

        -如果调用vfork()或waitpid()函数出错,则返回-1 

        -如果调用exec()函数出错,则在子进程中执行exit(127)

        -如果都成功,则返回command进程的终止状态(由waitpid()的status参数获得)

//system.c  system()函数演示
#include<stdio.h>
#include<stdlib.h> //system()是标准库函数
#include<sys/wait.h>int main(void){int s = system("./new hello 123"); //就像在命令行输入if(s == -1){ //system()失败perror("system");return -1;}if(WIFSIGNALED(s)){ //宏1printf("异常终止:%d\n",WTERMSIG(s)); //sytem()成功,./new失败}else{printf("正常终止:%d\n",WEXITSTATUS(s));}//创建第2个新进程s = system("ls -i -l --color=auto"); //试试不加--if(s == -1){                     //.bashrc中alias ls='ls --color=autu'后,perror("system");            //只有bash终端自带效果,但程序中要手敲return -1;}if(WIFEXITED(s)){ //宏2,等效滴printf("正常终止:%d\n",WEXITSTATUS(s));}else{printf("异常终止:%d\n",WTERMSIG(s));}return 0;
}
//编译执行

       vfork()生成的子进程,不会复制数据,而是共用父进程的数据,省时省力,至今用于system()底层。运行时子进程优先用,父进程阻塞;子进程结束后,父进程才继续运行。

        近来,写时复制的发明,vfork()用得少了,可不深究,更多用fork()即可。

3.1  电子表?

      尝试用system()写个电子钟表,显示时分秒,每秒更新。

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

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

相关文章

docker-compose;私有镜像仓库harbor搭建;镜像推送到私有仓库harbor

docker-compose&#xff1b;私有镜像仓库harbor搭建&#xff1b;镜像推送到私有仓库harbor 文章目录 docker-compose&#xff1b;私有镜像仓库harbor搭建&#xff1b;镜像推送到私有仓库harbordocker-compose私有镜像仓库harbor搭建镜像推送到私有仓库harbor docker-compose D…

论坛自动多播放源采集源码

论坛自动多播放源采集源码是一种用于自动抓取论坛中的多个视频播放源的程序源代码。它可以自动搜索并采集论坛中的多个视频播放源&#xff0c;帮助用户快速找到所需的视频资源。该源码可以帮助用户节省时间和精力&#xff0c;提高视频资源的获取效率。 演示 地 址&#xff1a;…

2023年小美赛认证杯A题太阳黑子预测(Sunspot Forecasting)思路模型代码解析

2023年小美赛认证杯A题&#xff1a;太阳黑子预测&#xff08;Sunspot Forecasting&#xff09; 【请电脑打开本文链接&#xff0c;扫描下方名片中二维码&#xff0c;获取更多资料】 一、问题重述 太阳黑子是太阳光球上的现象&#xff0c;呈暂时性斑点&#xff0c;比周围区域…

2023年中国金融科技研究报告

第一章 行业概况 1.1 定义 金融科技&#xff08;FinTech, Financial Technology&#xff09;代表了金融和技术的交汇。这一领域虽然处于发展的初期阶段&#xff0c;但已经展现出深远的影响力。金融科技的业务模式多样&#xff0c;涵盖了从传统金融服务的数字化转型到新兴技术…

索尼mxf覆盖部分恢复案例(索尼PMW-580)

索尼mxf覆盖部分恢复案例(索尼PMW-580) 索尼的摄像机型号是比较繁多的&#xff0c;高端系列基本上是以mxf文件为主&#xff0c;这一类案例之前处理不少&#xff0c;今天我们看一个索尼pmw-580摄像机删除后又覆盖的恢复案例。 故障存储:64G SD卡/Exfat文件系统 故障现象: 拍…

Kettle 浅入浅出

前言 最近又要迭代客户定制化的数据处理系统了。提到数据处理&#xff0c;不禁想到了以前使用过的 ETL 处理工具 Kettle。本文将对 Kettle 做一些简单的介绍。 Kettle 介绍 在介绍 Kettle 前先了解下什么是 ETL&#xff0c;ETL 是 Extract-Transform-Load 的缩写&#xff0c…

腹泻的原因,种类,风险因素,如何预防

谷禾健康 腹泻是常见的健康问题&#xff0c;相信绝大多数人在生活中都曾遭受过腹泻的困扰。 根据2016年柳叶刀期刊统计&#xff0c;慢性腹泻影响全世界 3%-20% 的成年人。全球每年有17亿儿童腹泻病例&#xff0c;腹泻是五岁以下儿童死亡的第五大原因&#xff0c;每年约有52.5万…

P1 什么是链表 C语言简单易懂

目录 前言 01 什么是链表 02 数组的特点 03 数组的缺点 3.1 删除数组其中一个元素 3.2 数组增加某个节点 04 链表 前言 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《 C 》✨✨✨ &#x1f525; 推荐专栏2: 《 Linux C应用编程&#xff08;概念…

Leetcode98 验证二叉搜索树

题意理解&#xff1a; 首先明确二叉树的定义&#xff0c;对于所有节点&#xff0c;根节点的值大于左子树所有节点的值&#xff0c;小于右子树所有节点的值。 注意一个误区&#xff1a; 根节点简单和左孩子&#xff0c;右孩子比大小是不够的&#xff0c;要和子树比&#xff0c;…

2024年天津天狮学院专升本专业课考试报名通知

天津天狮学院2024年高职升本科专业课报名时间考试时间通知 1.报名条件 报名条件和具体要求按照天津市招生委员会的文件规定执行。考生必须完成文化课报名环节&#xff0c;且填报天津天狮学院志愿&#xff0c;方可报考我校专业课考试。考生提供的各种证件应真实有效&#xff0…

数据结构和算法-树和二叉树的定义和基本术语和性质

文章目录 树的基本概念和相关术语相关的应用节点间的关系描述节点&#xff0c;树的属性描述有序树vs无序树树vs森林小结 树的相关性质考点1考点2考点3考点4考点5考点6小结 二叉树的相关概念和基本术语重要 &#xff08;五种状态&#xff09;特殊二叉树小结 二叉树的相关性质二叉…

中职组网络安全-FTPServer20221010.img(环境+解析)

任务环境说明&#xff1a; √服务器场景&#xff1a;FTPServer20221010.img √服务器操作系统&#xff1a;未知&#xff08;关闭链接&#xff09; √FTP用户名&#xff1a;attack817 密码&#xff1a;attack817 1.分析attack.pcapng数据包文件&#xff0c;通过分析数据包attack…

elasticsearch聚合、自动补全、数据同步

目录 一、数据聚合1.1 聚合的种类1.2 DSL实现聚合1.2.1 Bucket聚合语法1.2.2 聚合结果排序1.2.3 限定聚合范围1.2.4 Metric聚合语法 1.3 RestAPI实现聚合 二、自动补全2.1 拼音分词器2.2 自定义分词器2.3 自动补全查询2.4 RestAPI实现自动补全 三、数据同步3.1 思路分析3.1.1 同…

PCP的Parallel Repetition

1. 引言 见Alessandro Chiesa等人2023年论文《On Parallel Repetition of PCPs》。 Parallel Repetition&#xff08;并行重复&#xff09;可用于&#xff1a; 降低probabilistic proofs的soundness error 的同时提升某些衡量指标的效率 interactive proofs&#xff08;IPs…

模型层——多表操作

多表操作 一 创建模型 实例&#xff1a;我们来假定下面这些概念&#xff0c;字段和关系 作者模型&#xff1a;一个作者有姓名和年龄。 作者详细模型&#xff1a;把作者的详情放到详情表&#xff0c;包含生日&#xff0c;手机号&#xff0c;家庭住址等信息。作者详情模型和作…

YOLOv5全网独家首发改进:SENetv2,Squeeze-Excitation模块融合Dense Layer,效果秒杀SENet

💡💡💡本文自研创新改进:SENet v2,针对SENet主要优化点,提出新颖的多分支Dense Layer,并与Squeeze-Excitation网络模块高效融合,融合增强了网络捕获通道模式和全局知识的能力 推荐指数:五星 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/catego…

【24押题卷】小马哥终极押题3+1!

告别11月&#xff0c;迎接12月&#xff01;说来也是缘分&#xff0c;各平台发了快200套真题&#xff0c;梳理了6本教材课后习题&#xff0c;讲解了吴大正、郑君里、奥本三本重点习题。终于我“悟了”&#xff0c;你们会发现&#xff0c;我今年做的每一套真题解析&#xff0c;几…

Linux Ubuntu protobuf 安装方法

文章目录 安装方法检验安装意外状况 安装方法 去GitHub官网找下载地址 安装 protobuf 21.11 GitHub 资源地址 选择安装版本 &#xff08;是一个URL网址&#xff09; 例如这里选用&#xff1a;protobuf-all-21.11.zip 资源 安装依赖库&#xff0c;执行指令 6. sudo apt-get …

【Node.js】笔记整理4 - 版本管理工具nvm

写在最前&#xff1a;跟着视频学习只是为了在新手期快速入门。想要学习全面、进阶的知识&#xff0c;需要格外注重实战和官方技术文档&#xff0c;文档建议作为手册使用 系列文章 【Node.js】笔记整理 1 - 基础知识【Node.js】笔记整理 2 - 常用模块【Node.js】笔记整理 3 - n…

【JAVA面向对象编程】--- 探索子类如何继承父类

&#x1f308;个人主页: Aileen_0v0&#x1f525;学习专栏: Java学习系列专栏 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 继承 继承的普通成员方法调用 及 普通成员变量修改 构造方法的调用 子类构造方法 继承 package Inherit;class Animal …