文章目录
信号量概念
•英文:semaphore,简称SEM,主要用来进程间同步
•本质:内核维护的一个正整数,可对其进行各种操作+/-操作
•分类:system V 信号量、POSIX 有名信号量、POSIX 无名信号量
•用途:用来标识系统中可用的共享资源的个数,协调各进程有序地使用这些资源,防止发生冲突
•信号量类似于酒店房间的房卡,房间资源是有限的、房卡也是有限的
•P操作:程序在进入临界区之前要先对资源进行申请
•V操作:程序离开临界区后要释放相应的资源,如房卡交给房东
通信原理
•类似于房卡,不是单个值,而是一组(实际上是数组)信号量元素构成
•将信号量设置成一个绝对值
•在信号量当前值的基础上加上一个数量
•在信号量当前值的基础上减去一个数量,降到0以下可能会引起阻塞
•阻塞进程一直等待其它进程修改信号量的值,直到恢复正常运行
•信号量本身无意义,通常会与一块临界资源(如共享内存)关联使用
相关API
•获取信号量ID:int semget (key_t key, int nsems, int semflg);
•P/V操作:int semop (int semid, struct sembuf *sops, size_t nsops);
–操作术语:荷兰语中的首字母,由荷兰计算机科学家Edsger Dijkstra确定
–其它操作术语:down(减小信号量)、up(增大信号量)
–POSIX标准:wait、post
•信号量设置:int semctl (int semid, int semnum, int cmd, …);
semget
含义
创建或打开一个信号量
函数原型
int semget (key_t key, int nsems, int semflg);
头文件
sys/ipc.h
sys/sem.h
函数参数
–key:用来表示信号量的键,通常使用值IPC_PRIVATE或由ftok创建
–nsems:信号的数量,所有的信号放在一个数组内
–semflg:位掩码,用来设置信号量的权限或检查一个已有信号量的权限
>>IPC_CREAT:如果找不到指定key相关联的信号量,创建一个新信号量集合
>>IPC_EXCL:若指定了IPC_CREAT且指定key关联的信号量存在,报EEXIST错误
函数返回值
–成功:返回用于操作信号量的句柄ID
–失败:-1,并设置errno全局变量
semctl
含义
信号量设置
函数原型
int semctl (int semid, int semnum, int cmd, …);
头文件
– #include <sys/ipc.h>
– #include <sys/sem.h>
函数参数
–semid :用于操作信号量的句柄ID、标识符
–semnum :信号的数量,所有的信号放在一个数组内
–cmd:
»IPC_RMID:删除信号量集及相关联的内核semid_ds数据结构
»IPC_STAT:获取 semid_ds 副本
»IPC_SET:设置 semid_ds 数据结构
»GETVAL:获取信号集中第semnum个信号量的值
»GETALL:获取所有的信号量的值
»SETVAL:设置信号集中的第semnum个信号量的值
函数返回值
–成功:根据cmd命令,返回不同的值
–失败:-1,并设置errno全局变量
semop
含义
信号量P/V操作
函数原型
int semop(int semid, struct sembuf *sops, size_t nsops);
头文件
– #include <sys/ipc.h>
– #include <sys/sem.h>
函数参数
–semid :用于操作信号量的IPC标识符
–sops :指向数组的指针,数组中包含了需要执行的操作
–nsops :数组的大小
函数返回值
–成功:根据cmd命令,返回不同的值
–失败:-1,并设置errno全局变量
sembuf结构体
• sem_num:用来标识要操作的信号集中的信号量
•sem_op:
–若大于0:将sem_op的值加到信号量值上
–若等于0:对信号量值进行检查,确定其当前值是否为0,若为0操作结束,若不为0,则一直阻塞,直到信号量的值变为0为止
–若小于0:将信号量值减去sem_op。最后结果大于或等于0,操作立即结束;若最后结果小于0,则当前进程会阻塞
•sem_flag:
–SEM_UNDO
–IPC_NOWAIT
sem_flag:
–SEM_UNDO
–IPC_NOWAIT
struct sembuf {
unsigned short sem_num; /* semaphore index in array */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
使用流程
•使用semget创建或打开一个信号量集
•使用semctl SETVAL或SETALL操作初始化集合中的信号量(其中一个进程操作即可,内核中维护,对其它进程是全局可见的)
•使用semop操作信号量值。多个进程通过多信号量值的操作来表示一些临界资源的获取和释放
•当所有进程不再需要信号量集时,使用semctl IPC_RMID操作删除这个信号量集(其中一个进程操作即可)
举例
创建一个信号量,并对其进行p/v操作
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#define handle_error(msg) \
{perror(msg);exit(EXIT_FAILURE);}
// creat a semphore
// print and set semphore
// P/V sem value
// delete sem
union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
/*
struct sembuf
{
unsigned short sem_num;//semaphore index in array
short sem_op; //sepaphore operation
short sem_flg; //operation flags
};
*/
int main (void)
{
int sem_id;
key_t key;
if ((key = ftok(".", 514))== -1)
handle_error("ftok");
if ((sem_id = semget (key, 3, IPC_CREAT | 0770)) == -1)
handle_error("semget");
printf ("sem id: %d\n", sem_id);
// get sem value
int sem_value;
sem_value = semctl (sem_id, 0, GETVAL);
printf ("sem value: %d\n", sem_value);
// set sem value
union semun sem_union;
sem_union.val = 2;
semctl (sem_id, 0, SETVAL, sem_union);
sem_value = semctl (sem_id, 0, GETVAL);
printf ("sem value: %d\n", sem_value);
//sem p/v operations
struct sembuf sops;
sops.sem_num = 0;
sops.sem_op = -2;// if sem value=0, blocked
sops.sem_flg = SEM_UNDO;
if (semop (sem_id, &sops, 1) == -1)
handle_error("sem_op");
sem_value = semctl (sem_id, 0, GETVAL);
printf ("sem value: %d\n", sem_value);
sleep (30);
semctl (sem_id, 0, IPC_RMID);
return 0;
}
应用:对共享内存的同步访问
write
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int sem_id;
void sem_init (int semid, int nsignum, int sem_value)
{
union semun sem_union;
sem_union.val = sem_value;
if (semctl (semid, nsignum, SETVAL, sem_union) == -1)
{
perror ("semctl");
exit (EXIT_FAILURE);
}
}
void sem_p (int semid, int nsignum)
{
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
if (semop (sem_id, &sops, 1) == -1)
{
perror ("semop");
exit (EXIT_FAILURE);
}
}
void sem_v (int semid, int nsignum)
{
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
if (semop (sem_id, &sops, 1) == -1)
{
perror ("semop");
exit (EXIT_FAILURE);
}
}
void sem_print (int sem_id, int nsignum)
{
int sem_value;
sem_value = semctl (sem_id, nsignum, GETVAL);
printf ("sem[%d] = %d\n", nsignum, sem_value);
}
int main (void)
{
int shm_id;
key_t shm_key = ftok ("./", 5151);
key_t sem_key = ftok ("./", 5152);
shm_id = shmget (shm_key, 1028, IPC_CREAT | 0644);
char *shm_addr = shmat (shm_id, NULL, 0);
sem_id = semget (sem_key, 2, IPC_CREAT | 0644);
if (sem_id == -1)
{
sem_id = semget (sem_key, 2, 0644);
}
else
{
sem_init (sem_id, 0, 0); //sem[0]: for read
sem_init (sem_id, 1, 1); //sem[1]: for write
}
while (1) //write
{
sem_p (sem_id, 1);
fgets (shm_addr, 1028, stdin);
sem_v (sem_id, 0);
}
return 0;
}
read
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/types.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;
};
int sem_id;
void sem_init (int semid, int nsignum, int sem_value)
{
union semun sem_union;
sem_union.val = sem_value;
if (semctl (semid, nsignum, SETVAL, sem_union) == -1)
{
perror ("semctl");
exit (EXIT_FAILURE);
}
}
void sem_p (int semid, int nsignum)
{
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = -1;
sops.sem_flg = SEM_UNDO;
if (semop (sem_id, &sops, 1) == -1)
{
perror ("semop");
exit (EXIT_FAILURE);
}
}
void sem_v (int semid, int nsignum)
{
struct sembuf sops;
sops.sem_num = nsignum;
sops.sem_op = 1;
sops.sem_flg = SEM_UNDO;
if (semop (sem_id, &sops, 1) == -1)
{
perror ("semop");
exit (EXIT_FAILURE);
}
}
void sem_print (int sem_id, int nsignum)
{
int sem_value;
sem_value = semctl (sem_id, nsignum, GETVAL);
printf ("sem[%d] = %d\n", nsignum, sem_value);
}
int main (void)
{
int shm_id;
key_t shm_key = ftok ("./", 5151);
key_t sem_key = ftok ("./", 5152);
shm_id = shmget (shm_key, 1028, IPC_CREAT | 0644);
char *shm_addr = shmat (shm_id, NULL, 0);
sem_id = semget (sem_key, 2, IPC_CREAT | 0644);
if (sem_id == -1 )
{
perror ("semget");
exit (EXIT_FAILURE);
}
else
{
sem_init (sem_id, 0, 0);//sem[0]: for read
sem_init (sem_id, 1, 1);//sem[1]: for write
}
while (1) //read
{
sem_p (sem_id, 0);
printf ("from shm:%s", shm_addr);
sem_v (sem_id, 1);
}
return 0;
}