当前位置:   article > 正文

嵌入式学习之linux系统编程---14 进程控制(fork、execl命令)_fork execl

fork execl

1、fork函数—创建进程的函数

fork函数:
头文件:#include <unistd.h>
函数原型:pid_t fork(void)
  • 1
  • 2
  • 3

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 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());
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在该例程中,有一点需要说明:
想要获取进程的pid号,在C语言中使用的是getpid()函数以及getppid()函数

在这里插入图片描述

在ubuntu界面的编译以及运行结果如下图所示:
在这里插入图片描述
由运行结果可以看出,在ubuntu界面编译运行之后,会将父进程以及子进程的if语句满足条件后的结果均执行出来。
即:既打印了

This is parent,parent pid is 2479
  • 1

又打印了

This is child,child pid is 2480, parent pid is 2479
  • 1

为什么呢?
在这里插入图片描述
我们可以将父进程执行fork函数之后创建的子进程看做是父进程的拷贝,这样的话,父进程以及子进程在地址空间里面的内容都一样,代码也一样。执行父进程的过程中,会自动选择执行下面的代码:

	if(pid >0){
		printf("This is parent,parent pid is %d\n",getpid());
	}
  • 1
  • 2
  • 3

而子进程会选择执行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[]);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

在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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

其中,“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
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

5、execl函数调用shell命令

在上一个例程中实现了子进程调用hello文件并执行,那么是否可以使用execl函数来调用shell命令呢?
答案是是的,因为shell命令本身也是可执行文件。

举个例子,此时要调用ls这个命令,ls这个命令它的可执行文件在根目录下,在ubuntu界面使用ls /bin/ls即可看到
在这里插入图片描述
将上述子进程的

execl("/home/samba/jincheng/hello","hello",NULL);
  • 1

改为:

 execl("/bin/ls","ls","-al",NULL);
  • 1

在这里插入图片描述
与在ubuntu界面直接使用shell命令的执行结果是一样的。
在这里插入图片描述

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/210497
推荐阅读
相关标签
  

闽ICP备14008679号