Fork me on GitHub

IPC——信号量

信号量作用

当多个进程/线程进行共享操作时,用于资源保护,以防止出现相互干扰的情况。再间简洁一点,信号量用于“资源的保护“。

进程信号量:实现的是进程所操作资源的保护。

线程信号量:实现的是线程所操作资源的保护。

疑问:资源保护,这个“资源”到底指的是谁?

:这个资源指的就是你操作的数据,保护的目的就是不要出现相互干扰,导致紊乱和错误数据的产生。

 

资源保护操作的种类

资源保护的操作分两种,一种叫互斥,另一个种叫同步。有些资源保护机制只能实现互斥,不能实现同步,比如文件锁。但是这里讲的信号量既能实现互斥,也能实现同步。

互斥

对于互斥操作来说,多进程共享操作时,多个进程间不关心谁先操作、谁后操作的先后顺序问题,它们只关心一件事,那就是我在操作时别人不能操作。互斥时,如果当前正在操作的进程,他的时间片到了,切换到了其他进程上,但是当该进程检测到上一个进程还没有操作完时,该进程在当前的时间片内会休眠,直到再次切换会上一个进程,将操作完成后再切换回来,此时才能进行操作。这跟上厕所时把门关起来是一样的,我在蹲坑时你不能蹲,你在蹲坑时我不能蹲,这就是互斥,至于蹲坑先后顺序并没有要求。

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 <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>                        
int semget(key_t key, int nsems, int semflg);

功能

根据key值创建新的、或者获取已有的信号量集合,并返回其标识符。

实现互斥时:集合中只需要一个信号量

实现同步时:集合中需要多个信号量 

参数

key:设置同消息队列和共享内存。一般都使用ftok获取key值。

nsems:指定集合中信号量的个数。

用于互斥时,数量都指定为1,因为只需要一个信号量。如果是

用于同步时,需要多个信号量(≥2)。

semflg:设置同消息队列和共享内存。一般都设置为0664|IPC_CREAT。

返回值

调用成功则返回信号量集合的标识符,失败则返回-1,并且errno被设置。

semctl

原型 

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>                           
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 <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>                            
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被设置。

 

使用信号量实现互斥 

在不使用信号量的时候

 1 #include <stdlib.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <fcntl.h>
 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 }    
View Code

输出文件file里面的内容会比较乱,这种乱不是因为写文件时笔尖乱指导致数据覆盖,而是因为父子进程交叉执行,导致父子进程的数据都被隔断。参考文章:初级文件IO——若干种文件共享操作 如何影响 文件文件描述符表

file内容截取

hello world
hhhhh hello wwwww
world
hhhhh hello wwwww
world
hhhhh hello wwwww
world
hhhhh hello wwwww
world
hhhhh hello wwwww
world
hhhhh hello wwwww
world
hhhhh hello wwwww
world
hhhhh hello wwwww
world
hhhhh hello wwwww
View Code

——————————————————————————————————————————————————————————————————

引入信号量机制实现互质写文件

main.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <sys/stat.h>
 6 #include <fcntl.h>
 7 #include <signal.h>
 8 #include "semaphore.h"
 9 
10 
11 
12 #define NSEMS 1
13 
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<NSEMS; i++)
36     {
37         init_sem(semid, i, 1);
38     }
39 
40     ret = fork();
41     if(ret > 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 }
View Code

sem.c

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/types.h>
  5 #include <sys/stat.h>
  6 #include <fcntl.h>
  7 #include <sys/types.h>
  8 #include <sys/ipc.h>
  9 #include <sys/sem.h>
 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<nsems; i++)
 66     {    
 67         ret = semctl(semid, i, IPC_RMID);
 68         if(ret == -1) print_err("semctl del sem fail");
 69     }
 70 
 71     remove(SEM_FILE);
 72 }
 73 
 74 void p_sem(int semid, int semnum_buf[], int nsops)
 75 {
 76     int i = 0;
 77     int ret = -1;
 78     struct sembuf sops[nsops];
 79     
 80     for(i=0; i<nsops; i++)
 81     {
 82         sops[i].sem_num = semnum_buf[i];//信号量编号
 83         sops[i].sem_op     = -1;//-1 p操作
 84         sops[i].sem_flg = SEM_UNDO;//防止死锁 
 85     }
 86 
 87     ret = semop(semid, sops, nsops);
 88     if(ret == -1) print_err("semop p fail");
 89 }
 90 
 91 
 92 void v_sem(int semid, int semnum_buf[], int nsops)
 93 {
 94     int i = 0;
 95     int ret = -1;
 96     struct sembuf sops[nsops];
 97     
 98     for(i=0; i<nsops; i++)
 99     {
100         sops[i].sem_num = semnum_buf[i];//信号量编号
101         sops[i].sem_op     = 1;//+1 v操作
102         sops[i].sem_flg = SEM_UNDO;//防止死锁 
103     }
104 
105     ret = semop(semid, sops, nsops);
106     if(ret == -1) print_err("semop p fail");
107 }
View Code

