exec族函数,system函数,popen函数的用法
exec族函数
作用
我们一般使用fork()来创建一个新的进程,在新进程里面如果说我们想要执行另外一个程序,使用exec族函数是一个解决方法。当新进程调用exec族函数的时候,进程原来的程序就会被替换成新的程序。这个过程中是没有创建新的进程的,进程前后的pid也是不变的。
函数原型
#include<stdio.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 execvpe(const char *file, char *const argv[],char *const envp[]);
返回值
exec族函数调用成功之后并不会返回值,但是如果调用失败时,会自动设置error值并返回-1,然后在原程序的断点处继续运行。
参数说明
path: 可执行文件的路径
arg:可执行文件的参数,最后必须以NULL结尾
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
用demo来验证exec族函数的用法
execl(const char *path,const char *arg,…)
此demo采用的开发环境是ubuntu20.04
- 先写一个C程序demo.c,该C程序可以用来获取命令行输入的参数。
#include<stdio.h>
int main(int argc,char *argv[]){
int i = 0;
for(i=0;i<argc;i++){
printf("argv[%d] : %s\n",i,argv[i]);
}
}
运行结果:
命令行输入:
gcc demo.c -o echoarg
./echoarg aa bb
输出结果:
argv[0]=./echoarg
argv[1]=aa
argv[2]=bb
- 写demo2.c来验证execl()
#include<stdio.h>
#include<unistd.h>
int main(){
printf("before execl...\n");
if(execl("./echoarg","echoarg","abc",NULL)==-1){
printf("execl failed!\n");
}
printf("after execl...\n");
return 0;
}
运行结果
命令行输入:
gcc demo2.c
./a.out
输出结果:
before execl…
argv[0]:echoarg
argv[1]:abc
可以看到,当demo2.c调用execl()之后,在第一个输出语句执行完之后,就直接执行了由demo.c编译而成的可执行文件echoarg。并把最后应该输出的after execl…给去掉了。这就可以证明上面说的:当新进程调用exec族函数的时候,进程原来的程序就会被替换成新的程序。
execlp()
我们先把上面的demo2.c复制成demo3.c,然后稍微改一下
#include<stdio.h>
#include<unistd.h>
int main(){
printf("before execl...\n");
if(execl("ps","ps","-l",NULL)==-1){
printf("execl failed!\n");
}
printf("after execl...\n");
return 0;
}
命令行输入:
gcc demo3.c
./a.out
输出结果:
before execl…
execl failed!
after execl…
说明execl()函数调用失败。
原因就是execl()函数的第一个参数没有包含路径,程序找不到可执行文件。
怎么解决这个问题呢?有两种解决方法
-
第一种:改变环境变量(建议最好不要用)
1.1. 首先先查看环境变量:
命令行命令:$echo PATH
这个时候终端就会显示环境变量
1.2. 将需要该路径名写入环境变量
命令行命令:export PATH="" -
第一种:将execl()换成execlp()
将前面的demo3.c复制成demo4.c
#include<stdio.h>
#include<unistd.h>
int main(){
printf("before execl...\n");
if(execlp("ps","ps","-l",NULL)==-1){
printf("execl failed!\n");
}
printf("after execl...\n");
return 0;
}
命令行输入:
gcc demo4.c
./a.out
输出结果:
before execl…
(ps的结果,这里就省略了)
这就说明:execlp()可以通过环境变量PATH来查询可执行文件
其他的exec族函数
其他的函数用到的几率不多,后期用到的时候可以直接查阅相关文档。
这里就不多介绍了!
system函数
有了execl族函数的基础,到了system函数这里,看他的源码就可以看的大概了。
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;
}
分析一下代码,大概流程是这样的
- 首先,先判断参数是否为空
- 用fork()创建新进程,根据fork()的返回值判断当前进程为子进程还是父进程还是调用失败。
- 如果当前进程为子进程,则在命令行中执行参数cmdstring代表的命令,若在调用sh -c cmdstring命令时失败,就会返回-127,别的原因就返回-1;如果为父进程,则等待子进程执行完毕;如果调用失败,将状态码复赋值为-1.
用demo来验证system()的作用
demo5.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
int data = 10;
while(1){
printf("Please input a data\n");
scanf("%d",&data);
if(data == 1){
int fdSrc;
pid = fork();
if(pid >0){
wait(NULL);
}
if(pid == 0){
system("./changData");
}
}
else{
printf("wait,do nothing\n");
}
}
return 0;
}
当键盘输入1时,用fork()创建一个新进程。fork()会返回两次值,一次是父进程,一次是子进程。当前进程为子进程时,在命令行执行./changeData,也就是另外一个可执行文件。
这里有一个老师写的博客,讲的就是linux的system函数,写的很详细:
https://www.cnblogs.com/leijiangtao/p/4051387.html
popen函数
popen()的作用就是可以保存命令行输出的结果。在linux系统,用命令man popen可以查看popen()函数的具体介绍:
NAME
popen, pclose - pipe stream to or from a process
SYNOPSIS
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
参数说明
command:命令行命令
type:类型
返回值
popen()函数最后返回的是一个“流”,我们需要用fread()从这个流里面获取保存的信息
直接用demo来验证一下popen
demo6.c
#include<stdio.h>
int main(){
char ret[1024]={0};
FILE *fp;
fp = popen("ps","r");
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
int nread = fread(ret,1,1024,fp);
printf("read ret %d byte,ret=%s\n",nread,ret);
return 0;
}
命令行输入:
gcc demo6.c
./a.out
输出结果:
read ret 151 byte,ret= PID TTY TIME CMD
110801 pts/2 00:00:00 bash
114116 pts/2 00:00:00 a.out
114117 pts/2 00:00:00 sh
114118 pts/2 00:00:00 ps