通过调用fork和exec函数都能创建新的进程,但两者有着本质的区别:fork函数拷贝了父进程的内存映像,而exec函数用用新的映像来覆盖调用进程的进程映像的功能。
一 fork函数
#include <unistd.h>
pid_t fork(void); //创建子进程成功时,向子进程返回0,并将子进程的进程ID返回给父进程
//创建失败时,返回-1,并将errno设置为EAGAIN
返回值是允许父进程和子进程区别自己并执行不同代码的关键特征。
#include <stdio.h> #include <unistd.h> #include <sys/types.h> int main( void ){ pid_t childpid; //子进程的ID childpid = fork(); //创建子进程 if (childpid==- 1 ){ //创建子进程失败 perror( " Failed to fork " ); return 1 ; } if (childpid== 0 ){ //创建子进程成功 printf( " I am child %ld\n " ,( long )getpid()); //打印子进程的ID } else { printf( " I am parent %ld\n " ,( long )getpid()); //打印父进程的ID } }
二 exec函数
有六种不同形式的exec函数,如下:
#include <unistd.h> int execl( const char *path, //进程映像文件的路径名,可以是全限定路径名,也可以是相对于当前目录的路径名
const char *arg,... ); int execle( const char *path, const char *arg,..., char *const envp[] ); //最后一个参数必须以空指针(NULL)作结束 int execlp( const char *file, const char *arg,... ); int execv( const char *path,
char * const argv[]); //参数数组,用来存放指向你的字符串参数的指针数组 int execve( const char *path, char * const argv[], char * const envp[]); int execvp( const char *file, char * const argv[]);
execv开头的函数是把参数以"char *argv[]"这样的形式传递命令行参数。而execl开头的函数采用了我们更容易习惯的方式,把参数一个一个列出来,然后以一个NULL
表示结束,也可以写成(char *)0。
如果创建子进程不成功,所有的exec函数都返回-1,并设置errno,以下是errno的类型和原因。
E2BIG:新进程的参数表和环境表长度以系统所允许的ARG_MAX字节的限制要长
EACCES:对新进程路径前缀中目录的搜寻权限被否定,新进程映像文件的执行权限被否定,或者新进程映像文件不是正常的文件,且不能被执行
EINVAL:新进程映像文件有恰当的权限,且以可识别可执行的二进制格式出现
ELOOP:在对参数path或file进行解析时存在循环
ENAMETOOLONG:path或file的长度超出了PATH_MAX的范围,或者路径名组件比NAME_MAX要长
ENOENT:path或file组件命名的不是一个现存的文件,或者path或file为空字符串
ENOEXEC:映像文件有恰当的访问权限,但它的格式不可识别(不适用于execlp或execvp)
ENOTDIR:映像文件路径前缀的组件不是一个目录
int main( int argc, char *argv[], char * envp[]) { char *arg[]={ " ls " , " -a " ,NULL}; if (fork()== 0 ) { printf( " execl...........\n " ); if (execl( " /bin/ls " , " ls " , " -a " ,NULL)< 0 ) { fprintf(stderr, " execl failed:%s " ,strerror(errno)); return - 1 ; } } if (fork()== 0 ) { printf( " execv...........\n " ); if (execv( " /bin/ls " ,arg)< 0 ) { fprintf(stderr, " execl failed:%s\n " ,strerror(errno)); return - 1 ; } } if (fork()== 0 ) { printf( " execlp...........\n " ); if (execlp( " ls " , " ls " , " -a " ,NULL)< 0 ) { fprintf(stderr, " execl failed:%s " ,strerror(errno)); return - 1 ; } } if (fork()== 0 ) { printf( " execvp...........\n " ); if (execvp( " ls " ,arg)< 0 ) { fprintf(stderr, " execl failed:%s\n " ,strerror(errno)); return - 1 ; } } if (fork()== 0 ) { printf( " execle...........\n " ); if (execle( " /bin/ls " , " ls " , " -a " ,NULL,envp)< 0 ) { fprintf(stderr, " execl failed:%s " ,strerror(errno)); return - 1 ; } } if (fork()== 0 ) { printf( " execve...........\n " ); if (execve( " /bin/ls " ,arg,envp)< 0 ) { fprintf(stderr, " execl failed:%s\n " ,strerror(errno)); return - 1 ; } } return 0 ; }