system()、popen()、fork()三个函数 的区别

一、system:在执行期间调用进程会一直等待shell命令执行完成。
fork :执行期间父进程等待子进程的退出码。
实际上system()函数执行了三步操作:
1.fork一个子进程;
2.在子进程中调用exec函数去执行command;
3.在父进程中调用wait去等待子进程结束。
返回值:
1>如果 exec 执行成功,即 command 顺利执行,则返回 command 通过 exit 或 return 的返回值。(注意 :command 顺利执行不代表执行成功,当参数中存在文件时,不论这个文件存不存在,command 都顺利执行)
2>如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在, 返回 127
3>如果 command 为 NULL, 则 system 返回非 0 值.
4>对于fork失败,system()函数返回-1。
判断一个 system 函数调用 shell 脚本是否正常结束的方法的条件应该是
1. status != -1
2.(WIFEXITED(status) 非零且 WEXITSTATUS(status) == 0

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
        int status=system("ls -l");
        if(status==-1)
                perror("system()"),exit(1);//相当于fork失败
        if(WIFEXITED(status)!=0){//正常退出
                if(WEXITSTATUS(status)==0){//操作正确
                        printf("run commond correct!\n");
                }
                else{
                        printf("run commond failed and exit coding is %d\n",WEXITSTATUS(status));
                }

        }
        else
                printf("exit coding is %d\n",WEXITSTATUS(status));
}


二、 popen()函数:
popen:无须等待shell命令执行完成就返回 (并行执行),
popen后需要调用pclose防止子进程变成”僵尸”状态。
创建一个管道用于进程间通信,并调用shell,因为管道被定义为单向的 所以 type 参数 只能定义成 只读或者 只写, 不能是 两者同时, 结果流也相应的 是只读 或者 只写.
原型: FILE *popen(const char *command, const char *type);
函数功能:popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令。这个进程必须由 pclose 关闭。
command参数:
command 参数 是一个字符串指针, 指向的是一个 以 null 结束符 结尾的字符串, 这个字符串包含 一个 shell 命令. 这个命令 被送到 /bin/sh 以 -c 参数 执行, 即由 shell 来执行.
type 参数 也是 一个 指向 以 null 结束符结尾的 字符串的指针
参数type可使用“r”代表读取,“w”代表写入。
依照此type值,popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。
随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中。
返回值:
若成功则返回文件指针,否则返回NULL,错误原因存于errno中

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
        FILE* fd=popen("ls -l","r");
        char buf[1024]={0};
        if(fd==NULL)
                perror("popen()"),exit(1);//相当于fork失败
        else{
                while(fgets(buf,1024,fd)!=NULL){
                        fprintf(stdout,"%s",buf);
                }
        }
        pclose(fd);
        return 0;
}


三、fork函数
fork()函数若调用成功有两个返回值:子进程返回0,父进程返回进程标记,出错返回-1。fork()函数将运行的程序分为两个几乎一样的进程,每个进程都是从同一位置启动的线程。两个进程中的线程同时执行,就像两个用户同时启动。
注意:
*
当调用fork函数时,在该位置的进程一分为二,一个是父进程,一个是子进程。
*
若调用成功返回两个值,父进程返回子进程的标志,子进程的返回值是0,不成功返回-1.

之所以成功调用会返回两个值是因为复制时,复制了进程的堆栈段。所以两个进程会停留在fork函数中,等待返回。
每个进程都有不一样的进程标识符,可以通过getpid()函数获得,有一个记录父进程pid的变量,可以通过getppid()获得变量的值。
子进程是父进程的副本,他会获得父进程的数据空间、堆、栈、等资源的副本。注意,父子进程是不共享这些空间的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
        printf("before %d\n",getpid());
        pid_t pid=fork();
        if(pid==0){
                char *arg[]={"ls","-l",NULL};
                execvp("ls",arg);
        }else{
                while(1)
                        ;
                wait(NULL);
                exit(0);
        }

}

猜你喜欢

转载自blog.csdn.net/ffsiwei/article/details/80975001
今日推荐