进程间通信 共享内存 + 信号量

共享内存是最快的 IPC形式,,一旦这样的内存映射到进程的地址空间,这些空间的数据将不再涉及内核。

ipcs -m 查看共享内存

几个共享内存函数

0 申请共享内存id

key_t key = ftok(“PathName” , Project_id);

1 创建共享内存

int shmget(key_t key ,size_t size,int shmflg);
参数:
key: 共享内存的id ftok得到
size : 共享内存的大小

shmflg: 类似于创建文件时的mode ,
返回值: 成功返回共享内存的标识码shmid,失败返回-1

shmflag:
IPC_CREAT ,不存在就创建,存在返回句柄,shmid

IPC_EXCL | IPC_CREAT:创建,存在就出错返回

2 shmat() 将共享内存段连接到进程地址空间

void* shmat(int shmid,const void*shmaddr, int shmflg)

shmid : 共享内存标识码,

shmaddr: 指定连接的地址(一般设为空,由OS分配),也就是shmat的返回值

shmflg: 两个可能取值

SHM_RND 和SHM_RDONLY 
SHM_RND为低边界地址的倍数(2的乘方)
公式:SHM_ADDR -=  SHMADDR%SHMLBA

shmaddr 为NULL,内核自动选一个地址

返回值:成功返回一个指向共享内存的句柄,
失败返回-1

3 shmdt:将共享内存与当前进程脱离
(脱离 != 删除)

int shmdt(const void* shmaddr);
shmaddr为共享内存的地址,即shmat的返回值
返回值 :成功:0 失败 -1

4 shmctl 控制共享内存

int shmctl (int shmid, int cmd, struct shmid_ds *buf)

shmid(共享内存的操作句柄)

cmd: 将要采取的动作

命令 ipcs -stat
ipcs -set

System V共享内存

共享内存的步骤:
1 创建/打开共享内存对象ftok(“存在的路径”,任意整数);
eg: key_t ftok(".",0x02) 可以写成一个函数 只要参数相同,一定是共享内存

