共享内存是最快的 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 中。