IPC资源必须删除,否则不会自动消除,除非重启,所以System V IPC资源的生命周期随内核
信号量(信号灯):用于保护共享资源(临界资源)
我们来举个例子:电影院买票,我们需要进行买票。我们在之前的共享内存是一个整体使用的,但是在电影院中,会有很多的座位供我们挑选。信号量本质上是一个计数器,当我们购买电影票时,我们需要进行电影票的预定,申请信号量的本质就是对公共资源的一种预定机制。
信号量分为二元信号量和多元信号量,在二元信号量中,信号量的个数为1(相当于将临界资源看成一整块),二元信号量本质解决了临界资源的互斥问题,以下面的伪代码进行解释:
- while (1)
- {
- if(sem == 1)
- sum--;
- else
- // 挂起
- // 写入共享内存
- sem++;
- }
根据以上代码,当进程A申请访问共享内存资源时,如果此时sem为1(sem代表当前信号量个数),则进程A申请资源成功,此时需要将sem减减,然后进程A就可以对共享内存进行一系列操作,但是在进程A在访问共享内存时,若是进程B申请访问该共享内存资源,此时sem就为0了,那么这时进程B会被挂起,直到进程A访问共享内存结束后将sem加加,此时才会将进程B唤起,然后进程B再对该共享内存进行访问操作。
在这种情况下,无论什么时候都只会有一个进程在对同一份共享内存进行访问操作,也就解决了临界资源的互斥问题。
实际上,代码中计数器sem减减的操作就叫做P操作,而计数器加加的操作就叫做V操作,P操作就是申请信号量,而V操作就是释放信号量。
函数的原型:
函数的功能:
创建一个新的信号量或获取一个已经存在的信号量的键值。
函数的参数:
- key:为整型值,用户可以自己设定。有两种情况:键值是IPC_PRIVATE,该值通常为0,意思就是创建一个仅能被进程进程给我的信号量。键值不是IPC_PRIVATE,我们可以指定键值,例如1234;也可以一个ftok()函数来取得一个唯一的键值。
- nsems:表示初始化信号量的个数。比如我们要创建一个信号量,则该值为1.,创建2个就是2。
- semflg:信号量的创建方式或权限。有IPC_CREAT,IPC_EXCL。IPC_CREAT如果信号量不存在,则创建一个信号量,否则获取。IPC_EXCL只有信号量不存在的时候,新的信号量才建立,否则就产生错误。
函数的返回值:
成功返回信号量的标识码ID。失败返回-1
函数的原型:
函数的功能:
控制信号量的函数,在这个函数中我们可以删除信号量或初始化信号量。
函数的参数:
- semid:信号量的标志码(ID),也就是semget()函数的返回值
- semnum:操作信号在信号集中的编号。从0开始
- cmd:表示要进行的操作
cmd参数可以使用的命令:
- IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
- IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
- IPC_RMID将信号量集从内存中删除。
- GETALL用于读取信号量集中的所有信号量的值。
- GETNCNT返回正在等待资源的进程数目。
- GETPID返回最后一个执行semop操作的进程的PID。
- GETVAL返回信号量集中的一个单个的信号量的值。
- GETZCNT返回这在等待完全空闲的资源的进程数目。
- SETALL设置信号量集中的所有的信号量的值。
- SETVAL设置信号量集中的一个单独的信号量的值。
函数的返回值:
成功返回0,失败返回-1
函数的原型:
函数的功能:
用户改变信号量的值。也就是使用资源还是释放资源使用权
函数的参数:
- shmid:信号量的标识码。也就是semget()的返回值。
- sops是一个指向结构体数组的指针。
struct sembuf{ unsigned short sem_num;//第几个信号量,第一个信号量为0; short sem_op;//对该信号量的操作。 short _semflg; };
- nsops:操作结构的数量,恒大于或等于1。
函数的返回值:
成功返回0,失败返回-1
- // 查看系统中的信号量
- ipcs -s
- // 删除系统中的信号量
- ipcrm -s shmid
通过对system V系列进程间通信的学习,可以发现共享内存、消息队列以及信号量,虽然它们内部的属性差别很大,但是维护它们的数据结构的第一个成员确实一样的,都是ipc_perm类型的成员变量。
这样设计的好处就是,在操作系统内可以定义一个struct ipc_perm类型的数组,此时每当我们申请一个IPC资源,就在该数组当中开辟一个这样的结构。