fork系统调用用于创建一个新进程,称为子进程,它与进程(称为系统调用fork的进程)同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。子进程使用相同的pc(程序计数器),相同的CPU寄存器,在父进程中使用的相同打开文件。
它不需要参数并返回一个整数值。下面是fork()返回的不同值。
负值:创建子进程失败。
零:返回到新创建的子进程。
正值:返回父进程或调用者。该值包含新创建的子进程的进程ID [1] 。
头文件
#include<unistd.h>/#包含<unistd.h>
#include<sys/types.h>/#包含<sys/types.h>
pid_t fork( void);
(pid_t 是一个宏定义,其实质是int 被定义在#includesys/types.h>中)
返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
说明:原进程调用fork()函数创建子进程, fork()被调用一次返回两次,原进程中返回子进程id,新进程中返回值为0;
子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
fork()例:
#include "head.h"
int main() {
pid_t pid;
if((pid = fork()) < 0) {
perror("fork()"); //调用一次,返回两次,原进程返回新进程id(不为零),新进程返回
exit(1);
}
if(pid) {
printf("I'm parent process! <%d>--><%d>--><%d>\n",getppid(), getpid(), pid);
}
else {
printf("Im child process! <%d>--><%d>\n", getppid(), getpid());
}
return 0;
}
I'm parent process! <14180>--><14371>--><14372>
Im child process! <14371>--><14372>
getpid---- 获取父进程id;
getppid---获取当前进程id;
例二:
#include "head.h"
int main() {
pid_t pid;
int i = 1;
for(; i <=10; i++) {
if ((pid = fork()) < 0) {
perror("fork()");
exit(1);
}
if (pid == 0) { //终止子进程,否则将继续调用fork(),会创建无数个子孙进程;
break;
}
}
if(!pid) {
printf("I'm %dth child process!\n", i);
}
else
printf("I'm the parent process!\n");
return 0;
}
僵尸进程:子进程再其父进程未调用wait(),或 waitip()的情况下退出,这些进程将成为僵尸进程;如果父进程存在,且不调用wait(),这些进程将无法回收,等到父进程结束后由initi(1号进程)回收;
孤儿进程:父进程结束后他的其他子进程还存在,这些进程就是孤儿进程;这些进程将被init(1号进程)收养;
exec 函数
exec函数的原型如下:
int execl(const char * path,const char * arg,…);
int execle(const char * path,const char * arg,char * const envp[]);
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[]);
path:要执行的程序路径。可以是绝对路径或者是相对路径。在execv、execve、execl和execle这4个函数中,使用带路径名的文件名作为参数。
file:要执行的程序名称。如果该参数中包含“/”字符,则视为路径名直接执行;否则视为单独的文件名,系统将根据PATH环境变量指定的路径顺序搜索指定的文件。
argv:命令行参数的矢量数组。
envp:带有该参数的exec函数可以在调用时指定一个环境变量数组。其他不带该参数的exec函数则使用调用进程的环境变量。
arg:程序的第0个参数,即程序名自身。相当于argv[0]。
…:命令行参数列表。调用相应程序时有多少命令行参数,就需要有多少个输入参数项。注意:在使用此类函数时,在所有命令行参数的最后应该增加一个空的参数项(NULL),表明命令行参数结束。
返回值:一1表明调用exec失败,无返回表明调用成功。
使用exec函数族主要有两种情况:
(1)当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用exec函数族中的任意一个函数让自己重生。
(2)如果一个进程想执行另一个程序,**那么它就可以调用fork函数新建一个进程,然后调用exec函数族中的任意一个函数,**这样看起来就像通过执行应用程序而产生了一个新进程(这种情况非常普遍)。
#include "head.h"
int main() {
pid_t pid;
if((pid = fork()) < 0) {
perror("fork()");
exit(1);
}
if (pid) {
wait(NULL);
}
else {
//execl("/bin/cat", "cat", "/etc/hosts", NULL);
execlp("cat", "cat", "/etc/hosts", NULL);
printf("haha\n");
fflush(stdout);
// 这句不会输出,上一句执行成功后即被替换为cat 语句,17行之后将不再执行;
}
return 0;
}