系统建立IPC通讯 ( 消息队列、 信号量 和 共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
返回值在0-255 即只有八位有效

2 附加到共享内存对象上(shmat->attach) 用法和malloc十分相似
eg :void* shmat (shmid, NULL(虚拟地址由系统说了算,0),需要用时手动强转,

3 使用共享内存
先写,再读
共享内存和管道不同,管道一被访问就出队列,共享内存就是普通的内存,

消息队列

是个带类型的队列,每次按照指定的类型出队列

IPC(共享内存  消息队列  信号量)
广义的消息队列是一个队列,队列每个元素都自带一个类型,先进先出的出入队顺序

消息队列服务器集群其实是一个大型的消息队列,他含有许多服务件,服务件与业务无关,有很多的客户端程序,通过网络的进行特定的出入队
消息队列提供了从一个进程向另一个进程发送一块数据的方法
IPC资源必须删除,否则不会自动清理。除非手动重启,  所以   System V  IPC生命周期随内核

System V 信号量

作用 : 进程的同步与互斥

实质就是一个计数器 + PCB等待队列

计数器是对资源的计数,+1 或 -1;
取值只有 0 和 1

原理:

访问临界资源前,先看信号量的计数器的值是 -1 还是0(预减1,并未真正减)

若为 0 则代表临界资源可用,访问时,临界资源-1;

退出时,临界资源再 +1 ,

这样一个进程访问临界资源时,别的进程就只能等待了,等待的进程处于信号量的PCB等待队列,进行阻塞等待

这样就达到了 进程间互斥,

同步是指,当
前操作能够无中断的达到目的,而不是过一会儿才到达目的。

进程同步 ,计数器将不再限制为0 或1 ,而可以取任意值。计数器 将初始化为资源对数量 。

访问资源时 (P操作),将对计数器 预-1操作,

如果 大于等于0,则访问临界资源(即告诉PCB等待队列可以访问资源了)

进程互斥: 有的进程操作内存的时候,不能允许别的进程同时使用同一内存,因此各进程之间竞争使用这一内存资源 ,这就是 进程互斥

临界资源(互斥资源):系统中的资源只能被一个进程使用,称为临界资源。

临界区: 系统中涉及到临界资源使用的程序称为临界区
IPC资源必须删除,否则不会自动清理。除非手动重启,  所以   System V  IPC生命周期随内核

信号:

信号是进程之间事件异步通知的一种方式,属于软中断。在这里插入图片描述
共有 62种信号,没种信号都有自己的一个编号和一个宏定义名称
作用:是进程进行情况的有一种告知,告知系统当前(不一定是错误的,)状态,

信号的 处理 :
1 忽略
2 执行信号的默认处理动作
3 用户提供一个信号处理函数, 内核捕捉到信号时, 模拟用户去执行该函数, 称为信号捕捉(catch).

信号的产生:

1 键盘
eg: ctrl +c —> 2号 SIGINT
ctrl + z —>屏幕锁定 20 SIGTSTP
ctrl + s 解除锁定
ctrl + | 终止信号 15号信号

2 ** 硬件**
eg: MMU产生的信号(11号信号,段错误) MMU负责虚拟地址翻译为物理地址,负责逻辑地址和物理地址之间的映射

		若非法,MMU硬件设备给OS发送这个错误情况,然后**OS给程序发送11号信号**(MMU负责管理内存单元,)
		eg: 解引用空指针
        eg :  CPU 发出的8号信号 浮点数异常
        	    (除0运算)

3 软件

1 断言 assert()

	   类似MMU将虚拟翻译为物理
	   进程遇到abort  ()函数而异常终止

abort进程自己将自己挂掉(自挂东南枝)

void abort(void) abort 函数总是成功的,所以没有返回值

pipe读端未关闭出现 SIGPIPE(管道破裂),这家伙出现错误没有任何预告,出错时会说明失效。

2 alarm 函数 (设置关机时间)
int alarm(unsigned int second)
返回值为 0 或者当前设定时间剩余的秒数

该函数的执行结果是终止当前进程

7
信号的捕捉 使用 signal()函数 man 来了解一下 signal 函数在这里插入图片描述
我们 发现, 两个参数 ,一个信号编号,一个函数指针 ,我们都可以自己实现哦! 运行时,这时候 kill -9 指 令任然有效的

当第二个参数为 SIG_IGN时,忽略掉信号 ,不进行捕捉在这里插入图片描述

3 调用函数产生 :

kill 底层实际上是调用kill 函数 来 进行的
kill(number , pid)给指定进程发信号

raise()给自己发信号

abort () 进程杀死自己

信号阻塞:

有时信号不方便立刻被处理,等待时机再被处理

信号 1 未决信号集 :从产生到递达:
2 阻塞信号集 :标识那些信号不能被立刻处理,通过一些函数来阻塞信号集.
3 信号处理函数集:内部为函数指针数组 ------------------,数组内部存用来处理信号的的函数
我们采用 位图来存储信号 无符号的 64为整数 0-63

问题 : 1 同一时刻收到了大量的某一个信号(只讨论1-31号信号 )就会出现问题
2 一个函数在多个执行流中执行时可能会出现问题 ,(eg:一个执行一半,触发另一个函数,然后又跑去执行另一个,所以会出现问题 )

重入是对函数来说的:

这时 ,如果一个函数在多个执行流中没问题,我们称这个函数为可重入函数否则,称之为不可重入函数
注意 ,递归只是一个执行流
**
怎么判断不可重入 : ** 1 函数调用了全局变量 /静态变量 ,不可重入
2 malloc(本质上还是调用了全局变量)
3 调用了不可重入函数也不可重入
4 调用标准I/O库,标准I/O库的实现基本都是以不可重入规则写的,基本都为全局数据结构

volatile: C 语言的关键字**,这就是个坑,不容易发现**,代码优化可能会出BUG,

volatile告诉编译器不要将变量优化到寄存器中,每次都去内存中读.

经常使用在多线程中,在多线程中,经常会出错(如 懒汉模式).

waitpid

	  如果在调用 waitpid()时子进程已经结束,则 waitpid()会立即

返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回,

而子进程的进程识别码也会一起返回。如果不在意结束状态值,则

参数 status 可以设成 NULL。参数 pid 为欲等待的子进程识别码,

其他数值意义如下:

pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。

pid=-1 等待任何子进程,相当于 wait()。

pid=0 等待进程组识别码与目前进程相同的任何子进程。

pid>0 等待任何子进程识别码为 pid 的子进程。

参数options提供了一些额外的选项来控制waitpid,参数 option 可以为 0 或可以用"|"运算符把它们连接起来使用,比如:

ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

如果我们不想使用它们,也可以把options设为0,如:

ret=waitpid(-1,NULL,0);

WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。

WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。WIFSTOPPED(status)宏确定返回值是否对应与一个暂停子进程。

子进程的结束状态返回后存于 status,底下有几个宏可判别结束情况:

WIFEXITED(status)如果若为正常结束子进程返回的状态,则为真;对于这种情况可执行WEXITSTATUS(status),取子进程传给exit或_eixt的低8位。

WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。

WIFSIGNALED(status)若为异常结束子进程返回的状态,则为真;对于这种情况可执行WTERMSIG(status),取使子进程结束的信号编号。

WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED 来判断后才使用此宏。

WIFSTOPPED(status) 若为当前暂停子进程返回的状态,则为真;对于这种情况可执行WSTOPSIG(status),取使子进程暂停的信号编号。

WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。

如果执行成功则返回子进程识别码(PID) ,如果有错误发生则返回

返回值-1。失败原因存于 errno 中。

发布了90 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44030580/article/details/103612619
今日推荐