Linux系统编程05

在代码中启动多个进程

使用system库函数启动多个进程
在这里插入图片描述
传统的进程调用就是我们在命令框里输入运行某个进程,而我们可以依靠代码,实现让一个进程取启动另一个进程
在这里插入图片描述
在进程运行过程我们使用命令ps -elf看到正在运行的有三个进程

system的调用过程
首先./system进程通过system库函数创建sh -c进程,在通过sh -c进程来启动./sleep进程,因此就有上面说的有桑格进程在运行,然后./sleep进程运行结束,跳回sh -c进程,进程在结束,跳回./system进程执行后续操作
在这里插入图片描述
fork系统调用启动多个进程
在这里插入图片描述

在执行了fork指令之后,进程就会将自己复制一份一摸一样的进程出来,里面的代码段,pc指针都相同,这两个进程一个是父进程,一个是子进程,在父进程和子进程中已执行指令和未执行执行指令都是相同的,这个时候如果我们不加以控制这两个进程就会并行的执行相同的指令。
父进程中的fork的放回值是子进程的pid,子进程中的fork返回值是0,这样我们就可以按照其返回值进行判断,让不同的进程走向不同的分支,实现不同的代码
在这里插入图片描述
我们可以看到父进程由命令行(bash分支)创建

深入fork

fork实现的底层原理
(1)fork调用的时候回拷贝一份task_struct
(2)子进程修改一些必要数据
(3)加入到就绪队列

第一步和第二步不可抢占,时间尽可能短,我们称为进程调用的上半部;第三步是可以抢占的,执行这个操作的时间可以长,我们称为下半部

系统调用是怎样实现的

用户态下不能执行所有的指令,因此cpu的使用状态分为用户态和内核态

需要调用硬件的功能或者硬件发生一些事件,此刻cpu状态就会处于内核态进行处理

硬件中断(用户态—>内核态)

fork的性能
上面那我们说过在执行fork指令之后,进程会复制一个和自己完全一样的进程出来,那么复制出来的进程和我们原来的进程是怎么使用内存空间的呢,这是一个问题,因此fork在进行复制时,其实不会给新的进程分配物理空间,当我们只进行读取操作这样对两个进程都没有影响,但是当某个进程执行写操作,需要修改进程内存中的数据,就会在复制一个修改数据之前的数据块,分配物理内存,让另外一个进程指向这个内存空间
在这里插入图片描述

fork的拷贝

在逻辑上,父子进程的用户态空间(栈,堆,数据段)是拷贝的
在代码中可以看到我们先让父进程睡眠等待子进程输出完毕以及更改数据完毕再执行父进程,但是我们再子进程中修改的数据只影响了子进程中的输出,并没有改变父进程中的数据输出,再次验证了用户态空间(栈,堆,数据段)不是共享的,是拷贝的
在这里插入图片描述

使用fork和wait手动实现system库函数
在这里插入图片描述

FILE的拷贝

printf的本质:往stdout中写入内容,遇到换行\n或者缓冲区满的时候将数据拷贝到内核文件对象中

在这里插入图片描述

内核态是拷贝还是共享

对于文件对象父进程和子进程是共享的,标准输出输入设备父子进程也都是共享的

exec(系统调用)函数族

exec将一个可执行程序加载到本能地进程的地址空间
调用exec会清空数据(栈,堆,数据段),将函数参数中的pathname加载进来,取代原来的代码段,重置PC指针

在这里插入图片描述
图中标注出来的是需要重点掌握的函数execl l是指list,是可变参数,execv v是指vector元素为指针的数组

execl参数含义:pathname指明可执行参数的路径;第二个或者更多的参数表示要调用程序所需要携带的参数,NULL值表示参数输入完毕,我们也可以看到我们执行了两个程序,但是只使用了一个进程
execl用法
在这里插入图片描述
execv用法
在这里插入图片描述
以上的方法也可以用system函数实现

使用strtok分割字符串
第一个参数是传入传出参数,只能使用字符数组,不能只用字面值
在这里插入图片描述
在这里插入图片描述

wait

在上面使用fork将进程复制为父子进程的示例中,我们可以看到我们在执行父进程程序之前我们是先对父进程使用sleep(1)让父进程睡眠1秒

在执行,那我们为什么要给父进程执行sleep(1)指令呢,如果不执行又是什么情况,下面是如果不对不父进程执行sleep(1)的输出结果。

