Linux多进程/线程编程之【fork()和exec()】

2023-05-16

目录

一、fork系统调用创建子进程

1.1 为什么要创建子进程

1.2 fork系统调用的内部原理

1.3 关于子进程

1.4 线程和fork

二、exec族函数及实战

2.1 为什么需要exec族函数

2.2 exec族的6个函数介绍

2.3 exec()函数返回 

2.4 exec编程实战

2.5 线程和exec()

三、总结

3.1 多进程

3.2 多线程


一、fork系统调用创建子进程

1.1 为什么要创建子进程

(1)每一次程序的运行都需要一个进程,需要创建多个进程实现宏观上的并行。

1.2 fork系统调用的内部原理

(1)进程的分裂生长模式:如果操作系统需要一个新进程来运行一个程序,那么操作系统就会用一个现有的进程来复制生成一个新进程,老进程叫父进程,新进程叫子进程。

(2)fork系统调用调用一次会返回两次,使用fork后要用if判断返回值,返回值等于0的就是子进程,返回值大于0的就是父进程。

(3)fork的返回值在父进程中等于本次创建的子进程的进程ID,在子进程中等于0;

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
    pid_t  p1 = -1;
    p1 = fork();        // 返回2次
    if (p1 == 0)
    {
        // 这里一定是子进程
        // 先sleep一下让父进程先运行,先死
        sleep(1);
        printf("子进程, pid = %d.\n", getpid());       
        printf("hello world.\n");
        printf("子进程, 父进程ID = %d.\n", getppid());
    }
    if (p1 > 0)
    {
        // 这里一定是父进程
        printf("父进程, pid = %d.\n", getpid());
        printf("父进程, p1 = %d.\n", p1);
    }
    if (p1 < 0)
    {
        // 这里一定是fork出错了
    }
    // 在这里所做的操作会被执行两遍
    //printf("hello world, pid = %d.\n", getpid());
    return 0;
}

1.3 关于子进程

子进程由父进程复制而来,有自己独立的PCB,被内核同等调度。

1.4 线程和fork

当多线程进程调用fork()时,仅会将发起调用的线程复制到子进程中。子进程中该线程的线程ID与父进程中发起fork()调用线程的线程ID相一致(后面会有例子说明)。其他线程均在子进程中消失,也不会为这些线程调用清理函数以及针对线程特有数据的解构函数。

这将导致如下一些问题:(线程中fork()会导致子进程的内存泄漏)

虽然只将发起调用的线程复制到子进程中,但全局变量的状态以及所有的Pthreads对象(如互斥量、条件变量等)都会在子进程中得以保留。(因为在父进程中为这些Pthreads对象分配了内存,而子进程则获得了该内存的一份拷贝。)这会导致很棘手的问题。例如,假设在调用fork()时,另一线程已然锁定了某一互斥量,且对某一全局数据结构的更新也做到了一半。此时,子进程中的该线程无法解锁这一互斥量(因为其并非该互斥量的属主),如果试图获取这一互斥量,线程会遭阻塞。此外,子进程中的全局数据结构拷贝可能也处于不一致状态,因为对其进行更新的线程在执行到一半时消失了。

因为并未执行清理函数和针对线程特有数据的解构函数,多线程程序的fork()调用会导致子进程的内存泄露。另外,子进程的线程很可能无法访问(父进程中)由其他线程所创建的线程特有数据项,因为(子进程)没有相应的引用指针。

二、exec族函数及实战

需要注意的是,exec并不是一个函数,其实只是一组函数的统称,包括六个函数:

2.1 为什么需要exec族函数

#include <unistd.h>  
  
int execl(const char *path, const char *arg, ...);  
  
int execlp(const char *file, const char *arg, ...);  
  
int execle(const char *path, const char *arg, ..., char *const envp[]);  
  
int execv(const char *path, char *const argv[]);  
  
int execvp(const char *file, char *const argv[]);  
  
int execve(const char *path, char *const argv[], char *const envp[]);  

(1)fork系统调用是为了执行新的程序,但需要直接在子进程的if里写入新的程序代码,这样不够灵活,譬如说我们如果想要执行ls -al 就不可以了(没有源代码,只有可执行程序)。

