Linux学习笔记-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

  1. 先写一个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

  1. 写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.1. 首先先查看环境变量:
    命令行命令:$echo PATH
    这个时候终端就会显示环境变量
    1.2. 将需要该路径名写入环境变量
    命令行命令:export PATH=""

  2. 第一种:将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;
}

分析一下代码,大概流程是这样的

  1. 首先,先判断参数是否为空
  2. 用fork()创建新进程,根据fork()的返回值判断当前进程为子进程还是父进程还是调用失败。
  3. 如果当前进程为子进程,则在命令行中执行参数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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值