在这里插入图片描述
看输出结果父子进程的pid没有问题,但是(1)子进程的ppid有问题;(2)以及子进程打印信息的位置有问题,现在子进程打印的位置在命令提示行之后,显示错乱。并且我们可以看到程序先打印了父进程的指令,再打印执行子进程程序,这样导致子进程执行完毕要回收资源时找不到父进程

这就涉及到进程的退出;在linux里进程退出之后其资源的回收由其父进程回收(调用wait )
在这里插入图片描述
我们可以使用wait函数让父进程在子进程销毁之后为子进程回收资源,这样进程的运行又回归正常
在这里插入图片描述
子进程未终止,父进程已终止;这样的进程我们称之为孤儿进程,他们需要重新找其他进程作为自己的父进程,一啊不能都是找1进程作为父进程

子进程终止的时候,父进程一直不调用wait,此时进程已经死亡,但是资源还没回收,这样的进程我们称之为僵尸进程

使用wait获取子进程的退出状态

可以根据下面的宏来检测是否为正常退出
在这里插入图片描述
在这里插入图片描述
可以获取我们的返回值进行放回
在这里插入图片描述

我们用9号信号杀死进程,便会打印出其时非正常退出
在这里插入图片描述

wait的缺陷:假如一个父进程有多个子进程,那么wait只能等一个子进程死

waitpid

在这里插入图片描述

options可以设置属性,可以设置的值WNOHANGWNOHANG的作用,过一段时间回来查看以下子进程是否死亡,如果死亡就为子进程回收资源,如果没死那么在过一会再来看,
如果加上WNOHANG属性,那么如果子进程终止返回0;如果进程已终止,就会回收资源
非阻塞通常配合循环使用
在这里插入图片描述

pid的值如果是-1,那么就是可以等待任何一个子进程

在这里插入图片描述

同步:事件发生的时间顺序总是确定固定的
异步:某件事件发生之后另一个事件不一定执行

进程的正常终止

(1)在main函数中调用return,可以使用echo $?查看返回值
好处:写法方便
坏处:只能退出当前函数,不能退出进程
在这里插入图片描述
(2)使用exit(number)可以在进程的任何时刻都可以退出进进程,其中number是其退出进程的返回值

并且我们可以看到printf没有换行符\n,因此打印的数据是hello存储在标准输出stdout文件流里面的,并没有存储在文件对象里面,所以exit()可以帮我么清空文件流,并且将数据显示在屏幕上

如果使用printf加上了换行符\n那么打印的数据就会存储到文件对象中

在这里插入图片描述

