The difference between popen and system

1. The exec function cluster

2、system()

3、popen()


1. The exec function cluster

    exec has a total of 7 functions, called exec function cluster. When a process calls the exec function, the program executed by the process is completely replaced with a new program, and the new program is executed from the main function. Because exec does not create a new process, the process ID of the forward and backward process has not changed . exec just replaces the text segment, data segment, heap segment, and stack segment of the current process with a new program on the disk.

#include <unistd.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 execve(const char *path, char *const argv[], char *const envp[]);

int fexecve(int fd, char *const argv[], char *const envp[]);
  • The functions with l, execl, execlp, execle, indicate that the following parameters are given in the form of variable parameters and all end with a null pointer.
execl("/bin/ls", "ls", NULL);
  • Functions with v, execv, execvp, indicate that the parameters required by the command are given in the form of char *arg[] and the last element of arg must be NULL.
char *argv[] = {"ls", "-a", "-l", NULL};
execvp("ls", argv);
  • The function with p, execlp, execvp, means that the first parameter does not need to give a specific path, just give the function name, the system will look for the corresponding program in the PATH environment variable
execlp("ls", "ls", "-l", NULL);
  • The function with e, execle, passes environment variables to the process that needs to be replaced, and the original environment variables no longer work.
char *envp[] = {"AA=11", "BB=22", NULL};
execle("./env", "env", NULL, envp);
  • The function with f, fexecve, takes a file descriptor as a parameter.

2、system()

    The system() function performs three steps:

  • fork a child process;
  • Call the exec function in the child process to execute the command;
  • Call wait in the parent process to wait for the end of the child process. For fork failure, the system() function returns -1. If the exec execution is successful, that is, the command is successfully executed, the value returned by the command through exit or return is returned. (Note that the successful execution of the command does not mean successful execution, such as command: "rm debuglog.txt", the command is executed smoothly regardless of whether the file exists or not) If the exec execution fails, that is, the command is not executed smoothly, such as being interrupted by a signal. Or the command command does not exist at all, and the system() function returns 127. If the command is NULL, the system() function returns a non-zero value, generally 1.

  We see that the calling process of the system will wait for the completion of the shell command execution (waitpid waits for the end of the child process) before returning during the execution. We can understand that the system is a serial execution, and the calling process gives up "control" during the execution.

int system(const char * cmdstring)
{
    pid_t pid;
    int status;
    if(cmdstring == NULL)
    {
        return (1); //如果cmdstring为空,返回非零值,一般为1
    }
    if((pid = fork()) < 0)
    {
        status = -1; //fork失败,返回-1
    }
    else if(pid == 0)
    {
        execl("/bin/sh", "sh", "-c", cmdstring, (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成功,则返回子进程的返回状态
}

3、popen()

  •  popen is always used with pclose. popen() creates an anonymous pipe, forks or invokes a child process, and then executes the command. The return value is in the standard IO stream. Because it is in the pipeline, the data stream is one-way. Command can only generate stdout or read stdin, so type has only two values:'w' or'r'. r indicates that the command reads the data stream from the pipe, and w indicates that the stdout of the command is output to the pipe. The command cannot be read and output at the same time. popen returns the pointer of the FIFO data stream. Parallel execution, you can read the standard output of the command in the program, or write the standard input of the command in the program.
  • The pclose function closes the standard IO stream, waits for the termination of the command, and then returns to the termination status of the shell.
static pid_t    *childpid = NULL;  
                        /* ptr to array allocated at run-time */  
static int      maxfd;  /* from our open_max(), {Prog openmax} */  

#define SHELL   "/bin/sh"  

FILE* popen(const char *cmdstring, const char *type)  
{  
    int     i, pfd[2];  
    pid_t   pid;  
    FILE    *fp;  

    /* only allow "r" or "w" */  
    if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {  
        errno = EINVAL;     /* required by POSIX.2 */  
        return(NULL);  
    }  

    if (childpid == NULL) {     /* first time through */  
        /* allocate zeroed out array for child pids */  
        maxfd = open_max();  
        if ( (childpid = calloc(maxfd, sizeof(pid_t))) == NULL)  
            return(NULL);  
    }  

    if (pipe(pfd) < 0)  
        return(NULL);   /* errno set by pipe() */  

    if ( (pid = fork()) < 0)  
        return(NULL);   /* errno set by fork() */  
    
    /* child */ 
    else if (pid == 0) {                             
        if (*type == 'r') {  
            close(pfd[0]);  
            if (pfd[1] != STDOUT_FILENO) {  
                dup2(pfd[1], STDOUT_FILENO);  // 子进程的标准输出写到pfd[1]
                close(pfd[1]);  
            }  
        } else {  
            close(pfd[1]);  
            if (pfd[0] != STDIN_FILENO) {  
                dup2(pfd[0], STDIN_FILENO);  // 子进程从的标准输入从pfd[0]读入
                close(pfd[0]);  
            }  
        }  
          /* close all descriptors in childpid[] */  
        for (i = 0; i < maxfd; i++)  
            if (childpid[i] > 0)  
                close(i);  

        execl(SHELL, "sh", "-c", cmdstring, (char *) 0);  
        _exit(127);  
    }  

    /* parent */  
    if (*type == 'r') {  
        close(pfd[1]);  
        if ((fp = fdopen(pfd[0], type)) == NULL)  
            return(NULL);  
    } else {  
        close(pfd[0]);  
        if ((fp = fdopen(pfd[1], type)) == NULL)  
            return(NULL);  
    }  
    childpid[fileno(fp)] = pid; /* remember child pid for this fd */  
    return(fp);  
}  

 

Guess you like

Origin blog.csdn.net/MOU_IT/article/details/110881623