Linux系统编程06

软件互斥

之前我们提到过让两个进程同步对一块共享内存进行去读,会产生竞争,从而没有得到我们想要的结果,下面我们采用阻塞进程,进程等待的方法用软件实现两个进程对同一块共享内存的访问,这样保证了每次每个进程都会在执行完操作才会被中断,但是这样的软件互斥只能保证计算机在单核的前提下才能生效,如果计算机时多核时,软件互斥就不行了,就得使用硬件互斥

在这里插入图片描述

硬件互斥

信号量(system V)

信号连那个就是一个整数,用来描述资源的个数

计数信号量
二元信号量,资源要么0,要么1(p操作—测试并加锁【不可分隔,原语】,先检查信号量的值,如果大于零信号量减一,如果信号量的值小于零则等待;v操作–解锁,对信号量加一)

临界区:使用p,v操作包围起来的代码段,临界区越小越好,但是一定要把共享资源的访问给保护好

利用信号量保护共享资源(不太重要,工作中用不到)

system V 信号量是一个信号量集合(整数数组)
在这里插入图片描述

创建一个信号量

在这里插入图片描述

设置初始资源值

有可变参数
在这里插入图片描述
根据第三个参数的不同,后续参数也会有不同,下面时后续可变参数的类型集合
在这里插入图片描述
如果cmdSETVAL,那么semnum的值为数组下标,后续的参数就时int类型的val或者上面union中的其中一个
如果cmdGETVAL,后续就不需要添加参数
在这里插入图片描述

设置PV操作

p测试并加锁 ,sem <= 0 阻塞,sem > 0 ,sem--;
v解锁 sem++;

实现pv操作有两个阶段,分别时定义pv操作和调用pv操作

定义pv操作
首先我们定义好pv操作,然后再来调用semop函数,
调用semop函数会拿到sembuf的数值,
(1)如果sem_op > 0资源加上sem_op;
(2)如果sem_op < 0且当前资源+sem_op >=0就会继续运行;
(3)若sem_op < 0且当前资源 +sem_op < 0阻塞到资源变化
在这里插入图片描述

struct sembuf
在这里插入图片描述
sem_num:下标
sem_op:正数增加资源,负数减少资源
sem_flg:目前先填SEM_UNDO 其作用防止一个进程在加锁的状态下终止而发生的死锁
在这里插入图片描述

更高精度的时间统计

使用微妙进行统计,第二个参数为时区,因为我们只需要相对时间,因此加上NULL就可以
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
每次P,V操作大概是600纳秒,时间比较长,访问一次cashe一般在0.5 ~ 1纳秒之间,if else流水线预测失败有3纳秒的消耗,访问内存在100纳秒以内,访问缓存只有几纳秒;从这来看信号量的消耗比较大,效率低,因此基本能不怎么使用信号量

信号量控制(semctl)

key相同的信号量,长度必须相同

上面我们讲到的信号量控制都是控制一个信号量,假如有两个信号量我们该怎么控制呢,我们可以使用二元信号量,先P一下再去看相应的信号量是否能够让我们继续执行下面的步骤;但这样的写法不是很优雅,因此我们想到使用计数信号量来实现多个信号量的监控,假如我们要生产面包和蛋糕,都要使用鸡蛋,但是制作面包和蛋糕所消耗的鸡蛋和面粉数量都不相同。

在这里插入图片描述

生产者消费者问题

二元信号量解决生产者消费者问题

在这里插入图片描述
要把所有对共享资源的访问保护到PV操作之间
在这里插入图片描述

消息队列(了解)

狭义的消息队列,是IPC只能用在本机通信(保留消息边界),下面讲的就是狭义的消息队列
广义的消息队列:网络通信

管道是流式消息没有消息边界,通信双方要事先约定边界,否则双方对消息的获取有问题,但是没有边界通信效率会更高
网络TCP也是流式消息
在这里插入图片描述

消息队列(system V)先进先出
创建一个消息队列,就是创建一个消息的信箱
msgflg的值IPC_CREAT|0600
在这里插入图片描述

发送和接收消息

