信号量作用
当多个进程/线程进行共享操作时,用于资源保护,以防止出现相互干扰的情况。再间简洁一点,信号量用于“资源的保护“。
进程信号量:实现的是进程所操作资源的保护。
线程信号量:实现的是线程所操作资源的保护。
疑问:资源保护,这个“资源”到底指的是谁?
答:这个资源指的就是你操作的数据,保护的目的就是不要出现相互干扰,导致紊乱和错误数据的产生。
资源保护操作的种类
资源保护的操作分两种,一种叫互斥,另一个种叫同步。有些资源保护机制只能实现互斥,不能实现同步,比如。但是这里讲的信号量既能实现互斥,也能实现同步。
互斥
对于互斥操作来说,多进程共享操作时,多个进程间不关心谁先操作、谁后操作的先后顺序问题,它们只关心一件事,那就是我在操作时别人不能操作。互斥时,如果当前正在操作的进程,他的时间片到了,切换到了其他进程上,但是当该进程检测到上一个进程还没有操作完时,该进程在当前的时间片内会休眠,直到再次切换会上一个进程,将操作完成后再切换回来,此时才能进行操作。这跟上厕所时把门关起来是一样的,我在蹲坑时你不能蹲,你在蹲坑时我不能蹲,这就是互斥,至于蹲坑先后顺序并没有要求。
2018-9-23更新
想象一下这种场景,10个进程,每个进程都要访问临界区,CPU分配给进程的时间片是10ms。假设第一个进程进入临界区并加锁,10ms时间片到,进程还没有出临界区,锁还在第一个进程的手里。由于时间片到了,调度进程切换其他进程到CPU上执行,假设在第2ms的时候需要进入临界区,由于临界区的锁在第一个进程手中,于是当前进程阻塞。后面8个进程同样原因阻塞。有一个时刻CPU还会切换到第一个进程(第一个进程是由于时间片到了,被切换掉,但他还是就绪状态),第一个进程执行完毕后唤醒阻塞队列上其他进程。假设唤醒第二个进程,第二个进程拿锁顺利进入临界区,10ms没执行完,被切换掉了。但是此时就绪队列上只有第二个进程,于是第二个经常继续执行,执行完毕后唤醒第三个进程,依次类推
2018-9-23更新
同步
同步其实本身就包含了互斥,不过同步不仅仅只互斥,同步对于谁先操作、谁后操作的先后顺序有要求,比如规定A进程先写,然后是B进程写,然后是C进程写,绝对不能出现这操作顺序以外的顺序。所以所谓同步就是,多个共享操作时,进程必须要有统一操作的步调,按照一定的顺序来操作。
实现同步、互斥,其实就是加锁
这个很形象,我要操作我就上把锁,我上锁的过程中你就不能操作,直到我把锁打开了,你才能操作,你操作时也会加锁,加锁后我就不能操作了。所以说信号量就是一个加锁机制,通过加锁来实现同步和互斥。不管是进程还是线程,都存在同步和互斥的问题,同步和互斥的目的其实就是为了实现“资源”的保护,不要让数据(资源)出现紊乱。
疑问:信号量既然是一种加锁机制,为什么进程信号量会被归到了进程间通信里面呢?
资源保护时,某个进程的操作没有完全完成之前,别人是不能操作的,那么进程间必须相互知道对方的操作状态,必须会涉及到通信过程。所以信号量实现资源保护的本质就是,通过通信让各个进程了解到操作状态,然后查看自己能不能操作。
信号量的使用步骤
①进程调用semget函数创建新的信号量集合,或者获取已有的信号量集合。
②调用semctl函数给集合中的每个信号量设置初始值
③调用semop函数,对集合中的信号量进行pv操作
什么是pv操作?pv操作其实说白了就是加锁、解锁操作。
P操作(加锁):对信号量的值进行-1,如果信号量的值为0,p操作就会阻塞
V操作(解锁):对信号量的值进行+1,V操作不存在阻塞的问题
④调用semctl删除信号量集合
API
semget
原型
#include#include #include int semget(key_t key, int nsems, int semflg);
功能
根据key值创建新的、或者获取已有的信号量集合,并返回其标识符。
实现互斥时:集合中只需要一个信号量
实现同步时:集合中需要多个信号量
参数
key:设置同消息队列和共享内存。一般都使用ftok获取key值。
nsems:指定集合中信号量的个数。
用于互斥时,数量都指定为1,因为只需要一个信号量。如果是
用于同步时,需要多个信号量(≥2)。
semflg:设置同消息队列和共享内存。一般都设置为0664|IPC_CREAT。
返回值
调用成功则返回信号量集合的标识符,失败则返回-1,并且errno被设置。
semctl
原型
#include#include #include int semctl(int semid, int semnum, int cmd, ...);
功能
根据cmd的要求对集合中的各个信号量进行控制,...表示它是一个变参函数,如果第四个参数用不到的话,可以省略不写。
参数
semid:信号量标识符。通过标识符就能找到信号量集合。
semnum:集合中某个信号量的编号。信号量的编号为非负整数,而且是自动从0开始编号的。通过信号量编号就能找到集合中对应信号量,然后对这个具体的信号量进行控制操作。
cmd:控制选项。
IPC_STAT:将信号量的属性信息从内核读到第四个参数所以指定的struct semid_ds缓存中。
IPC_SET:修改属性信息,此时也会用到struct semid_ds结构体变量具体的修改方法同消息队列和共享内存。
IPC_RMID:删除信号量集合时,并不需要把所有的信号量都删除掉后才能删除,只需要指定semid和IPC_RMID就可以把整个信号量集合删除,其中第二个参数semnum没有被用到,所以semnum的值可以随便写,不过我们一般都是把它写为0。
所以删除整个信号量集合时,删除的写法可以统一的为:
semctl(semid, 0, IPC_RMID);
SETVAL:通过第四个参数,给集合中semnu编号的信号量设置一个int初始值。此时第四个参数就是那个int值
如果是二值信号量的话,设置初始值要么是0,要么是1;
如果信号量的目的是互斥的话,基本都是设置为1。当设置为1后,多个进程互斥操作时,那就是谁先运行就谁先操作。
如果是同步的话,初值是1还是0,这要就要看具体的情况了。
...:表示,如果用不到时可以省略不写。
第四个参数对应内容是变着的,为了应对这种变化就用到了一个联合体。
union semun { int val; struct semid_ds *buf; unsigned short *array; /* 不做要求 */ struct seminfo *__buf; /* 不做要求 */};
这个联合体类型并没有被定义在信号量相关的系统头文件中,我们使用这个联合体时,我们需要自己定义这个类型,至于联合体类型名可以自己定,不过一般都是直接沿用semun这个名字。
成员介绍:
val:存放用于初始化信号量的值
buf:存放struct semid_ds结构体变量的地址
这个联合怎么用?
当需要指定struct semid_ds缓存时
union semun sem_un; //定义一个联合体变量struct semid_ds buff; //定义一个struct semid_ds缓存sem_un.buf = &buff; //现在整个联合体的值就是buf中缩放的buff的地址 semctl(semid, 0, IPC_STAT, sem_un); //这里将联合体传递给semctl函数,其实就是将buff的地址传递给了semctl函数
当需要指定信号量的int初始值时
union semun sem_un; sem_un.val = 1; //现在整个联合体的值就是1 semctl(semid, 0, IPC_STAT, sem_un);
返回值
调用成功返回非-1值,失败则返回-1,errno被设置。
semop
原型
#include#include #include int semop(int semid, struct sembuf *sops, unsigned nsops);
功能
对指定的信号量进行p操作、或者是v操作。
p操作:将信号量的值-1,当信号量的值为0时,p操作默认是阻塞的。
v操作:将信号量的值+1,v操作不存在阻塞的问题。
参数
semid:信号量集合的标识符。
sops:这个参数更好理解的写法是struct sembuf sops[]。
struct sembuf{ unsigned short sem_num; short sem_op; short sem_flg; }
这个结构体不需要我们自己定义,因为在semop函数对应的头文件中已经定义了。
sem_num:信号量编号,决定对集合中哪一个信号量进行pv操作
sem_op:设置为-1,表示想-1进行p操作,设置1表示想+1进行v操作
sem_flg:
IPC_NOWAIT:一般情况下,当信号量的值为0时进行p操作的话,semop的p操作会阻塞。如果你不想阻塞的话,可以指定这个选项,NOWAIT就是不阻塞的意思。不过除非某些特殊情况,否则我们不需要设置为非阻塞。
SEM_UNDO:防止死锁。以二值信号量为例,当进程在v操作之前就结束时,信号量的值就会一直保持为0,那么其它进程将永远无法p操作成功,会使得进程永远休眠下去,这造成就是死锁。但是设置了SEM_UNDO选项后,如果进程在结束时没有V操作的话,OS会自动帮忙V操作,防止死锁。
nsops:用于指定数组struct sembuf sops[]元素个数。
返回值
调用成功返回0,失败则返回-1,errno被设置。
使用信号量实现互斥
在不使用信号量的时候
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 7 void print_err(char *estr) 8 { 9 perror(estr); 10 exit(-1);11 }12 13 int main(void)14 {15 pid_t ret=0;16 int fd=-1;17 18 fd=open("./file",O_RDWR|O_CREAT|O_TRUNC,0664);19 if(-1 == fd) print_err("open file fail");20 21 ret=fork();22 if(ret>0)23 {24 while(1)25 {26 write(fd,"hello ",6);27 write(fd,"world\n",6);28 }29 }30 else if(ret == 0)31 {32 while(1)33 {34 write(fd,"hhhhh ",6);35 write(fd,"wwwww\n",6);36 }37 }38 }
输出文件file里面的内容会比较乱,这种乱不是因为写文件时笔尖乱指导致数据覆盖,而是因为父子进程交叉执行,导致父子进程的数据都被隔断。参考文章:
file内容截取
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
hello worldhhhhh hello wwwwwworldhhhhh hello wwwwwworldhhhhh hello wwwwwworldhhhhh hello wwwwwworldhhhhh hello wwwwwworldhhhhh hello wwwwwworldhhhhh hello wwwwwworldhhhhh hello wwwwwworldhhhhh hello wwwww
——————————————————————————————————————————————————————————————————
引入信号量机制实现互质写文件
main.c
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include "semaphore.h" 9 10 11 12 #define NSEMS 113 14 int semid;15 16 17 void signal_fun(int signo)18 {19 del_sem(semid, NSEMS);20 exit(-1);21 }22 23 int main(void)24 {25 int i = 0;26 int ret = 0;27 int fd = -1;28 int semnum_buf[1] = { 0};29 30 fd = open("./file", O_RDWR|O_CREAT|O_TRUNC, 0664);31 if(fd == -1) print_err("open file fail");32 33 semid = creat_or_get_sem(NSEMS);34 35 for(i=0; i 0)42 {43 signal(SIGINT, signal_fun);44 while(1)45 {46 semnum_buf[0] = 0;//设置要操作的信号量的编号47 p_sem(semid, semnum_buf, 1); //P操作48 write(fd, "hello ", 6);49 write(fd, "world\n", 6);50 semnum_buf[0] = 0; //设置要操作的信号量的编号51 v_sem(semid, semnum_buf, 1);//v操作52 } 53 }54 else if(ret == 0)55 {56 while(1)57 {58 semnum_buf[0] = 0;//设置要操作的信号量的编号59 p_sem(semid, semnum_buf, 1); //P操作60 write(fd, "hhhhh ", 6);61 write(fd, "wwwww\n", 6);62 semnum_buf[0] = 0; //设置要操作的信号量的编号63 v_sem(semid, semnum_buf, 1);//v操作64 } 65 }66 67 return 0;68 }
sem.c
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 11 union semun 12 { 13 int val; 14 struct semid_ds *buf; 15 unsigned short *array; /* 不做要求 */ 16 struct seminfo *__buf; /* 不做要求 */ 17 }; 18 19 #define SEM_FILE "./semfile" 20 21 void print_err(char *estr) 22 { 23 perror(estr); 24 //exit(-1); 25 } 26 27 int creat_or_get_sem(int nsems) 28 { 29 int semid; 30 int fd = -1; 31 key_t key = -1; 32 33 fd = open(SEM_FILE, O_RDWR|O_CREAT, 0664); 34 if(fd == -1) print_err("open ./semfile fail"); 35 36 key = ftok(SEM_FILE, 'a'); 37 if(key == -1) print_err("ftok fail"); 38 39 semid = semget(key, nsems, 0664|IPC_CREAT); 40 if(semid == -1) print_err("semget fail"); 41 42 return semid; 43 } 44 45 void init_sem(int semid, int semnum, int val) 46 { 47 int ret = -1; 48 union semun sem_un; 49 50 /* semnum:信号量编号 51 * SETVAL:设置信号量初始值cmd 52 * sem_un:初始值 53 */ 54 sem_un.val = val; 55 ret = semctl(semid, semnum, SETVAL, sem_un); 56 if(ret == -1) print_err("semctl fail"); 57 } 58 59 60 void del_sem(int semid, int nsems) 61 { 62 int ret = 0; 63 int i = 0; 64 65 for(i=0; i
sem.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #ifndef H_SEM_H 2 #define H_SEM_H 3 4 extern void print_err(char *estr); 5 extern int creat_or_get_sem(int nsems); 6 extern void init_sem(int semid, int semnum, int val); 7 extern void del_sem(int semid, int nsems); 8 extern void p_sem(int semid, int semnum_buf[], int nsops); 9 extern void v_sem(int semid, int semnum_buf[], int nsops);10 11 #endif
输出文件file
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
hello worldhhhhh wwwwwhello worldhhhhh wwwwwhello worldhhhhh wwwwwhello worldhhhhh wwwwwhello worldhhhhh wwwww
——————————————————————————————————————————————————————————————————
通过同步让三个亲缘进程按照顺序打印出111111、222222、333333
mian.c
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include "semaphore.h" 9 10 11 12 #define NSEMS 313 14 int semid;15 16 17 void signal_fun(int signo)18 {19 del_sem(semid, NSEMS);20 exit(-1);21 }22 23 int main(void)24 {25 int i = 0;26 int ret = 0;27 int fd = -1;28 int semnum_buf[1] = { 0};29 30 //创建信号量集合31 semid = creat_or_get_sem(NSEMS);32 33 //初始化信号量集合中的每个信号量34 for(i=0; i 0)42 {43 ret = fork();44 if(ret > 0) //父进程45 {46 while(1)47 { 48 semnum_buf[0] = 2;49 p_sem(semid, semnum_buf, 1);50 printf("333333\n");51 sleep(1);52 semnum_buf[0] = 0;53 v_sem(semid, semnum_buf, 1);54 55 } 56 }57 else if(ret == 0) //子进程258 {59 while(1)60 {61 semnum_buf[0] = 1;62 p_sem(semid, semnum_buf, 1);63 printf("222222\n");64 sleep(1);65 semnum_buf[0] = 2;66 v_sem(semid, semnum_buf, 1);67 } 68 }69 }70 else if(ret == 0)//子进程171 {72 signal(SIGINT, signal_fun);73 while(1)74 {75 semnum_buf[0] = 0;76 p_sem(semid, semnum_buf, 1);77 printf("111111\n");78 sleep(1);79 semnum_buf[0] = 1;80 v_sem(semid, semnum_buf, 1);81 } 82 }83 84 return 0;85 }
sem.c
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 11 union semun 12 { 13 int val; 14 struct semid_ds *buf; 15 unsigned short *array; /* 不做要求 */ 16 struct seminfo *__buf; /* 不做要求 */ 17 }; 18 19 #define SEM_FILE "./semfile" 20 21 void print_err(char *estr) 22 { 23 perror(estr); 24 //exit(-1); 25 } 26 27 int creat_or_get_sem(int nsems) 28 { 29 int semid; 30 int fd = -1; 31 key_t key = -1; 32 33 fd = open(SEM_FILE, O_RDWR|O_CREAT, 0664); 34 if(fd == -1) print_err("open ./semfile fail"); 35 36 key = ftok(SEM_FILE, 'a'); 37 if(key == -1) print_err("ftok fail"); 38 39 semid = semget(key, nsems, 0664|IPC_CREAT); 40 if(semid == -1) print_err("semget fail"); 41 42 return semid; 43 } 44 45 void init_sem(int semid, int semnum, int val) 46 { 47 int ret = -1; 48 union semun sem_un; 49 50 /* semnum:信号量编号 51 * SETVAL:设置信号量初始值cmd 52 * sem_un:初始值 53 */ 54 sem_un.val = val; 55 ret = semctl(semid, semnum, SETVAL, sem_un); 56 if(ret == -1) print_err("semctl fail"); 57 } 58 59 60 void del_sem(int semid, int nsems) 61 { 62 int ret = 0; 63 64 //第二个参数没用上。 65 ret = semctl(semid, 0, IPC_RMID); 66 if(ret == -1) print_err("semctl del sem fail"); 67 68 remove(SEM_FILE); 69 } 70 71 void p_sem(int semid, int semnum_buf[], int nsops) 72 { 73 int i = 0; 74 int ret = -1; 75 struct sembuf sops[nsops]; 76 77 for(i=0; i
sem.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #ifndef H_SEM_H 2 #define H_SEM_H 3 4 extern void print_err(char *estr); 5 extern int creat_or_get_sem(int nsems); 6 extern void init_sem(int semid, int semnum, int val); 7 extern void del_sem(int semid, int nsems); 8 extern void p_sem(int semid, int semnum_buf[], int nsops); 9 extern void v_sem(int semid, int semnum_buf[], int nsops);10 11 #endif
——————————————————————————————————————————————————————————————————
使用信号量来解决共享内存的同步问题
shm1.c
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include "semaphore.h" 15 16 17 18 #define SHM_FILE "./shmfile" 19 20 #define SHM_SIZE 4096 21 22 int shmid = -1; 23 int semid = -1; 24 void *shmaddr = NULL; 25 26 27 28 static void print_err(char *estr) 29 { 30 perror(estr); 31 exit(-1); 32 } 33 34 void create_or_get_shm(void) 35 { 36 int fd = 0; 37 key_t key = -1; 38 39 fd = open(SHM_FILE, O_RDWR|O_CREAT, 0664); 40 if(fd == -1) print_err("open fail"); 41 42 key = ftok(SHM_FILE, 'b'); 43 if(key == -1) print_err("ftok fail"); 44 45 shmid = shmget(key, SHM_SIZE, 0664|IPC_CREAT); 46 if(shmid == -1) print_err("shmget fail"); 47 48 //write(fd, &shmid, sizeof(shmid)); 49 } 50 51 char buf[300] = { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ 52 222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222\ 53 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2222222222"}; 54 55 void signal_fun(int signo) 56 { 57 shmdt(shmaddr); 58 shmctl(shmid, IPC_RMID, NULL); 59 60 del_sem(semid);//删除信号量集合 61 62 remove("./fifo"); 63 remove(SHM_FILE); 64 65 exit(-1); 66 } 67 68 69 70 int main(void) 71 { 72 int peer_pid = -1; 73 74 /* 给SIGINT信号注册捕获函数,用于删除共享内存、管道、文件等 */ 75 signal(SIGINT, signal_fun); 76 77 78 /* 创建、或者获取共享内存 */ 79 create_or_get_shm(); 80 81 //创建信号量集合 82 semid = creat_or_get_sem(2); 83 84 /* 初始化信号量集合 */ 85 int i = 0; 86 for(i=0; i<2; i++) 87 { 88 //将编号0的信号量初始化为1,其它初始化为0 89 if(i == 0) init_sem(semid, i, 1); 90 else init_sem(semid, i, 0); 91 } 92 93 //建立映射 94 shmaddr = shmat(shmid, NULL, 0); 95 if(shmaddr == (void *)-1) print_err("shmat fail"); 96 97 int semnum_buf[1] = { 0};//存放信号量的编号 98 while(1) 99 { 100 //p sem 0101 semnum_buf[0] = 0;102 p_sem(semid, semnum_buf, 1);103 104 /* 向共享内存写数据 */105 memcpy(shmaddr, buf, sizeof(buf));106 sleep(1);107 108 //v sem 1109 semnum_buf[0] = 1;110 v_sem(semid, semnum_buf, 1);111 }112 113 return 0;114 }
shm2.c
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include "semaphore.h"15 16 #define SHM_FILE "./shmfile"17 18 #define SHM_SIZE 409619 20 int shmid = -1;21 int semid = -1;22 void *shmaddr = NULL; 23 24 25 26 void print_err(char *estr)27 {28 perror(estr); 29 exit(-1);30 }31 32 void create_or_get_shm(void)33 {34 int fd = 0;35 key_t key = -1; 36 37 fd = open(SHM_FILE, O_RDWR|O_CREAT, 0664);38 if(fd == -1) print_err("open fail");39 40 key = ftok(SHM_FILE, 'b');41 if(key == -1) print_err("ftok fail");42 43 shmid = shmget(key, SHM_SIZE, 0664|IPC_CREAT);44 if(shmid == -1) print_err("shmget fail");45 46 //read(fd, &shmid, sizeof(shmid));47 }48 49 void signal_fun(int signo)50 {51 if(SIGINT == signo)52 {53 shmdt(shmaddr);54 shmctl(shmid, IPC_RMID, NULL);55 remove("./fifo");56 remove(SHM_FILE);57 58 exit(-1);59 }60 else if(SIGUSR1 == signo)61 {62 63 }64 }65 66 int main(void)67 {68 signal(SIGINT, signal_fun);69 70 /* 创建、或者获取共享内存 */71 create_or_get_shm();72 73 //获取别人创建号的信号量74 semid = creat_or_get_sem(2);75 76 //建立映射77 shmaddr = shmat(shmid, NULL, 0);78 if(shmaddr == (void *)-1) print_err("shmat fail"); 79 80 int semnum_buf[1] = { 0};//存放信号量编号 81 while(1)82 {83 //p sem 184 semnum_buf[0] = 1;85 p_sem(semid, semnum_buf, 1);86 87 //从共享内存去除数据并打印显示88 printf("%s\n", (char *)shmaddr);89 bzero(shmaddr, SHM_SIZE);//清空共享内存90 91 //v sem 092 semnum_buf[0] = 0;93 v_sem(semid, semnum_buf, 1);94 }95 96 return 0;97 }
sem.c
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 11 union semun 12 { 13 int val; 14 struct semid_ds *buf; 15 unsigned short *array; /* 不做要求 */ 16 struct seminfo *__buf; /* 不做要求 */ 17 }; 18 19 #define SEM_FILE "./semfile" 20 21 static void print_err(char *estr) 22 { 23 perror(estr); 24 //exit(-1); 25 } 26 27 int creat_or_get_sem(int nsems) 28 { 29 int semid; 30 int fd = -1; 31 key_t key = -1; 32 33 fd = open(SEM_FILE, O_RDWR|O_CREAT, 0664); 34 if(fd == -1) print_err("open ./semfile fail"); 35 36 key = ftok(SEM_FILE, 'a'); 37 if(key == -1) print_err("ftok fail"); 38 39 semid = semget(key, nsems, 0664|IPC_CREAT); 40 if(semid == -1) print_err("semget fail"); 41 42 return semid; 43 } 44 45 void init_sem(int semid, int semnum, int val) 46 { 47 int ret = -1; 48 union semun sem_un; 49 50 /* semnum:信号量编号 51 * SETVAL:设置信号量初始值cmd 52 * sem_un:初始值 53 */ 54 sem_un.val = val; 55 ret = semctl(semid, semnum, SETVAL, sem_un); 56 if(ret == -1) print_err("semctl fail"); 57 } 58 59 60 void del_sem(int semid) 61 { 62 int ret = 0; 63 64 ret = semctl(semid, 0, IPC_RMID); 65 if(ret == -1) print_err("semctl del sem fail"); 66 67 remove(SEM_FILE); 68 } 69 70 void p_sem(int semid, int semnum_buf[], int nsops) 71 { 72 int i = 0; 73 int ret = -1; 74 struct sembuf sops[nsops]; 75 76 for(i=0; i
sem.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #ifndef H_SEM_H 2 #define H_SEM_H 3 4 extern int creat_or_get_sem(int nsems); 5 extern void init_sem(int semid, int semnum, int val); 6 extern void del_sem(int semid); 7 extern void p_sem(int semid, int semnum_buf[], int nsops); 8 extern void v_sem(int semid, int semnum_buf[], int nsops); 9 10 #endif