(2)使用exec族函数可以运行新的可执行程序,即把一个编译好的可执行程序直接加载运行。

(3)一个进程一旦调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。(不过exec类函数中有的还允许继承环境变量之类的信息。) 那么如果我的程序想启动另一程序的执行但自己仍想继续运行的话,怎么办呢?那就是结合fork与exec的使用

2.2 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)execl和execv

(l后缀:命令参数部分必须以","相隔,且最后一个参数必须为NULL)

(v后缀: 命令参数部分必须是1个以NULL结尾的字符串指针数组的头部指针)

这两个是最基本的exec,都可以用来加载运行可执行程序,区别是传参格式不同。

execl是把多个参数(多个字符串,必须以NULL结尾)依次排列(l就是list的缩写);

而execv是把多个参数事先放入一个字符串数组NULL结尾),再把这个字符串数组传给execv函数。

(2)execlp和execvp:(p后缀,参数执行文件部分可以不带路径,exec函数会去$PATH寻找)

这两个函数较上面两个来说,上面两个函数必须指定可执行程序的全路径,而这两个加了p的函数可以只指定file即文件名,函数会到环境变量PATH所指定的目录下去找。

(3)execle和execvpe:(e后缀,参数必须带环境变量部分)

main函数的原型其实不止是int main(int argc, char argv),还可以是int main(int argc, argv, char **env),第三个参数是一个字符串数组,内容是环境变量。

如果用户在执行execle和execvpe的时候没有传递第三个参数,则程序会默认从父进程那里继承一份环境变量(默认的,最早来源于OS);如果传递了第三个参数则替代了默认的环境变量。

2.3 exec()函数返回 

exec函数会取代执行它的进程,  也就是说, 一旦exec函数执行成功, 它就不会返回了, 进程结束

与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,颇有些神似"三十六计"中的"金蝉脱壳"。看上去还是旧的躯壳,却已经注入了新的灵魂。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。

通常exec会放在fork() 函数的子进程部分, 来替代子进程执行啦, 执行成功后子程序就会消失,  但是执行失败的话, 必须用exit()函数来让子进程退出!

2.4 exec编程实战

  • exec实战1

(1)使用execl运行ls -l -a

execl("/bin/ls", "ls", "-l", "-a", NULL);

(2)使用execv运行ls

char * const arg[] = {"ls", "-l", "-a", NULL};
​
execv("/bin/ls", arg);

(3)使用execl运行自己写的程序

execl("hello", "aaa", "bbb", NULL);

(4)使用execv运行自己写的程序

char * const arg[] = {"aaa", "bbb", NULL};
​
execv("hello", arg);
  • exec实战2

(1)execlp

execlp("ls", "ls", "-l", "-a", NULL);   

(2)execle

char * const envp[] = {"AA=aaaa", "XX=abcd", NULL};
execle("hello", "hello", "-l", "-a", NULL, envp);

2.5 线程和exec()

只要有任一线程调用了exec()系列函数之一时,调用程序将被完全替换。除了调用exec()的线程之外,其他所有线程都将立即消失。没有任何线程会针对线程特有数据执行结构函数,也不会调用清理函数。该进程的所有互斥量(为进程私有)和属于进程的条件变量都会消失。调用exec()之后,调用线程的线程ID是不确定的。

三、总结

3.1 多进程

在多进程中fork()和exec()通常是结合起来一起使用:

fork()创建一个子进程,且fork()后立即接上exec()函数,在fork()创建的子进程中,exec函数会取代执行相关脚本文件,执行程序,然后退出,不会返回

3.2 多线程

在多线程中,通常不建议使用fork()和exec():

