对于Linux下system()函数的深度理解(4)

【IPC通信】基于管道的popen和pclose函数
标准I/O函数库提供了popen函数,它启动另外一个进程去执行一个shell命令行。这里我们称调用popen的进程为父进程,由popen启动的进程称为子进程。 popen函数还创建一个管道用于父子进程间通信。父进程要么从管道读信息,要么向管道写信息,至于是读还是写取决于父进程调用popen时传递的参数。下在给出popen、pclose的定义:
#include


FILE * popen( const char * command,const char * type);
 int pclose(FILE * stream);
下面通过例子看下popen的使用:假如我们想取得当前目录下的文件个数,在shell下我们可以使用:
 ls | wc -l 我们可以在程序中这样写:
 #include
#include
#include
#include
 #define MAXLINE 1024
 int main()
{
char result_buf[MAXLINE], command[MAXLINE];
int rc = 0; // 用于接收命令返回值
FILE *fp;
snprintf(command, sizeof(command), "ls ./ | wc -l");
 fp = popen(command, "r");
if(NULL == fp)
{
perror("popen执行失败!");
exit(1);
}
while(fgets(result_buf, sizeof(result_buf), fp) != NULL)
{
 if('\n' == result_buf[strlen(result_buf)-1])
{
result_buf[strlen(result_buf)-1] = '\0';
}
printf("命令【%s】 输出【%s】\r\n", command, result_buf);
}
 rc = pclose(fp);
if(-1 == rc)
{
perror("关闭文件指针失败");
 exit(1);
}
else
{
printf("命令【%s】子进程结束状态【%d】命令返回值【%d】\r\n", command, rc, WEXITSTATUS(rc));
}
 return 0;
}
编译并执行:
$ gcc popen.c
$ ./a.out 命令【ls ./ | wc -l】
输出【2】 命令【ls ./ | wc -l】子进程结束状态【0】命令返回值【0】
上面popen只捕获了command的标准输出,如果command执行失败,子进程会把错误信息打印到标准错误输出,父进程就无法获取。比如,command命令为“ls nofile.txt” ,事实上我们根本没有nofile.txt这个文件,这时shell会输出“ls: nofile.txt: No such file or directory”。这个输出是在标准错误输出上的。通过上面的程序并无法获取。注:如果你把上面程序中的command设成“ls nofile.txt”,编译执行程序你会看到如下结果:
$ gcc popen.c
$ ./a.out ls: nofile.txt: No such file or directory
命令【ls nofile.txt】子进程结束状态【256】命令返回值【1】 需要注意的是第一行输出并不是父进程的输出,而是子进程的标准错误输出。有时子进程的错误信息是很有用的,那么父进程怎么才能获取子进程的错误信息呢?这里我们可以重定向子进程的错误输出,让错误输出重定向到标准输出(2>&1),这样父进程就可以捕获子进程的错误信息了。
例如command为“ls nofile.txt 2>&1”,输出如下:
命令【ls nofile.txt 2>&1】 输出【ls: nofile.txt: No such file or directory】
命令【ls nofile.txt 2>&1】子进程结束状态【256】命令返回值【1】
附:子进程的终止状态判断涉及到的宏,设进程终止状态为status. WIFEXITED(status)如果子进程正常结束则为非0值。 WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。 WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。 WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。 WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。 WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。 2011-11-12 任洪彩
 qdurenhongcai@163.com 转载请注明出处。


但是根据上面那位博主说的使用system()函数前把SIGCHLD信号处理方式显式修改为SIG_DFL方式,同时记录原来的处理方式,使用完system()后再设为原来的处理方式后,程序还是会死掉.而且看不到system的返回值是多少(因为system在执行系统命令的时候,程序已经挂掉了),故暂时使用博主提到的第二种解决方式使用popen()函数替代system()函数.修改后的函数如下
int my_system(const char * cmd)
{
FILE * fp;
int res; char buf[1024];
if (cmd == NULL)
{
printf("my_system cmd is NULL!\n");
 return -1;
 }
if ((fp = popen(cmd, "r") ) == NULL)
{
perror("popen");
 printf("popen error: %s/n", strerror(errno)); return -1;
}
else
 {
 while(fgets(buf, sizeof(buf), fp))
{
printf("%s", buf);
}
if ( (res = pclose(fp)) == -1)
{
printf("close popen file pointer fp error!\n"); return res;
 }
else if (res == 0)
{
 return res;
 }
else
{
printf("popen res is :%d\n", res); return res;
}
}
 }
此时调用my_system()来执行system函数的功能(my_system函数中是使用popen()函数来实现的), 测试了一天,没有再次出现程序突然死掉的问题(修改前连续循环调用system()函数测试,每10次就会至少导致程序挂掉一次.连续不停顿的调用). 以上是我对这个问题的总结,先做个记录,待修复bug后再回来仔细研究.

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/e8bdb4476961837c542a6c15e6eb0cf2.html