Linux进程——exec族函数、exec族函数与fork函数的配合

exec族函数解析

作用

        我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。

功能

        在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。

函数族

        exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe

函数原型

#include <unistd.h>
extern char **environ;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 execvpe(const char *file, char *const argv[],char *const envp[]);

返回值

        exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。

参数说明

path可执行文件的路径名字
arg可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件

exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:

l使用参数列表
p使用文件名,并从PATH环境进行寻找可执行文件
v先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数
e多了envp[]数组,使用新的环境变量代替调用进程的环境变量

将exac函数归为带l、带p、带v三类来说明参数特点

一、带l的一类exac函数(l表示list),包括execl、execlp、execle,要求将新程序的每个命令行参数都说明为 一个单独的参数。这种参数表以空指针结尾。
1.新建一个echoarg并使用execl函数调用
//文件17.c
#include <unistd.h>
#include <stdio.h>int main()
{printf("before execl\n");if(execl("./echoarg","echoarg","hello","word",NULL) == -1){printf("open execl failed\n");//调用execl失败会返回-1,则会执行if里的代码perror("why");}printf("after execl\n");return 0;
}
//文件echoarg.c
#include <stdio.h>int main(int agrc,char *argv[])
{int i;for(i = 0;i < agrc;i++){printf("argv[%d]:%s\n",i,argv[i]);}return 0;
}

先用gcc编译echoarg.c,生成可执行文件echoarg并放在当前路径下。文件echoarg的作用是打印命令行参数。然后再调用execl,用execl 找到并执行echoarg,将当前进程main替换成进程echoarg,就会执行ehcoarg里面的代码,所以”after execl” 没有在终端被打印出来。

路径对应不上的情况:

//文件16.c
#include <unistd.h>
#include <stdio.h>int main()
{printf("before execl\n");if(execl("/bin/echoarg","echoarg","hello","word",NULL) == -1){printf("open execl failed\n");perror("why");//perror函数会打印错误原因,输入内容后结尾会自动加冒号(:)并换行}printf("after execl\n");return 0;
}

可见echoarg存在于当前路径并不存在于bin路径下,所以调用execl函数无法找到echoarg并执行echoarg,调用失败返回值为-1,则会继续执行if函数里的代码。

注:当前文件格式是./+可执行文件名字,其他路径为 /其他路径/可执行文件名字

2.使用execl函数调用ls指令
//文件18.c
#include <unistd.h>
#include <stdio.h>int main()
{printf("before execl\n");if(execl("/bin/ls","ls",NULL) == -1)//不需要参数则找到并使用ls后以NULL结尾{printf("open execl failed\n");perror("why");}printf("after execl\n");return 0;
}

whereis ls//查看ls的路径

可见打印完“before execl”后使用execl函数使用execl函数找到并调用date可执行文件,编译运行代码会直接显示出当前所有文件,实现功能调用。不会执行if中的代码。

3.使用execl函数调用date指令
//文件19.c
#include <unistd.h>
#include <stdio.h>int main()
{printf("we can use execl to know the time\n");//不需要参数则找到并使用ls后以NULL结尾if(execl("/bin/date","date",NULL) == -1){printf("open execl failed\n");perror("why");}printf("after execl\n");return 0;
}

date//查看系统时间

可见一开始直接用date指令可以看到系统时间,在上述代码中通过使用execl函数找到并调用date可执行文件,编译运行代码会直接显示出系统时间,实现功能调用。

二、带p的一类exac函数,包括execlp、execvp、execvpe,如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
1.使用execlp函数调用date指令
//文件20.c
#include <unistd.h>
#include <stdio.h>int main()
{printf("we can use execl to know the time\n");if(execlp("date","date",NULL) == -1){printf("open execl failed\n");perror("why");}printf("after execl\n");return 0;
}

上述代码如果调用execl函数则会调用失败,原因是没有输入可执行文件的正确路径。

而在file参数位置直接输入可执行文件名字,调用execlp函数能通过环境变量PATH查找到可执行文件date并执行。

2.环境变量PATH解读

如果使用的可执行文件在PATH里,则使用execlp函数可以不用详细描述绝对路径

打印环境变量

echo $PATH

修改环境变量

export PATH=$PATH:/home/CLC/Linux1
//通过pwd查看执行文件的路径并将其添加进环境变量PATH路径

修改完毕后结果是:运行/hone/CLC/Linux1下的可执行文件不需要加./便可直接运行

 

在当前Linux1文件下运行可执行文件不需要加./,使用cd回到目录文件夹也是输出结果一样

三、带v不带l的一类exac函数,包括execv、execvp、execve,应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。

如char *arg[]这种形式,且arg最后一个元素必须是NULL,例如char *arg[] = {“ls”,NULL};

1.execvp

#include <unistd.h>
#include <stdio.h>int main()
{printf("we can use execl to know the time\n");char *agrv[] = {"ls",NULL};//定义一个指针数组if(execvp("ls",agrv) == -1)//将指针数组的地址作为参数{printf("open execl failed\n");perror("why");}printf("after execl\n");return 0;
}

定义一个指针数组并将其地址作为execvp函数后面的参数,同时要以NULL结尾,便可以找到并执行ls可执行文件。同时file参数位置可直接输入可执行文件名字,不需要加路径。

2.execv

#include <unistd.h>
#include <stdio.h>int main()
{printf("before execv\n");char *agrv[] = {"date",NULL};if(execv("/bin/date",agrv) == -1){printf("open execl failed\n");perror("why");}printf("after execvp\n");return 0;
}

跟execvp一样需要定义一个指针数组并将其地址作为参数部分,但需要加上可执行文件路径名字,date在bin下,所以需要加上/bin/才能正确使用execv函数找到并执行date可执行文件。

exec族函数与fork函数的配合

流程图如下:

当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉。

代码如下:

#include<stdio.h>
#include <unistd.h>
int main()
{int a= 0;int fork_r=0;while(1){printf("please input a num\n");scanf("%d",&a);if(a==1)//输入为1,创建子进程{fork_r=fork();if(fork_r==0)//返回值为两次,等于0为子进程{execl("./changeData","changeData",NULL);//找到并执行changeData}}else{printf("no change success\n");}}return 0;
}

上述代码调用execl函数找到并执行的changeData函数如下

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc,char **argv)
{int fdSrc;char *readBuf = NULL;fdSrc = open("config",O_RDWR);int size = lseek(fdSrc,0,SEEK_END);lseek(fdSrc,0,SEEK_SET);readBuf = (char* )malloc(sizeof(char)*size + 8);int n_read = read(fdSrc,readBuf,size);char *p = strstr(readBuf,"leng=");if(p == NULL){printf("not found\n");exit(-1);}p = p+strlen("leng=");*p = '9';lseek(fdSrc,0,SEEK_SET);int n_write = write(fdSrc,readBuf,strlen(readBuf));close(fdSrc);return 0;
}

编译格式是:

gcc changeData.c -o changeData//-o后面的可执行文件才能被execl调用

原配置文件config的值如下:

peed=5
leng=1
SCORE=90
LEVEL=95

只有当输入的值为1时,才会创建子进程并使用execl函数找到并执行changeData可执行文件,从而将原配置文件config中“leng=1”改成“leng=9”。

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

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

相关文章

Golang起步篇(Windows、Linux、mac三种系统安装配置go环境以及IDE推荐以及入门语法详细释义)

Golang起步篇 Golang起步篇一. 安装Go语言开发环境1. Wondows下搭建Go开发环境(1). 下载SDK工具包(2). 解压下载的压缩包&#xff0c;放到特定的目录下&#xff0c;我一般放在d:/programs下(路径不能有中文或者特殊符号如空格等)(3). 配置环境变量步骤1&#xff1a;先打开环境变…

[ATC复盘] abc329 20231118

[ATC复盘] abc329 20231118 总结A - Spread1. 题目描述2. 思路分析3. 代码实现 B - Next1. 题目描述2. 思路分析-3. 代码实现 C - Count xxx1. 题目描述2. 思路分析3. 代码实现 D - Election Quick Report2. 思路分析3. 代码实现 E - Stamp2. 思路分析3. 代码实现 F - Colored…

SEnet注意力机制(逐行代码注释讲解)

目录 ⒈结构图 ⒉机制流程讲解 ⒊源码&#xff08;pytorch框架实现&#xff09;及逐行解释 ⒋测试结果 ⒈结构图 左边是我自绘的&#xff0c;右下角是官方论文的。 ⒉机制流程讲解 通道注意力机制的思想是&#xff0c;对于输入进来的特征层&#xff0c;我们在每一个通道学…

Codeforces Round #909 (Div. 3)

A. Game with Integers 签到题&#xff0c;但是本蒟蒻11分钟才AC&#xff0c;主要还是英文题面不熟练&#xff0c;题目中加粗了after&#xff0c;只有下一步操作之后能被整除才胜利。 英文题面的加粗单词很重要&#xff0c;注意提高签到题速度。 B. 250 Thousand Tons of TNT…

HarmonyOS开发Java与ArkTS如何抉择

在“鸿蒙系统实战短视频App 从0到1掌握HarmonyOS”视频课程中&#xff0c;很多学员来问我&#xff0c;在HarmonyOS开发过程中&#xff0c;面对Java与ArkTS&#xff0c;应该选哪样&#xff1f; 本文详细分析Java与ArkTS在HarmonyOS开发过程的区别&#xff0c;力求解答学员的一些…

联想系列台式机Win11系统改Win7系统BIOS设置步骤

联想最新一代的台式机默认操作系统Win11&#xff0c;采用UEFIGPT启动模式&#xff0c;并且开启了安全启动功能&#xff0c;一般用户不能直接将Win11改成Win7&#xff0c;如果需要更改操作系统&#xff0c;是需要再BIOS菜单中关闭安全启动功能的&#xff0c;并且把启动模式设置成…

探索亚马逊大语言模型:开启人工智能时代的语言创作新篇章

文章目录 前言一、大语言模型是什么&#xff1f;应用范围 二、Amazon Bedrock总结 前言 想必大家在ChatGPT的突然兴起&#xff0c;大家多多少少都会有各种各样的问题&#xff0c;比如&#xff1a;大语言模型和生成式AI有什么关系呢&#xff1f;大语言模型为什么这么火&#xf…

报道 | 2023年12月-2024年2月国际运筹优化会议汇总

2023年12月-2024年2月召开会议汇总&#xff1a; The 16th Annual International Conference on Combinatorial Optimization and Applications (COCOA 2023) Location: Virtual Important dates: Conference: December 11, 2023 (Start) - December 13, 2023 (End) Details…

C/C++数据结构之中缀表达式转换为后缀表达式,删除堆栈元素

在这篇博客中&#xff0c;我们将深入分析一个使用C编写的栈和表达式计算程序。该程序不仅实现了基本的栈操作&#xff0c;还提供了中缀表达式转后缀表达式和删除堆栈中的元素等实用功能。通过逐一讲解每个函数的功能&#xff0c;我们将更全面地理解这个程序的实现。 资源获取&a…

【论文解读】FFHQ-UV:用于3D面部重建的归一化面部UV纹理数据集

【论文解读】FFHQ-UV 论文地址&#xff1a;https://arxiv.org/pdf/2211.13874.pdf 0. 摘要 我们提出了一个大规模的面部UV纹理数据集&#xff0c;其中包含超过50,000张高质量的纹理UV贴图&#xff0c;这些贴图具有均匀的照明、中性的表情和清洁的面部区域&#xff0c;这些都是…

【Web】Ctfshow SSRF刷题记录1

核心代码解读 <?php $url$_POST[url]; $chcurl_init($url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $resultcurl_exec($ch); curl_close($ch); ?> curl_init()&#xff1a;初始curl会话 curl_setopt()&#xff1a;会…

mysql 中with的用法(3)

有表&#xff08;tb&#xff09;,数据如下&#xff1a; 请用SQL,生成如下的样式&#xff1a; 一、建表 CREATE TABLE tb (id varchar(3) DEFAULT NULL,pid varchar(3) DEFAULT NULL,name varchar(64) DEFAULT NULL ) INSERT INTO tb (id, pid, name) VALUES(002, 0, 浙江省)…