_exit()作用和exit()的作用是一样的,但是其不会自动清空文件缓冲区数据(标准输出stdout
_exit()_Exit()的作用是一样的

在这里插入图片描述

进程异常终止
(1)主动异常终止
在这里插入图片描述

在这里插入图片描述
6号信号,自杀信号
在这里插入图片描述

(2)另一个进程/硬件发信号终止

进程组

进程组是进程的集合,每个进程只能属于一个进程组,组ID是组长的PID,父子进程属于同一个进程组

即使组长进程终止,组ID也不变

新进程的PID不和组长ID重复

普通组员可以脱离原来的组创建新的组,但是组长不行

获取组ID和设置组ID
getpgid(pid)中的参数pid是指你要获取的进程的pid,如果参数值为0,那么返回值为本进程的父进程PID
在这里插入图片描述

通过shell启动的进程是一个新的进程组的组长

因此此进程一已经不能在重新建立新的组

但是其子进程可以创建新的进程组,并且这不会影响之前的父子关系
在这里插入图片描述
setpgid(0,0)两个参数的意义,第一个参数表示我们要修改谁的进程组ID,第二个参数表示我们要将目标进程的进程组ID改为多少

因此两个0表示要将本能进程设为新的一个进程组组长

在这里插入图片描述

在一个终端(会话)中,有至多一个前台进程组,有任意个后台进程组

使用会话session管理进程组

会话是进程组的集合,创建会话的进程我们称之为会话首进程,会话首进程必定是组长,其也是会话的第一个进程

会话可以连接一个终端,如果有终端连接会话,就会有一个专门的进程去和会话进行交互,这个进程我们称之为控制进程
如果终端关闭,所有会话内的进程会收到一个断开连接信号

获取会话ID
参数为我们我们想要获取的进程PID,如果参数为0则返回当前进程的会话ID
在这里插入图片描述
更改会话ID,实质不是区=去更改会话ID 是拿当前的的进程创建新的会话
在这里插入图片描述
在这里插入图片描述

守护进程daemon

即使是会话(终端)关闭,进程依然可以持续运行,因此守护进程是孤儿进程

守护进程一般以d结尾,例如sshd就是一个守护进程

守护进程特点:
(1)创建新的会话(将父进程终止,重新创建会话)
(2)重置掉当前工作目录pwd和文件创建掩码umask
(3)关闭所有的文件描述符(因此如果守护进程需要输出数据只能输出到日志系统,可以和日志系统进行交互)

守护进程的使用
在这里插入图片描述
日志系统末尾打印出我们的日志数据
在这里插入图片描述

日志系统
在这里插入图片描述

日志系统本质就是一个可写入文件,并且会记录其优先级priority下面就是一些优先级参数
在这里插入图片描述

进程间通信(Inter Process Communication ---- IPC)

目的打破进程之间的隔离,从而使得进程之间可以共享数据

IPC包括:(1)管道–重要;(2)共享内存;(3)信号量;(4)消息队列;(4)信号–重要

有名管道:在文件系统中存在一个管道文件

创建有名管道
在这里插入图片描述
删除一个管道
在这里插入图片描述
改变名字或者位置和mv 实现的功能一样
在这里插入图片描述
创建硬链接
在这里插入图片描述

匿名(无名)管道:在文件系统中不存在管道文件,只用于父子进程之间

管道用法popen(库函数)
在这里插入图片描述

进程执行popen库函数之后会创建一个子进程,父子进程之间用管道连接在一起,管道两端是文件流

popen库函数的第二个参数type的值可以为wrw表示父进程入数据到管道文件流FILE内,子进程把自己的stdin重定向为管道;r表示父进程取管道文件流FILE,子进程把自己的stdout重定向为管道;

读模式
在这里插入图片描述

写模式
在这里插入图片描述

pipe系统调用

pipe可以在一个进程的内核态创建两个文件对象,因此需要有两个文件描述符去指向这两个文件对象,这两个文件对象分别对应着管道的读端和写端,因此我们需要有两个整形分别为pipefd[0] pipefd[1]

这也就是下面的pipe函数参数为pipefd[2],表示参数应该是一个长度为2int数组

在这里插入图片描述

此进程通过pipe系统调用向自己的写管道文件中写入数据,此时写入的数据就会在读取管道中就可以实现读取

那么这样子的进程自己输入自己输出有什么用呢,这看上去没什么用,但是假如我们使用pipe之后再加上使用fork操作复制一个子进程,此时文件对象是共享的,因此子进程也会复制相同的文件描述符,指向这两个读写文件对象,此时父子进程可以通过pipe进行通信(半双工通信)

在这里插入图片描述

共享内存

让不同进程的虚拟内存页对应同一个物理页框

共享内存是效率最高的进程通信IPC
库文件经常使用共享内存,使用lsof命令可以看到加载到内存的库文件

System V的共享内存机制

ftok将文件名转换为一个实现进程间通信找到共享内存的key
在这里插入图片描述
在这里插入图片描述

我们就可以通过上面获取到的key使用shmget去构建一个共享内存

在这里插入图片描述

我们可以使用ipcs指令去查看我们创建的共享内存

在这里插入图片描述

尽管上面我们创建好了共享内存,但是我们的进程是无法使用的还只是虚拟内存,我们还需要将共享内存加载到我们的物理内存中,才能让我们使用

shmat函数的第一个参数shmid就是上面shmget函数的返回值,第二个参数我们填写NULL那么就会自动分配虚拟内存空间进行映射,最后一个参数我们填上0,代表什么事情都不用做

shmdt函数就是对共享空间进行回收,回收的是虚拟内存

在这里插入图片描述

共享内存的用法,当我们创建共享内存之后,共享内存块内的初始值会置为0
不同进程只要通过相同的key就可以对共享空间进行读写

在这里插入图片描述

私有共享内存

如果我们将key的值改为IPC_PRIVATE(这个宏的值为0),则会创建一个私有共享内存,此内存只能父子进程才能进行访问,只能实现父子进程之间的通信

在这里插入图片描述

如果我们不让子进程睡眠1秒,那么有可能打印不出任何消息因为子进程和父进程是同步进行的,可能父进程还没有向共享内存里写数据,子进程就已经执行输出指令

在这里插入图片描述

在上面的代码中我们可以看到我们将共享内存指针定义为int类型,然后再让父子进程向共享你内存中写入数据(执行假发操作),但是输出的结果却小于我们预测的结果(2000000)
这是由于竞争条件引起的
两个进程并发的访问共享资源,虽然上面我们只是执行了一句简单的p[0]++但是当其转换为汇编代码就会编程分为三步进行执行
(1)将p[0]数据mov写到寄存器
(2)寄存器将数据进行加法操作
(3)寄存器 将数据mov写回p[0]
在这三个步骤执行的过程中,很有可能发生时钟中断,因此就会发生数据丢失,导致在寄存器里已经执行加一操作的数据没有写回p[0],这就是竞争条件
在多个进程去同时读取一个共享内存空间时,就会发生资源丢失

shmctl

对共享内存进行管理
在这里插入图片描述

参数cmd取值
在这里插入图片描述
IPC_STAT获取状态
IPC_SET修改状态
IPC_RMID删除
这三个变量能够让函数执行不同指令,该函数违背了“单一职责”原则

参数shmid_ds *buf的结构体
在这里插入图片描述
shm_perm权限结构体
在这里插入图片描述

shm_segsz大小
shm_atime上次连接时间
shm_dtime上次结束连接的时间
shm_ctime修改的时间
shm_cpid创建者的pid
shm_lpid上次连接和解除连接的pid
shm_nattch当前共享内存有多少进程连接

设置和修改属性,修改属性之前必须先IPC_STAT

在这里插入图片描述

删除共享内存IPC_RMID

这个操作并不会真正的删除共享内存,如果共享内存还有其他进程正在连接,那么就会等到其他进程全部断开连接,才会真正删除共享内存,标记共享内存将要删除

最右边是执行期间在sleep时打印的,期间因为进程在睡眠,因此末尾只有一个dest的删除标记,但是由于此共享内存还有进程在连接,因此不会删除这个共享内存
中间的是在执行完毕之后打印的

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/168068.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot基于会员制医疗预约服务管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot基于会员制医疗预约服务管理系统演示 摘要 会员制医疗预约服务管理信息系统是针对会员制医疗预约服务管理方面必不可少的一个部分。在会员制医疗预约服务管理的整个过程中,会员制医疗预约服务管理系统担负着最重要的角色。为满足如今日益复杂的管理需…

VPN(虚拟专用网)攻略大全,你一定会用到!

你们好,我的网工朋友。 今天想和你聊聊VPN。 在VPN出现之前,企业分支之间的数据传输只能依靠现有物理网络(例如Internet)。 但由于Internet中存在多种不安全因素,报文容易被网络中的黑客窃取或篡改,最终…

​iOS上架App Store的全攻略

第一步:申请开发者账号 在开始将应用上架到App Store之前,你需要申请一个开发者账号。 1.1 打开苹果开发者中心网站:Apple Developer 1.2 使用Apple ID和密码登录(如果没有账号则需要注册),要确保使用与公…

Biotech - 环状 mRNA 的 LNP 递送系统 与 成环框架

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/133992971 环状 RNA(或 circRNA )是一种单链 RNA,与线性 RNA 不同,形成一个共价闭合的连续环。在环…

短视频矩阵系统源码---开发

一、智能剪辑、矩阵分发、无人直播、爆款文案于一体独立应用开发 抖去推----主要针对本地生活的----移动端(小程序软件系统,目前是全国源头独立开发),开发功能大拆解分享,功能大拆解: 7大模型剪辑法(数学阶乘&#x…

系统性认知网络安全

前言:本文旨在介绍网络安全相关基础知识体系和框架 目录 一.信息安全概述 信息安全研究内容及关系 信息安全的基本要求 保密性Confidentiality: 完整性Integrity: 可用性Availability: 二.信息安全的发展 20世纪60年代&…

JavaScript基础知识16——分支语句

哈喽,大家好,我是雷工。 今天学习JavaScript基础知识的分支语句,以下为学习笔记。 1、程序三大流程控制语句 ○写几句就从上往下执行几句,这种叫做顺序结构; ○有时要根据条件选择执行代码,这种叫分支结构…

【DM8连接】DBeaver连接DM8

dm.jdbc.driver.DmDriver jdbc:dm://{host}:{port} 5236 DmJdbcDriver18.jar

linux elf relationship between data structures involved in symbol resolution

When a program imports a certain function or variable, the linker will include a string with the function or variable’s name in the .dynstr section. A symbol (Elf Sym) that refers to the function or variable’s name in the .dynsym section, and a relocati…

QT的Qporcess功能的使用

具体实现代码如下&#xff1a; #include <QProgressBar>//必须要包含的头文件 #include <QProcess>// 创建一个QProgressBar对象QProgressBar *progressBar new QProgressBar(this);QProcess *proces;process_shownew process;// 设置进度条的最小值和最大值prog…

极智嘉(Geek+)柔性货箱到人拣选方案,助力Starlinks实现高效运营

近些年&#xff0c;电商业务席卷全球&#xff0c;一众企业蓬勃发展。比如沙特阿拉伯先进的物流与供应链解决方案供应商Starlinks的电子商务的销售额从6%增长到了23%。为满足日益增长的国际电商业务需求&#xff0c;以及订单交付时效性更高的要求&#xff0c;Starlinks与全球仓储…

闭包(函数)

把内部函数通过return扔出去 必要条件

laravel 中 npm run 同时执行多个命令

在使用laravel 启动项目时 经常需要同时运行两个命令。 1.前端既是 npm run dev 2.后端php则是 php artisan serve 可以安装 使用 concurrently 进行并行启动 concurrently - npm npm install concurrently --save 之后修改 package.json 在 scripts 中增加 &#xff08;多条…

--initialize specified but the data directory has files in it. Aborting. 问题解决

当电脑输入这条命令以试图初始化数据库的时候&#xff0c;出现这样的错误。 2023-10-23T09:04:21.258180Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more deta…

CanIf Transmit Buffering 机制说明

目录 前言未使能场景的报文发送机制使能场景的报文发送机制如何配置前言 在AUTOSAR CanIf 中,提供了发送的Buffering机制 。对于一个报文来说,Buffering起始于CanIf_Transmit,结束于CanIf_TxConfirmation 。 主要应用与一个MailBox 发送多个报文的场景,用于解决因为硬件发…

8个卓越的矢量图插画资源网站

在插画设计中&#xff0c;如何寻找能够自由缩放的矢量图插画素材&#xff0c;一直是我们设计的难点。 下面&#xff0c;为大家推荐8个矢量图插画素材网站&#xff0c;希望能够满足同学们的设计需求。 1&#xff1a;即时设计 即时设计是可云端编辑的专业级 UI 设计工具&#…

Python学习笔记——文件操作、异常捕获、模块

食用说明&#xff1a;本笔记适用于有一定编程基础的伙伴们。希望有助于各位&#xff01; 文件读取 文件操作比较常见&#xff0c;通常我们需要对文件进行读取操作&#xff1a; f1 open(assets/Files/测试文件.txt, r, encodingUTF-8)# 辨别是否可读print(f1.readable())# 读取…

打包Qt程序,自动添加依赖的库和文件(详细步骤)

1、打开对应版本的命令面板&#xff0c;选择即可&#xff1a; 一般安装qt的时候&#xff0c;都会自带的 2、进入到编译成功的程序所在的目录&#xff1a; 输入&#xff1a;windeployqt XXX.exe&#xff08;实际的程序名字&#xff09; 这样这个程序所依赖的库都会自动添加进来…

Mysql高级——锁(2)

4. 锁的内存结构 InnoDB 存储引擎中的锁结构如下&#xff1a; 锁所在的事务信息&#xff1a; 不论是表锁还是行锁&#xff0c;都是在事务执行过程中生成的&#xff0c;哪个事务生成了这个锁结构&#xff0c;这里就记录这个事务的信息。 此锁所在的事务信息在内存结构中只是一…

“中国版Zara”拉夏贝尔:从辉煌到破产清算

文/ 大力财经 拉夏贝尔的破产清算&#xff0c;让人不禁惋惜。这个曾经被誉为“中国版Zara”的女装品牌&#xff0c;在全国拥有超过2000家门店&#xff0c;一度是年轻人的追捧对象。 然而&#xff0c;由于市场竞争激烈、品牌定位模糊、库存积压严重等问题&#xff0c;拉夏贝尔…