msgid的值式上面创建消息队列的返回值
const void *msgp是指向消息的指针,下面是消息指针结构体,但是这个结构体需要用户自己定义,其实更改的是mtext的大小
msgsz发送消息的实际长度
在这里插入图片描述
在这里插入图片描述

proc文件系统

proc(process) 是伪文件系统,他不是真正代表磁盘里面的文件,它只是操作系统的运行状态在文件系统的映射
为什么要映射到文件里面呢,这样我们可以像修改文件一样取修改操作系统的属性
cat 读文件
echo 写文件

信号(重要)

信号是一种软件层面的异步事件机制

中断:硬件层面

信号的默认行为
在这里插入图片描述
Term终止
Ign忽略
Core 终止并生成core,core文件生成的原理 ,收到一个core信号就生成一个core文件
Stop暂停
Cont恢复

信号产生的时机

(1)软件层面 + 异步 (kill -9 pid
(2)软件层面 + 同步 (调用abort
(3)硬件层面 + 异步 (ctrl + c ;ctrl + \
(4)硬件层面 + 同步 (除0)

软件或者硬件来产生一个信号,信号作用的目标进程来递送信号,中间会产生一个时间间隔

信号的作用:实现进程的有序退出

当信号产生时
信号产生时会修改目标进程的 task_struct
目标进程会认为所有的信号都来自内核
响应时机(可递送信号的时机):几乎所有信号,除了D状态(不可中断睡眠)

下面我们将用改变信号响应之后的默认操作,改为调用某个函数

在这里插入图片描述
在这里插入图片描述
这个函数能够注册(让进程知道有这么一个函数存在,但是要等到信号到来时才会调用)一个信号处理行为,

信号递送行为,可以注册多个信号
我们可以看到当我们使用signal将暂停信号SIGINT(2号信号)的默认操作改为某个执行函数,当我们键盘上输入暂停信号CTRL+C时,进程并未暂停,而是执行了我们定义的函数,只有我们使用kill -9 pid或者CTRL + C命令才能终止进程
这样的函数我们称之为回调函数
在这里插入图片描述
当我们将输出语句后面的换行符\n去掉时,数据只会留在用户态的stdoout中,不会被写入到内核态的文件对象中,因此屏幕上也不会输出我们想要输出的数据

在这里插入图片描述

我们在进行一个信号递送过程中,但是信号还没递送完,又触发了一个相同的信号,系统不会进行第二个信号的递送,会等到第一个信号的递送完成之后才会进行第二个信号的递送。当发起第二个信号,如果上一个信号还没递送完成,这时第二个信号就会发生阻塞,当第二个信号到达至第二个信号开始递送的时间被称之为未决时间,但是当我们在第一个进程递送的时候触发了多个需要递送的信号,最终只会处理当前递送信号之后的哪一个递送信号
在这里插入图片描述

阻塞和未决实现的原理

阻塞:让产生的信号,不能马上递送,而是处于未决状态
未决:已产生但是未递送的信号

mask 阻塞信号集 pending 未决信号集 ,他们都属于位图(能够保存两种状态0或1

递送信号A,把信号A本身加入mask,之后有A信号产生,将该信号加入pedding;信号递送完成之后,A移除mask,递送pedding的信号,如果pedding位图为空,则不会在执行信号

不同信号的阻塞,如果在递送A信号的时候,产生另外的信号B,此刻会暂停A信号的递送,直接转向执行B信号的递送,B信号递送完毕再继续递送A信号

如果正在递送的时A信号,此时来了一个B信号,进程会转而执行B信号,但是当B信号递送的时候又来一个A信号,此时需要等到B信号递送完毕,并且之前的A信号递送完毕才会来递送这个A信号

低速系统调用

低速系统调用:可能陷入永久等待的系统调用,再信号递送完成之时,会子自动重启低速系统调用
在这里插入图片描述

signal的特点
(1)一次注册,永久生效
(2)递送A时,会将A加入mask,其他信号不会加入mask
(3)会自动重启低速系统调用
注册一次,生效一次,执行我们的函数之后吗,我们再将收到信号的操作改为默认操作
在这里插入图片描述

sigaction

sigaction可以完全取代signal,可以实现精确的控制信号

在这里插入图片描述
const struct sigaction :新状态
struct sigaction:保存旧状态(不想保存写NULL)

在这里插入图片描述

第一个和第二个结构体成员二选一,其代表递送行为对应的sigfunc
sa_mask:额外mask,其实是一个位图
sa_flags:属性

sa_flags属性
在这里插入图片描述
SA_NODEFER在递送A的时候不屏蔽A,就是说在递送A信号时又遇到一个A信号,这时会暂停现在的A信号递送,转而递送新的A信号
在这里插入图片描述
SA_RESETHAND只注册一次
在这里插入图片描述
默认使用1参数的回调函数,默认不会自动重启低速系统调用
在这里插入图片描述
递送过程中会把自己加入mask
但是我们可已使用SA_RESTART修改,让其自动重启低速调用
在这里插入图片描述
在这里插入图片描述

sa_mask
sa_mask其实是一个位图

在这里插入图片描述

这五个函数可以操作位图
在这里插入图片描述
sigemptyset 清空集合
sigfillset集合的每个位置为1
sigaddset将signum加入到集合中
sigdelset将signum从集合中移除
sigismember判断signum是否在集合中
mask阻塞集合,平时mask中没有信号,在递送中,将自己加入(有SA_NODEFER属性就不加入) pending未决集合

sa_mask用来指定递送过程中的额外屏蔽信号
在这里插入图片描述

在SIGINT信号的递送过程中,把SIGQUIT加入阻塞。如果进程在递送A的时候来了B信号,不会递送B信号,而是等A信号递送完毕才会递送B信号。sa_mask是一种临时的额外阻塞

sigprocmask实现全程阻塞 在这里插入图片描述
how表示要怎么样操作这个阻塞
SIG_BLOCK 将信号加入
SIG_UNBLOCK把信号移除
SIG_SETMASK清空记录的信号
在这里插入图片描述

const sigset_t *set 新设置的阻塞情况
sigset_t *oldset 保存原来设置的阻塞情况,如果是空指针那么就不保存

在这里插入图片描述
进程在屏蔽信号过程中如果我们输入信号,那么信号会被放在pending集合中,当屏蔽解除之后就会检查pending集合中有没有需要递送的信号,有就会执行信号递送
因为pending集合只能存储一个需要递送的信号,因此即使我们在屏蔽期间触发多次信号,最后信号解除之后也只会递送一次信号

获取pending集合

可以在信号屏蔽期间获取pending集合,查看是否有信号递送
在这里插入图片描述
sigpending也只能在信号屏蔽期间才能使用
在这里插入图片描述

pause

在没有被屏蔽的情况下,当信号递送完毕之后不会阻塞,程序会继续往下走
在这里插入图片描述

通常是用来调试程序的时候可以使用pause

在这里插入图片描述

kill发送信号

发送一个信号个一个进程

在这里插入图片描述
pid 目标进程的pid
sig 信号的类型
在这里插入图片描述

raise
给自己发信号
在这里插入图片描述
在这里插入图片描述

alarm

定义一个闹钟,闹钟结束时会发送一个SIGALRM信号

在这里插入图片描述

alarm不会阻塞

在这里插入图片描述

不能混合使用alarmsleep,因为有的操作系统中的sleep有可能时alarm实现的

时钟

间隔定时器
先过value的时间长度定时器会响一次,后面再每过interval的时间会响一次
在这里插入图片描述
定时器分为三种,统计的时不同的事件
ITIMER_REAL真实时间,我们物理世界的时间,会发送 SIGALARM信号
ITIMER_VIRTUAL虚拟时间 为了确保公平,统计的是用户态占用CPU的时间,时间到就会发送SIGVTALRM信号
真实事件过的比虚拟事件快
ITIMER_PROF实用时间,占用用户态 + 内核态CPU的时间 ,会发送SIGPROF信号
在这里插入图片描述
获取或者设置一个定时器
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值