嵌入式学习——进程间通信方式(5)—— 信号量
一、基本概念
信号量和之前的所说的管道、共享内存、消息队列、信号是不一样的,信号量的本质是一个整型计数器,不以传输数据为目的,主要是对临界资源进行一个保护,例如我们在操作共享内存时,要配合信号量来进行使用,目的是为了对共享内存数据的保护,防止多进程同时操作出现数据混乱。
二、工作原理
信号量只有两种操作:等待信号和操作信号,就是P操作和V操作(都是原子操作),P操作是使用临界资源,而V操作是释放临界资源。
P操作:申请临界资源进行使用(信号量大于0),则占用一个资源,把信号量减去1,若此时的信号量为0,则阻塞,一直在等待队列进行等待,直到有资源进行释放。
V操作:如果在等待队列有进程在等待资源,则唤醒阻塞的进程,如果没有的话,释放资源,把信号量的值加一。
原子操作:在进行原子操作时,不会被打断,直到操作完成
三、使用流程
1)创建或获取信号量
int semget(key_t key, int nsems, int semflg);
key:① 通过调用key_t ftok(const char *pathname, int proj_id)来进行获取;这个函数是通过指定的文件路径和计划编号合成一个key值。
② IPC_PRIVATE 内核会保证创建一个新的,唯一的IPC对象,这是一个宏定义,其值为0。
nsems:本参数用于在创建信号量的时候,表示可用的信号量数目。
semflg:IPC_CREAT 若系统中有相同的key值,则返回信号量的标识符;若不存在则创建,要与 0600 结合 给相应的权限(IPC_CREAT | 0600)。
IPC_EXCL 如果系统中有相同的key值,则会报错;不存在则创建。
注:可以在linux下输入 ipcs -l 查看信号量最大值,最大操作数,还有消息队列和共享内存的相关信息。
2)初始化信号量
int semctl(int semid, int semnum, int cmd, ...);
semid:就是创建/获取信号量时,返回的描述符。
semnum:表示第几个信号量,取值范围是0~num-1。
cmd:操作该信号的指令,有如下这些:
-
IPC_SET:通过第四个参数的buf来设定信号量集相关联的semid_ds中信号量集合权限为sem_perm中的uid,gid,mode。
-
IPC_RMID:从系统中删除该信号量集合。
-
GETVAL:返回第semnum个信号量的值。
-
SETVAL:设置第semnum个信号量的值,该值由第四个参数中的val指定。
-
GETPID:返回第semnum个信号量的sempid,最后一个操作的pid。
-
GETNCNT:返回第semnum个信号量的semncnt。等待semval变为大于当前值的线程数。
-
GETZCNT:返回第semnum个信号量的semzcnt。等待semval变为0的线程数。
-
GETALL:去信号量集合中所有信号量的值,将结果存放到的array所指向的数组。
-
SETALL:按arg.array所指向的数组中的值,设置集合中所有信号量的值。
-
IPC_STAT:获取此信号量集合的semid_ds结构,存放在第四个参数的buf中。
3)操作信号量
int semop(int semid, struct sembuf *sops, size_t nsops);
-
semid:System V信号量的标识符,用来标识一个信号量。
-
sops:是指向一个struct sembuf结构体数组的指针,该数组是一个信号量操作数组。
-
nsops:表示上面sops数组的数量,如只有一个sops数组,nsops就设置为1。
4)销毁信号量
int semctl(int semid, int semnum, int cmd, ...);
在销毁信号量时,指定描述符,把cmd指定为IPC_RMID即可。