fork()函数的调用会导致在子进程中除调用线程外的其它线程全都终止执行并消失,因此在多线程的情况下会导致死锁和内存泄露的情况。在进行多线程编程的时候尽量避免fork()的调用,同时在程序在进入main函数之前应避免创建线程,因为这会影响到全局对象的安全初始化。线程不应该被强行终止,因为这样它就没有机会调用清理函数来做相应的操作,同时也就没有机会来释放已被锁住的锁,如果另一线程对未被解锁的锁进行加锁,那么将会立即发生死锁,从而导致程序无法正常运行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux多进程/线程编程之【fork()和exec()】 的相关文章

  • 海康嵌入式软件面试经验(已拿offer)

    本科双非 xff0c 计算机类专业学渣 xff0c 0实习经历 xff0c 复习考研的时候学了一遍408 xff0c 强化阶段学的心态崩了就边复习边投简历 xff0c 之前投了好几次体面厂的测试岗都没进面试 xff0c 投开发岗反而进了 7
  • 前端浏览器渲染原理及优化

    文章目录 一 浏览器组成1 对浏览器内核的理解2 浏览器的主要组成部分 二 浏览器渲染原理1 浏览器的渲染过程步骤一 xff1a 步骤二 xff1a 步骤三 xff1a 步骤四 xff1a 步骤五 xff1a 2 相关概念 重排 更新元素的
  • Docker+github actions部署前端项目

    Docker 43 github actions部署前端项目 文章目录 Docker 43 github actions部署前端项目前言1 Docker相关文件配置2 创建自己的dokcer hub仓库3 yml文件配置 前言 在进行本篇实
  • pytorch可视化之:可视化网络结构+CNN可视化+TensorBorad使用

    Pytorch学习第五部分 xff1a pytorch可视化 Let 39 s go 一 可视化网络结构1 1 Resnet18结构1 2 使用torchinfo可视化网络结构1 2 1 torchinfo安装1 2 2 torchinfo
  • spring中用到的设计模式及应用场景

    spring中用到的设计模式及应用场景 1 工厂模式 xff1a 在Beanfactory和applicationContext创建中都用到了 2 单例模式 xff1a Bean默认就是单例模式 单例模式只允许创建一个对象 xff0c 获取
  • python架构之Django学习------流程总结

    python架构之Django学习 一 二 三 开发流程 xff1a step1 创建虚拟环境 mkvirtrualenv step2 安装django pip install django step3 创建项目 python manage
  • PDU 发送短信3

    T指令收发短信主要有两种模式 xff1a Text模式和PDU xff08 Protocol Data Unit xff0c 协议数据单元 xff09 模式 使用Text模式收发短信代码简单 xff0c 很容易实现 xff0c 最大缺点不支
  • Linux驱动---休眠与唤醒

    Linux 休眠与唤醒 文章目录 Linux 休眠与唤醒前言一 休眠 唤醒 机制二 重要的函数及其数据结构wait内核函数唤醒函数 三 驱动编程步骤附录 xff08 源码 xff09 前言 当应用程序必须等待某个时间发生 xff0c 比如必
  • 序列化Writable接口

    基本序列化类型往往不能满足所有需求 xff0c 比如在Hadoop框架内部传递一个自定义bean对象 xff0c 那么该对象就需要实现Writable序列化接口 实现Writable序列化步骤如下 xff08 1 xff09 必须实现Wri
  • MapReduce读取数据

    1 InputFormat 运行MapReduce程序时 xff0c 输入的文件格式包括 基于行的日志文件 二进制格式文件 数据库表等 那么 xff0c 针对不同的数据类型 xff0c MapReduce是如何读取这些数据的呢 InputF
  • Git的安装与配置,VScode如何连接Gitee?

    什么是gitee 要学gitee 你首先得知道gitee是什么 而且你得知道它的好处 首先它是国内最大的代码托管平台 国外GitHub的弟弟 然后 它能帮你 管理 昨天和今天 改动的文件 xff0c 还给你做 备份 xff1b 它能管理让你
  • 下载并使用Maven创建运行项目(配置Maven、tomcat、含idea实例)【精细教程】

    前言 xff08 重点 xff01 xff01 xff09 xff1a 很多人在安装好maven后 xff0c 遇到idea与maven版本不匹配的问题 xff0c 首先要根据自己的idea版本来选择maven版本 xff01 IDEA 2
  • 将本地项目commit到gitee上

    在新建的项目路径中 xff0c shift 43 右键 xff0c 打开powershell窗口 查看状态 xff1a git status 再执行 xff1a git add git commit m 34 add files 34 再次
  • docker搭建私有仓库

    一 宿主机安装 1 extras源中下载安装distributon包 编写源 extras name 61 extra baseurl 61 https mirrors aliyun com centos vault 7 9 2009 ex
  • HBase-API

    目录 引入的依赖 创建连接 命名空间 表 引入的依赖 lt dependencies gt lt dependency gt lt groupId gt org apache hbase lt groupId gt lt artifactI
  • 【嵌入式算法】学习笔记(一):数字滤波算法

    文章目录 摘要一 数字滤波简介二 常用数字滤波算法1 限幅滤波2 中值滤波3 算术平均滤波4 去极值平均滤波5 滑动平均滤波6 滑动加权滤波7 一阶滞后滤波 三 数字滤波小结 摘要 最近在做直流电机的毕设中 xff0c 由于需要采集转速 x
  • python架构之Django学习------mysql使用

    学习环境 xff1a ubuntu16 0 4 一 安装python包 pip install mysql python 二 使用mysql mysql uroot p show databases drop database test1
  • C语言经典面试题10道(五)

    41 什么是预编译 xff0c 何时需要预编译 xff1f 答案 xff1a xff11 总是使用不经常改动的大型代码体 xff12 程序由多个模块组成 xff0c 所有模块都使用一组标准的包含文件和相同的编译选项 在这种情况下 xff0c
  • ROS环境下的串口通讯

    目录 1 前言 2 内容 2 1 准备工作 2 1 1 连接外部USB设备 2 1 2 串口调试工具的下载 2 1 3 serial库的安装 2 2 代码部分 2 2 1 编写发布节点 2 2 2 编写发布节点 2 2 3 编辑checkl
  • 【线性控制理论】状态观测器—开环形式的状态观测器

    文章目录 前言一 开环形式的状态观测器 前言 在线性系统的各种综合问题中状态反馈展现了其优越性 xff0c 不管是系统的极点配置 xff0c 镇定以及解耦控制 xff0c 都有赖于引入适当的状态反馈才能实现 但是在状态反馈时 xff0c 我

