概述
1、fork函数—创建进程的函数
fork函数:
头文件:#include <unistd.h>
函数原型:pid_t fork(void)
fork函数的返回值:
fork函数的返回值有三种,(1)创建失败,则fork返回一个负值;(2)若创建成功,则在父进程中,fork返回新创建的子进程的pid号;同时,在子进程中,fork函数返回0值。
2、fork函数例程
要求:让进程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 errorn");
return -1;
}
//创建进程成功的话,返回的是子进程的pid号
if(pid >0){
printf("This is parent,parent pid is %dn",getpid());
}
//创建成功的话,子进程会返回0值
if(pid == 0){
printf("This is child,child pid is %d, parent pid is %dn",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 %dn",getpid());
}
而子进程会选择执行pid==0的情况时候的代码,所以在ubuntu界面的执行结果是打印了两句。
注意:子进程的数据空间、堆栈空间都会从父进程中拷贝,但是它们的pid号是不一样的,所以可以通过pid号来区分父子进程;也可以通过fork函数的返回值来区分父子进程。
思考:
(1)子进程创建成功以后,它的代码的执行位置在哪里呢?
父进程一定是从程序的开头开始执行,一直到程序结束。而子进程是从fork函数开始执行的。
(2)父子进程的执行顺序是什么嘞?
观察fork函数例程的执行结果,可以看到是父进程先执行的,那么一定是每次都先执行父进程么?不是这样的,父子进程的执行顺序是不一定的,因为父子进程的执行也需要抢cpu资源,哪个先抢到,哪个就先执行。
3、exec函数族
在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函数使得子进程重生。
4、execl函数示例
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 errorn");
return -1;
}
//创建进程成功的话,返回的是子进程的pid号
if(pid >0){
printf("This is parent,parent pid is %dn",getpid());
}
//创建成功的话,子进程会返回0值
if(pid == 0){
printf("This is child,child pid is %d, parent pid is %dn",getpid(),getppid());
}
i++;
printf("i is %dn",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 errorn");
return -1;
}
//创建进程成功的话,返回的是子进程的pid号
if(pid >0){
printf("This is parent,parent pid is %dn",getpid());
}
//创建成功的话,子进程会返回0值
if(pid == 0){
printf("This is child,child pid is %d, parent pid is %dn",getpid(),getppid());
execl("/home/samba/jincheng/hello","hello",NULL);
exit(1);
}
i++;
printf("i is %dn",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
5、execl函数调用shell命令
在上一个例程中实现了子进程调用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命令的执行结果是一样的。
最后
以上就是畅快砖头为你收集整理的嵌入式学习之linux系统编程---14 进程控制(fork、execl命令)的全部内容,希望文章能够帮你解决嵌入式学习之linux系统编程---14 进程控制(fork、execl命令)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复