sem.h

 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
View Code

输出文件file

hello world
hhhhh wwwww
hello world
hhhhh wwwww
hello world
hhhhh wwwww
hello world
hhhhh wwwww
hello world
hhhhh wwwww
View Code

 ——————————————————————————————————————————————————————————————————

通过同步让三个亲缘进程按照顺序打印出111111、222222、333333

mian.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/types.h>
 5 #include <sys/stat.h>
 6 #include <fcntl.h>
 7 #include <signal.h>
 8 #include "semaphore.h"
 9 
10 
11 
12 #define NSEMS 3
13 
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<NSEMS; i++)
35     {
36         if(i == 0) init_sem(semid, i, 1);
37         else init_sem(semid, i, 0);
38     }
39 
40     ret = fork();
41     if(ret > 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) //子进程2
58           {
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)//子进程1
71     {
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 }
View Code

sem.c

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/types.h>
  5 #include <sys/stat.h>
  6 #include <fcntl.h>
  7 #include <sys/types.h>
  8 #include <sys/ipc.h>
  9 #include <sys/sem.h>
 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<nsops; i++)
 78     {
 79         sops[i].sem_num = semnum_buf[i];//信号量编号
 80         sops[i].sem_op     = -1;//-1 p操作
 81         sops[i].sem_flg = SEM_UNDO;//防止死锁 
 82     }
 83 
 84     ret = semop(semid, sops, nsops);
 85     if(ret == -1) print_err("semop p fail");
 86 }
 87 
 88 
 89 void v_sem(int semid, int semnum_buf[], int nsops)
 90 {
 91     int i = 0;
 92     int ret = -1;
 93     struct sembuf sops[nsops];
 94     
 95     for(i=0; i<nsops; i++)
 96     {
 97         sops[i].sem_num = semnum_buf[i];//信号量编号
 98         sops[i].sem_op     = 1;//+1 v操作
 99         sops[i].sem_flg = SEM_UNDO;//防止死锁 
100     }
101 
102     ret = semop(semid, sops, nsops);
103     if(ret == -1) print_err("semop p fail");
104 }
View Code

sem.h

 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
View Code

——————————————————————————————————————————————————————————————————

使用信号量来解决共享内存的同步问题

shm1.c

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/ipc.h>
  5 #include <sys/types.h>
  6 #include <sys/shm.h>
  7 #include <sys/types.h>
  8 #include <sys/stat.h>
  9 #include <fcntl.h>
 10 #include <strings.h>
 11 #include <string.h>
 12 #include <signal.h>
 13 #include <errno.h>
 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 0
101         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 1
109         semnum_buf[0] = 1;
110         v_sem(semid, semnum_buf, 1);
111     }
112     
113     return 0;
114 }
View Code

shm2.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/ipc.h>
 5 #include <sys/types.h>
 6 #include <sys/shm.h>
 7 #include <sys/types.h>
 8 #include <sys/stat.h>
 9 #include <fcntl.h>
10 #include <strings.h>
11 #include <string.h>
12 #include <signal.h>
13 #include <errno.h>
14 #include "semaphore.h"
15 
16 #define SHM_FILE "./shmfile"
17 
18 #define SHM_SIZE 4096
19 
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 1
84         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 0
92         semnum_buf[0] = 0;
93         v_sem(semid, semnum_buf, 1);
94     }
95     
96     return 0;
97 }
View Code

sem.c

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/types.h>
  5 #include <sys/stat.h>
  6 #include <fcntl.h>
  7 #include <sys/types.h>
  8 #include <sys/ipc.h>
  9 #include <sys/sem.h>
 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<nsops; i++)
 77     {
 78         sops[i].sem_num = semnum_buf[i];//信号量编号
 79         sops[i].sem_op     = -1;//-1 p操作
 80         sops[i].sem_flg = SEM_UNDO;//防止死锁 
 81     }
 82 
 83     ret = semop(semid, sops, nsops);
 84     if(ret == -1) print_err("semop p fail");
 85 }
 86 
 87 
 88 void v_sem(int semid, int semnum_buf[], int nsops)
 89 {
 90     int i = 0;
 91     int ret = -1;
 92     struct sembuf sops[nsops];
 93     
 94     for(i=0; i<nsops; i++)
 95     {
 96         sops[i].sem_num = semnum_buf[i];//信号量编号
 97         sops[i].sem_op     = 1;//+1 v操作
 98         sops[i].sem_flg = SEM_UNDO;//防止死锁 
 99     }
100 
101     ret = semop(semid, sops, nsops);
102     if(ret == -1) print_err("semop p fail");
103 }
View Code

sem.h

 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
View Code

 

 

 

 

 

posted @ 2018-07-31 11:15  克拉默与矩阵  阅读(808)  评论(0编辑  收藏  举报