system函数
在一个程序中执行命令字符串很方便。例如,我们想把时间和日期放到一份文件中,先用time得到当前日历时间,接着调用localtime将日历转换为年、月、日、时、分、秒,然后调用strftime对上面结果进行格式化处理,最后将结果写入到文件中。
但是我们如果用stystem函数呢?
直接system(“data>file”),显然很方便。
system函数原型
#include<stdio.h>
int system(const char* command)
system调用了fork创建子进程,由子进程来调用/bin/sh -c来执行参数command命令,执行完后返回。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT信号则会被忽略。
调用/bin/sh来执行参数指定的命令,/bin/sh 一般是一个软连接,指向某个具体的shell。
实际上system执行过程中调用了三个函数:
1.调用fork创建子进程
2.子进程调用exec去执行command命令
3.父进程调用waitpid等待子进程退出
因为system调用了fork,exec、waitpid函数,因此有多个返回值:
1.command为空,返回0;
2.fork,exec,waitpid都执行成功,返回shell终止状态;
3..如果fork失败或者waitpid返回除EINTR之外的错误,则system返回-1,而且error中设置了错误类型值。
4.如果exec失败(表示不能执行shell),则返回127(等同shell执行exit(127)
函数源码:
int system(const char * command)
{
pid_t pid;
int status;
if(command == NULL)
{
return (1); //如果cmdstring为空,返回非零值,一般为1
}
if((pid = fork())<0)
{
status = -1; //fork失败,返回-1
}
else if(pid == 0) //子进程
{
execl("/bin/sh", "sh", "-c", command, (char *)0);
_exit(127); // exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在啦~~
}
else //父进程
{
while(waitpid(pid, &status, 0) < 0)
{
if(errno != EINTR)
{
status = -1; //如果waitpid被信号中断,则返回-1
break;
}
}
}
return status; //如果waitpid成功,则返回子进程的返回状态
}
popen函数
popen通过创建管道的方式来启动一个进程,并调用 shell. 因为管道是被定义成单向的, 所以 type 参数只能定义成只读或者只写, 不能是两者同时, 结果流也相应的是只读或者只写.
popen函数原型
#include<stdio.h>
FILE *popen(const char* command,const char* type)
int pclose(FILE* stream);
command 参数是一个字符串指针, 指向的是一个以 null 结尾的字符串, 这个字符串包含一个 shell 命令. 这个命令被送到 /bin/sh 以 -c 参数执行, 即由 shell 来执行.
返回值:成功返回一个文件指针,失败返回NULL。只能用 pclose() 函数来关闭。
popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。 随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中。
实例:
#include<stdio.h>
#include<stdlib.h>
int main(int argc,char* argv[])
{
char buf[128];
FILE* fp = popen("ls -l","w");
if(fp == NULL)
{
perror("popen");
exit(1);
}
while(fgets(buf,sizeof(buf),fp))
{
printf("%s",buf);
}
pclose(fp);
return 0;
}
测试结果:
总结:
1.system,popen都可用来创建进程.
2.system相当于fork+exec+waitpid,而popen是通过管道来启动进程.
3.system 在执行期间,调用进程会一直等待 shell 命令执行完成(waitpid),但是 popen 无需等待 shell 命令执行完成就返回了。可以理解为,system为串行执行,popen 为并行执行。
4.popen 函数执行完毕后必须调用 pclose 来对所创建的子进程进行回收,否则会造成僵尸进程的情况。