linux popen阻塞_Linux中system()函数与popen()函数

1. system()

程序可以通过调用 system() 函数来执行任意的shell命令。同样这个Shell命令执行的输出会打印在终端上。\

#include

int system(const char* command);

system()库函数通过fork出一个子进程,并且让子进程通过execl()程序替换来执行对应的 shell 命令。问题是替换成什么程序可以执行shell命令呢?答案是 /bin 目录下的sh程序。sh -c command即可执行指定的命令。sh 其实就是一个shell程序,在我的系统中 sh 是一个指向 dash 的软链接,所以system()的真正执行步骤应该是:调用system(),函数内部创建了一个子进程并替换成 shell 程序,而 shell 程序执行一条命令的步骤同样是创建一个子进程,并让子进程替换成需要执行的 command,所以可以看到system()运行期间总共存在三个进程。下图展现了用system()执行sleep命令的步骤:

system()当命令执行完毕才会返回,也就是阻塞函数。

在命令执行期间,SIGCHLD信号将被阻塞,SIGINT和SIGQUIT信号将被忽略。

如果 command 为NULL,则system()返回一个表示当前系统是否有可用shell的状态。

2. 基本的 system() 实现

先按照文档的描述来实现一个简单的system()函数,这其实就是对Linux编程的基本知识如创建子进程、进程等待、进程替换的一个综合运用。

int my_system(const char* command) {

pid_t pid = fork();

int status;

switch(pid) {

case -1:

return -1;

case 0: // child

execl("/bin/sh", "sh", "-c", command, NULL);

exit(-1); // execl error

default: // parent

waitpid(pid, &status, 0);

return status;

}

}

3. popen() : 通过管道与Shell命令进行通信

popen()与system()的功能类似,同样是执行一条Shell命令。但popen()使用了管道来读取Shell命令的输出或者是通过管道来给Shell命令传递输入。

#include

FILE* popen(const char* command, const char* mode);

int pclose(FILE* stream);

popen()函数创建了一个管道,然后创建一个子进程来执行Shell,而Shell同样需要创建一个子进程来执行指定的Shell命令。这个过程类似于system()。mode 参数是一个字符串,它确定函数的调用者是从管道中读取 Shell 命令的输出(mode为 r ),还是通过管道给Shell命令传递输入(mode为 w )。(由于单个管道只支持单向信息传输,所以必须在创建管道之前就确定数据传输方向)。下图展示了两种mode取值的不同之处:

popen()返回了该管道的操作句柄,即一个文件流指针,函数调用者可以通过这个操作句柄来和Shell命令进行通信。这也点出了popen()与system()的不同之处,即system()是一个阻塞函数,Shell命令执行完成之前不会返回,用户通过终端与Shell命令交互。而popen()是一个非阻塞函数,它在建立一个子进程并执行程序替换sh -c command之后会立刻返回,而不等待Shell程序的终止。用户通过主程序与Shell命令进行通信。即可以总结为popen()的调用进程与Shell命令进程是并行执行的。

由于使用的是管道,所以与 pipe 创建出的管道有相同的读写特性,即管道写端关闭后尝试读会返回0,管道读端关闭后尝试写会受到 SIGPIPE 信号而被杀死。

一旦 I/O 结束后可以使用pclose()函数关闭管道,并且该函数会等待Shell程序终止才返回。而pclose()的返回值为Shell进程的终止状态,即 wait 所得到的状态。

4. 基本的popen() 实现

#include

#include

#include

FILE* my_popen(const char* command, const char* type) {

FILE* fp;

// if type is invalid

if((type[0] != 'w' && type[0] != 'r') || type[1] != '\0') {

return NULL;

}

int pipefd[2];

pipe(pipefd);

pid_t pid = fork();

switch(pid) {

case -1:

perror("fork error");

exit(0);

case 0: // child

if(type[0] == 'w') {

close(pipefd[1]);

dup2(pipefd[0], STDIN_FILENO);

close(pipefd[0]); // close extra read end

}

else if(type[0] == 'r') {

close(pipefd[0]);

dup2(pipefd[1], STDOUT_FILENO);

close(pipefd[1]); // close extra write end

}

execl("/bin/sh", "sh", "-c", command, NULL);

exit(-1);

default: // parent

if(type[0] == 'r') {

close(pipefd[1]);

fp = fdopen(pipefd[0], type);

}

else {

close(pipefd[0]);

fp = fdopen(pipefd[1], type);

}

return fp;

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值