exec族函数函数的作用:
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
exec族函数定义:
可以通过这个网站查询:linux函数查询
功能:
在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何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[]);
exaclp函数带p,所以能通过环境变量PATH查找到可执行文件ps
返回值:
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明:
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
————————————————
版权声明:本文为CSDN博主「云英」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014530704/article/details/73848573
execl 函数原型
int execl(const char *path, const char *arg0, ..., NULL);
execl("/bin/ls", "ls", "-l", "-a", NULL);
- “/bin/ls”
作用:要执行的程序的完整路径
说明:指定了 ls 命令在文件系统中的位置
注意:execl 需要完整路径,不会自动搜索 PATH 环境变量 - “ls”
作用:程序的 argv[0](命令名称)
说明:通常是程序名本身,但可以任意命名
示例:ps 命令查看进程时显示的就是这个名称 - “-l”
作用:第一个命令行参数
说明:长格式显示,包含详细信息
效果:显示文件权限、所有者、大小、修改时间等 - “-a”
作用:第二个命令行参数
说明:显示所有文件(包括隐藏文件)
效果:显示以 . 开头的隐藏文件 - NULL –> 不能漏
作用:参数列表的结束标志
说明:告诉函数参数列表到此为止
必须性:绝对不能省略,否则会导致未定义行为
perror函数会打印错误信息
使用
//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
printf("before execl\n");
if(execl("./bin/echoarg","echoarg","abc",NULL) == -1)
{
printf("execl failed!\n");
perror("");//里面要放双引号或者双引号里面加点字符串,不能没有双引号
}
printf("after execl\n");
return 0;
}
exec族函数配合fork使用
实现功能,当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉。
代码 –> 子进程配置文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//write和close的头文件
#include <string.h>//strlen
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int data = 10;
while(1)
{
printf("please input a data\n");
scanf("%d",&data);
if(data == 1)//当输入1
{
pid = fork();//创建子进程
if(pid >0)//
{
wait(NULL);//阻塞父进程
}
if(pid == 0)//子进程来
{
int fdSrc;
char *readBuf = NULL;
fdSrc = open("config.txt",O_RDWR);//打开config.txt
int size = lseek(fdSrc,0,SEEK_END);//读取fdSrc文件的字节
lseek(fdSrc,0,SEEK_SET);//光标放在最前面
readBuf = (char *)malloc(sizeof(char)*size + 8);//读取的个数
int n_read = read(fdSrc, readBuf,size);//读取fdSrc的内容
char *p = strstr(readBuf,"LENG=");//查找LENG=
if(p == NULL)
{
printf("not found\n");
exit(-1);
}
p = p+strlen("LENG=");//移动指针到参数位置
*p = '5';//写入5
lseek(fdSrc,0,SEEK_SET);//光标放到最前面
int n_write = write(fdSrc,readBuf,strlen(readBuf));//写入
close(fdSrc);//关闭
exit(0);//退出
}
}
else
{
printf("wait ,do nothing\n");
}
}
将这个文件 gcc 为 changData
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>//write和close的头文件
#include <string.h>//strlen
#include <stdlib.h>
int main(int argc, char **argv)
{
int fdSrc;
char *readBuf = NULL;
if(argc != 2)
{
printf("patatm error\n");
exit(-1);
}
fdSrc = open(argv[1], 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=");//在 `readBuf` 字符串中搜索子串 `"LENG="`
if(p == NULL){
printf("not found\n");
exit(-1);
}
p = p + strlen("LENG=");//读取 LENG= 的个数 + p
*p = '5';//将光标位置的内容改为 5
lseek(fdSrc,0,SEEK_SET);
int n_write = write(fdSrc,readBuf,strlen(readBuf));
close(fdSrc);
// close(fdDes);
return 0;
}
用这个代码直接调用 changData
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//write和close的头文件
#include <string.h>//strlen
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int data = 10;
while(1)
{
printf("please input a data\n");
scanf("%d",&data);
if(data == 1)//当输入1
{
pid = fork();//创建子进程
if(pid >0)//
{
wait(NULL);//阻塞父进程
}
if(pid == 0)//子进程来运行
{
execl("./changData","changData","config.txt",NULL);
}
}
else
{
printf("wait ,do nothing\n");
}
}
解析 execl("./changData","changData","config.txt",NULL);
./changData
要执行的程序的路径changData
程序的argv[0]
(命令名称)config.txt
命令行参数NULL
参数列表的结束标志 一定要加
system 函数 (执行shell 命令)
linux system函数详解 – 南哥的天下 – 博客园 参考这个
头文件
#include<stdlib.h>
定义函数
int system(const char * string);
函数说明
system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
返回值
如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值。如果 system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。
system 函数就是调用封装好的exec函数
#include
#include
#include
#include
int system(const char * cmdstring)
{
pid_t pid;
int status;
if(cmdstring == NULL){
return (1);
}
if((pid = fork())<0){
status = -1;
}
else if(pid == 0){
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
-exit(127); //子进程正常执行则不会执行此语句
}
else{
while(waitpid(pid, &status, 0) < 0){
if(errno != EINTER){
status = -1;
break;
}
}
}
return status;
}
system 调用
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>//write和close的头文件
#include <string.h>//strlen
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
int data = 10;
while(1)
{
printf("please input a data\n");
scanf("%d",&data);
if(data == 1)
{
pid = fork();
if(pid >0)
{
wait(NULL);
}
if(pid == 0)
{
// exec会用新程序替换当前进程
// execl("./changData","changData","config.txt",NULL);
// system会创建子进程执行命令,并等待命令完成
system("./changData config.txt");
}
}
else
{
printf("wait ,do nothing\n");
}
}
return 0;
重点 system和exec的区别
特性 | system() 函数 | exec() 函数族 |
---|---|---|
执行后行为 | 会返回继续执行后续代码 | 不会返回,原进程被完全替换 |
进程关系 | 创建子进程执行命令,父进程等待 | 替换当前进程,不创建新进程 |
返回值 | 返回命令的退出状态 | 成功时不返回,失败返回-1 |
资源开销 | 较大(需要创建子进程) | 较小(直接替换当前进程) |
system
#include <stdlib.h>
int main() {
printf("准备调用system...\n");
// system会创建子进程执行命令,并等待命令完成
int result = system("ls -l");
// 命令执行完毕后,会继续执行这里的代码
printf("system调用完成,返回值: %d\n", result);
printf("这行代码会被执行\n");
return 0;
}
输出示例
准备调用system...
总用量 0
-rwxr-xr-x 1 user user 0 Dec 1 10:00 test
system调用完成,返回值: 0
这行代码会被执行
exec
#include <unistd.h>
int main() {
printf("准备调用exec...\n");
// exec会用新程序替换当前进程
execl("/bin/ls", "ls", "-l", NULL);
// 如果exec成功,下面的代码永远不会执行!
printf("这行代码不会被执行(除非exec失败)\n");
perror("exec失败"); // 只有exec失败时才会执行
return 0;
}
输出示例
准备调用exec...
总用量 0
-rwxr-xr-x 1 user user 0 Dec 1 10:00 test
(程序在此结束,不会返回原进程)
popen 函数
popen()
创建了一个管道,用于在程序和外部的 shell 命令之间建立双向数据通道,可以读取命令的输出或向命令发送输入。
原型
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
参数说明
command
- 要执行的 shell 命令字符串
- 示例:
"ls -l"
,"grep hello"
,"wc -l"
type
- 指定数据流方向:
"r"
:读取命令的输出(从命令到程序)"w"
:写入到命令的输入(从程序到命令)
返回值
- 成功:返回指向 FILE 结构的指针
- 失败:返回 NULL
FILE* 流指针
FILE*
是 C 语言中用于标准 I/O 操作的核心概念,它就像一个”文件操作的遥控器”,提供了对数据流的高级访问接口
一句话理解
FILE*
是一个指向FILE
结构体的指针,它封装了文件操作的底层细节,为我们提供了统一的缓冲流式访问接口。
FILE 结构体(通常实现)
// FILE 结构体的大致内容(实际实现因编译器而异)
typedef struct {
int _fd; // 文件描述符
char* _buffer; // 数据缓冲区
size_t _bufsize; // 缓冲区大小
int _flags; // 状态标志
char* _ptr; // 当前缓冲区位置
int _cnt; // 缓冲区剩余字节数
// ... 其他字段
} FILE;
打开文件
FILE *fp = fopen("file.txt", "r"); // 打开文件获取流指针
常用流操作函数
函数 | 作用 | 示例 |
---|---|---|
fopen() | 打开文件获取流指针 | FILE *fp = fopen("file.txt", "r") |
fclose() | 关闭流 | fclose(fp) |
fread() | 从流读取数据 | fread(buffer, 1, 100, fp) |
fwrite() | 向流写入数据 | fwrite(data, 1, len, fp) |
fprintf() | 格式化输出到流 | fprintf(fp, "Value: %d", 42) |
fscanf() | 从流格式化输入 | fscanf(fp, "%d", &value) |
fgets() | 从流读取一行 | fgets(line, 100, fp) |
fputs() | 向流写入字符串 | fputs("Hello", fp) |
代码
//文件execl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void)
{
char ret[1024] = {0};// 创建 1024 字节的缓冲区并初始化为 0
FILE *fp; // 声明文件流指针
fp = popen("ps","r"); // 执行 "ps" 命令,以读取模式打开管道
int nread = fread(ret,1,1024,fp);//从文件流`fp`中读取数据到缓冲区`ret
printf("read ret %d byte,ret=%s\n",nread,ret);/要print打印,不打印么有显示
return 0;
}
fp = popen("ps", "r"); // 执行 "ps" 命令,以读取模式打开管道
popen("ps", "r")
:创建子进程执行ps
命令"r"
模式:表示从命令的输出中读取数据- 返回
FILE*
流指针,可以通过标准 I/O 函数读取命令输出
int nread = fread(ret, 1, 1024, fp);//从文件流`fp`中读取数据到缓冲区`ret并返回字节数给nread
ret
:存储读取数据的缓冲区1
:每个元素的大小(1 字节)1024
:最多读取的元素数量fp
:输入流nread
:实际读取的字节数
重点:
- 比system在应用中的好处:
可以获取运行的输出结果 - 与相似函数的对比
特性 | popen() | system() | exec() |
---|---|---|---|
获取输出 | ✅ 可以读取命令输出 | ❌ 不能获取输出 | ❌ 不能获取输出 |
发送输入 | ✅ 可以向命令发送输入 | ❌ 不能发送输入 | ❌ 不能发送输入 |
进程控制 | 创建子进程,通过管道通信 | 创建子进程,等待完成 | 替换当前进程 |
返回值 | FILE* 流指针 | 命令退出状态 | 成功不返回 |