1、exec函数簇
2、system()
3、popen()
1、exec函数簇
exec总共有7个函数,称为exec函数簇。当进程调用exec函数时,该进程执行的程序完全替换为新程序,而新程序则从main函数开始执行。因为exec并不是创建新进程,所以前后进的进程ID没有改变。exec只是用磁盘上一个新程序替换了当前进程的正文段、数据段、堆段和栈段。
#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[]);
- 带l的函数,execl,execlp,execle,表示后边的参数以可变参数的形式给出且都以一个空指针结束。
execl("/bin/ls", "ls", NULL);
- 带v的函数,execv,execvp,表示命令所需的参数以char *arg[]形式给出且arg最后一个元素必须是NULL。
char *argv[] = {"ls", "-a", "-l", NULL};
execvp("ls", argv);
- 带p的函数,execlp,execvp,表示第一个参数无需给出具体的路径,只需给出函数名即可,系统会在PATH环境变量中寻找所对应的程序
execlp("ls", "ls", "-l", NULL);
- 带e的函数,execle,将环境变量传递给需要替换的进程,原来的环境变量不再起作用。
char *envp[] = {"AA=11", "BB=22", NULL};
execle("./env", "env", NULL, envp);
- 带f的函数, fexecve,取文件描述符作为参数。
2、system()
system()函数执行了三步操作:
- fork一个子进程;
- 在子进程中调用exec函数去执行command;
- 在父进程中调用wait去等待子进程结束。 对于fork失败,system()函数返回-1。 如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值。 (注意,command顺利执行不代表执行成功,比如command:"rm debuglog.txt",不管文件存不存在该command都顺利执行了) 如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127. 如果command为NULL,则system()函数返回非0值,一般为1。
我们看到system在执行期间调用进程会一直等待shell命令执行完成(waitpid等待子进程结束)才返回,我们可以理解system为串行执行,在执行期间调用进程放弃了”控制权”。
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总是和pclose一起出现被使用的。popen() 创建一个匿名管道,通过fork或者invoke一个子进程,然后执行command。返回值在标准IO流中,由于是在管道之中,因此数据流是单向的,command只能产生stdout或者读取stdin,因此type只有两个值:‘w’或‘r’。r表示command从管道中读取数据流,而w表示command的stdout输出到管道中。command无法同时读取和输出。popen返回该FIFO数据流的指针。并行执行,可以在程序中读取命令的标准输出,也可在程序中写入命令的标准输入。
- pclose函数关闭标准IO流,等待命令的终止,然后返回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);
}