IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 嵌入式学习之linux系统编程---14 进程控制(fork、execl命令) -> 正文阅读

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

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 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资源,哪个先抢到,哪个就先执行。
在这里插入图片描述

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 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

在这里插入图片描述

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命令的执行结果是一样的。
在这里插入图片描述

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 00:32:30  更:2022-04-01 00:34:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 23:59:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码