随机推荐

  • 【状态观测器】全维状态观测器

    文章目录 前言一 全维状态观测器 前言 本文主要提到的是闭环形式状态观测器中的全维状态观测器 xff0c 建议有时间可以阅读上一篇开环形式的状态观测器 篇幅不长 以理解状态观测器 一 全维状态观测器 全维状态观测器是闭环状态观测器的一种 还
  • Linux 安装zsh和zsh的配置

    1 在安装之前 xff0c 需要了解一些自己的shell是什么 命令 xff1a echo SHELL 这里是bash shell环境 2 安装zsh 输入命令 xff1a sudo pacman S zsh 直接安装zsh 3 安装zsh
  • Linux 对整个系统备份和还原

    对系统进行备份非常的重要 xff0c 如果有一天 xff0c 系统崩溃了 xff0c 可以重装系统 xff0c 但是重装系统后又需要进行相关的配置 xff0c 这会显得非常的麻烦 xff0c 又会浪费很多的时间 备份的方式 xff1a 分两
  • 计算机网络地址划分及子网掩码计算

    一 点分十进制记法 8 位的二进制数 转为 十进制数 注意 xff1a 1 与有分类的ip地址的区别 这里的 n 不是固定的数 是可以在0 32位之间任意的 xff0c 所以 CIDR 斜线记法 xff1a IP地址后面加上 斜线后面的是网
  • axios简介

    Axios 对原生的 AJAX 进行封装 xff0c 简化书写 get请求如下 xff1a xff08 get请求地址栏传参 xff09 axios method 34 get 34 url 34 http localhost 8080 a
  • 安装OpenCV、cython、numpy和h5py

    安装OpenCV xff1a 一 系统烧录 Raspberry Pi Imager 烧录步骤 xff1a 软件下载地址 xff1a Raspberry Pi OS Raspberry Pi xff08 建议提前准备一张容量在8G以上的SD卡
  • 优秀!他历时三个月终于拿到阿里offer,在这里分享一下阿里的社招面经!

    前言 这也是我第二次进入三面了 xff0c 也不知道这次能不能进呢 xff1f 球球阿里爸爸了 xff0c 许愿一个 hr 面可以吗 xff1f wwwww 本人双非本科大三 xff0c 基础不强 xff0c 有一个简单的秒杀项目 xff0
  • wireshark-----过滤使用方法

    1 ip过滤 ip addr 61 61 10 239 4 160
  • MySQL数据库安装步骤及报错1251解决方法

    MySQL数据库安装 MySQL是一种关系数据库管理系统 xff0c 所使用的 SQL 语言是用于访问数据库的最常用的标准化语言 xff0c 其特点为体积小 速度快 总体拥有成本低 xff0c 尤其是开放源码这一特点 xff0c 在 Web
  • Docker 安装问题

    Docker 安装问题 出现了Failed to start docker service Unit docker service not found 这个图在图上的链接处拿的 xff0c 我自己之前出现的错误图找不到了 这个错误 xff0
  • vscode配置ros开发环境

    前言 xff1a 其实有两种方法来配置vscode里的ros环境 xff0c 第一种就是先通过终端创建工作空间 xff0c 并编译后然后选择vscode打开catkin ws xff0c 然后在vscode中配置ros的编译环境 xff1b
  • 判断IP地址是否在同一个网段

    一 什么是子网掩码 xff1f 在了解ip地址的网段之前 xff0c 我们先来了解子网掩码 xff0c 很多对网络了解不深的朋友都对子网掩码有些迷惑 xff0c 不了解它是用来干什么的 xff1f 子网掩码不能单独存在 xff0c 它必须结
  • 理解MySQL七种连接

    如上图是MySQL的七种连接 由于MySQ对于外连接支持SQL99语法 xff0c 我们就以JOIN ON举例 表t dept 表t emp 1 内连接 xff1a A表 xff0c B表交叉的部分 SELECT FROM t dept d
  • IP地址的分类和规划

    每日分享 xff1a 你拼命奔跑的样子 xff0c 终究会在风中留下痕迹 xff01 文章目录 一 IP地址的格式二 私有IP地址三 IP地址分类 xff1a 四 子网掩码五 IP地址的规划 一 IP地址的格式 1 主机唯一的标识 xff0
  • QT5+TCP/IP多线程传输图片

    先上实现结果 一 概述 QT中设计TCP IP通信主要使用QTCPServer和QTCPSocket两个类 xff0c 功能分为服务器端和客户端 xff0c 服务器端负责接收图片 xff0c 客户端发送图片 多线程设计主要有两种方法 xff
  • ubuntu建立新用户

    1 新建testuser用户 sudo adduser testuser 2 设置root密码 sudo passwd root 3 更改sudoers编辑权限 sudo chmod u 43 w etc sudoers 4 给testus
  • Error: Flash Download failed - “Cortex-M3“错误解决办法

    在使用STM32F103的时候 xff0c 使用DAP仿真器下载程序 xff0c 出现下载不了的情况 xff0c 错误信息如下 xff1a 输出框里打印信息如下 xff1a No Algorithm found for 08000000H
  • 浏览器自定义滚动条样式

    当一段文本过长 xff0c 使用overflow auto属性后 xff0c 这段文本所在区域将会出现滚动条 有时候 xff0c 我们需要自定义浏览器的滚动条样式 xff0c 可以使用css3的scrollbar thumb属性来实现 首先
  • linux rpm安装讲解

    1 安装rpm安装包 rpm ivh rpm 2 删除rpm安装包 rpm evv rpm 注意 xff1a 使用 e不能完全删除
  • Linux多进程/线程编程之【fork()和exec()】

    目录 一 fork系统调用创建子进程 1 1 为什么要创建子进程 1 2 fork系统调用的内部原理 1 3 关于子进程 1 4 线程和fork 二 exec族函数及实战 2 1 为什么需要exec族函数 2 2 exec族的6个函数介绍