赞
踩
fork函数:
头文件:#include <unistd.h>
函数原型:pid_t fork(void)
fork函数的返回值:
fork函数的返回值有三种,(1)创建失败,则fork返回一个负值;(2)若创建成功,则在父进程中,fork返回新创建的子进程的pid号;同时,在子进程中,fork函数返回0值。
要求:让进程a(父进程)去创建新的进程b(子进程)
#include <unistd.h> #include <stdio.h> int main(void) { //定义变量,用该变量来接收fork的返回值 pid_t pid; pid = fork(); //判断fork函数返回值的三种情况 //第一种情况:返回值小于0,则创建进程失败 if(pid <0){ printf("fork is error\n"); return -1; } //创建进程成功的话,返回的是子进程的pid号 if(pid >0){ printf("This is parent,parent pid is %d\n",getpid()); } //创建成功的话,子进程会返回0值 if(pid == 0){ printf("This is child,child pid is %d, parent pid is %d\n",getpid(),getppid()); } }
在该例程中,有一点需要说明:
想要获取进程的pid号,在C语言中使用的是getpid()函数以及getppid()函数
在ubuntu界面的编译以及运行结果如下图所示:
由运行结果可以看出,在ubuntu界面编译运行之后,会将父进程以及子进程的if语句满足条件后的结果均执行出来。
即:既打印了
This is parent,parent pid is 2479
又打印了
This is child,child pid is 2480, parent pid is 2479
为什么呢?
我们可以将父进程执行fork函数之后创建的子进程看做是父进程的拷贝,这样的话,父进程以及子进程在地址空间里面的内容都一样,代码也一样。执行父进程的过程中,会自动选择执行下面的代码:
if(pid >0){
printf("This is parent,parent pid is %d\n",getpid());
}
而子进程会选择执行pid==0的情况时候的代码,所以在ubuntu界面的执行结果是打印了两句。
注意:子进程的数据空间、堆栈空间都会从父进程中拷贝,但是它们的pid号是不一样的,所以可以通过pid号来区分父子进程;也可以通过fork函数的返回值来区分父子进程。
思考:
(1)子进程创建成功以后,它的代码的执行位置在哪里呢?
父进程一定是从程序的开头开始执行,一直到程序结束。而子进程是从fork函数开始执行的。
(2)父子进程的执行顺序是什么嘞?
观察fork函数例程的执行结果,可以看到是父进程先执行的,那么一定是每次都先执行父进程么?不是这样的,父子进程的执行顺序是不一定的,因为父子进程的执行也需要抢cpu资源,哪个先抢到,哪个就先执行。
在linux中并没有exec函数,而是有6个以exec开头的函数族,下面列举exec函数族的6个函数成员的函数原型
int execl(const char *path, const char *arg, .../* (char *) NULL */);(最重要的一个)
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
exec函数族在使用时,最重要的思想就是“换核不换壳”。怎么解释呢?就相当于是用一个纸箱子盛放了苹果,执行了exec函数族之后,纸箱子没发生任何变化,只是纸箱子的东西发生了变化,纸箱子里可能变成梨啦。
exec函数族的使用场景:
(1)当进程认为自己不能再为系统和用户做出任何的贡献时,就可以调用任何的exec函数族让自己重生;
(2)如果一个进程想要执行另一个程序时,那么它就可以调用fork函数创建一个新的进程,然后调用任何一个exec函数使得子进程重生。
c代码如下:(在fork函数c代码的基础上进行的修改,看子进程与父进程打印出来的i值分别是多少)
#include <unistd.h> #include <stdio.h> int main(void) { //定义变量,用该变量来接收fork的返回值 pid_t pid; int i =0; pid = fork(); //判断fork函数返回值的三种情况 //第一种情况:返回值小于0,则创建进程失败 if(pid <0){ printf("fork is error\n"); return -1; } //创建进程成功的话,返回的是子进程的pid号 if(pid >0){ printf("This is parent,parent pid is %d\n",getpid()); } //创建成功的话,子进程会返回0值 if(pid == 0){ printf("This is child,child pid is %d, parent pid is %d\n",getpid(),getppid()); } i++; printf("i is %d\n",i); return 0; }
在ubuntu界面编译运行的结果如下:
父进程是从头开始运行的,运行到后面会执行一次i++的操作,故i=1;子进程是从fork函数开始执行的,运行到输出结果之后也会进行一下i++的操作,故i=1。父进程与子进程的i是相互独立的,他们不是共享的
下面将子进程进行拦截修改,将打印子进程与父进程的pid号的代码修改为hello.c的代码,查看执行结果
c代码如下:
#include <stdlib.h> #include <unistd.h> #include <stdio.h> int main(void) { //定义变量,用该变量来接收fork的返回值 pid_t pid; int i =0; pid = fork(); //判断fork函数返回值的三种情况 //第一种情况:返回值小于0,则创建进程失败 if(pid <0){ printf("fork is error\n"); return -1; } //创建进程成功的话,返回的是子进程的pid号 if(pid >0){ printf("This is parent,parent pid is %d\n",getpid()); } //创建成功的话,子进程会返回0值 if(pid == 0){ printf("This is child,child pid is %d, parent pid is %d\n",getpid(),getppid()); execl("/home/samba/jincheng/hello","hello",NULL); exit(1); } i++; printf("i is %d\n",i); return 0; }
其中,“exit()”函数如果执行成功,则返回值为0,失败则返回1.
在ubuntu界面进行编译运行,结果如下:
root@ubuntu:/home/samba/jincheng# ./execl2
This is parent,parent pid is 2788
i is 1
This is child,child pid is 2789, parent pid is 2788
root@ubuntu:/home/samba/jincheng# hello world
在上一个例程中实现了子进程调用hello文件并执行,那么是否可以使用execl函数来调用shell命令呢?
答案是是的,因为shell命令本身也是可执行文件。
举个例子,此时要调用ls这个命令,ls这个命令它的可执行文件在根目录下,在ubuntu界面使用ls /bin/ls即可看到
将上述子进程的
execl("/home/samba/jincheng/hello","hello",NULL);
改为:
execl("/bin/ls","ls","-al",NULL);
与在ubuntu界面直接使用shell命令的执行结果是一样的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。