进程间通信第二课--信号量 共享内存 消息队列

信号量

程序中存在一部分临界代码,要确保只有一个进程(或一个执行线程)可以进入临界区代码,并拥有对资源的独占式访问权

我们需要一种方法,通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域

这里讲的信号量比在线程的调用中使用的互斥量和信号量更加通用

P:等待,好像位于进入临界区域之前的检查点

V:给予或者释放,好像放弃对临界区域的控制权

P(sv):要是sv的值大于零,就减去1,如果它的值等于零,就挂起该进程的执行

V(sv):要是其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就把sv加1

【或者是这样的情况,当临界区域可用时,信号量sv的值是true,然后P(sv)操作将它减1使它变为false以表示临界区域正在被使用;

当进程离开临界区域时,使用V(sv)操作将它加1,使临界区域再次变为可用,但是任何时候只有一个进程或者线程进入临界区域】

1 int semget(key_t key, int num_sems, int sem_flags);

函数作用是创建一个信号量或取得一个已有信号量的键

1 int semop(int sem_id, struct sembuf * sem_ops, size_t num_sem_ops);

函数的作用是用于改变信号量的值

1 int semctl(int sem_id, int sem_num, int command,...);

函数的作用是允许我们直接控制信号量信息

使用信号量的例程:

  1 /* After the #includes, the function prototypes and the global variable, we come to the
  2  main function. There the semaphore is created with a call to semget, which returns the
  3  semaphore ID. If the program is the first to be called (i.e. it‘s called with a parameter
  4  and argc > 1), a call is made to set_semvalue to initialize the semaphore and op_char is
  5  set to X. */
  6
  7 #include <unistd.h>
  8 #include <stdlib.h>
  9 #include <stdio.h>
 10
 11 #include <sys/types.h>
 12 #include <sys/ipc.h>
 13 #include <sys/sem.h>
 14
 15 #include "semun.h"//包含必要的头文件
 16
 17 static int set_semvalue(void);//声明了函数原形
 18 static void del_semvalue(void);
 19 static int semaphore_p(void);
 20 static int semaphore_v(void);
 21
 22 static int sem_id;//定义全局变量
 23
 24
 25 int main(int argc, char *argv[])
 26 {
 27     int i;
 28     int pause_time;
 29     char op_char = ‘O‘;
 30
 31     srand((unsigned int)getpid());
 32
 33     sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);//获取一个信号量要是不存在就创建
 34
 35     if (argc > 1) {//要是函数带有参数
 36         if (!set_semvalue()) {
 37             fprintf(stderr, "Failed to initialize semaphore\n");
 38             exit(EXIT_FAILURE);
 39         }
 40         op_char = ‘X‘;//设置信号量为X
 41         sleep(2);
 42     }
 43
 44 /* Then we have a loop which enters and leaves the critical section ten times.
 45  There, we first make a call to semaphore_p which sets the semaphore to wait, as
 46  this program is about to enter the critical section. */
 47
 48     for(i = 0; i < 10; i++) {
 49
 50         if (!semaphore_p()) exit(EXIT_FAILURE);//调用这个函数,程序将进入临界区域,设置信号量以等待进入。
 51         printf("%c", op_char);fflush(stdout);
 52         pause_time = rand() % 3;//等待随机时间
 53         sleep(pause_time);
 54         printf("%c", op_char);fflush(stdout);
 55
 56 /* After the critical section, we call semaphore_v, setting the semaphore available,
 57  before going through the for loop again after a random wait. After the loop, the call
 58  to del_semvalue is made to clean up the code. */
 59
 60         if (!semaphore_v()) exit(EXIT_FAILURE);//调用这个函数,将信号量设置为可用,然后等待一段随机时间
 61         //相当于离开临界区
 62         pause_time = rand() % 2;
 63         sleep(pause_time);
 64     }    //循环十次
 65
 66     printf("\n%d - finished\n", getpid());
 67
 68     if (argc > 1) {
 69         sleep(10);
 70         del_semvalue();//整循环执行完毕之后,调用这个函数去清理代码
 71     }
 72
 73     exit(EXIT_SUCCESS);
 74 }
 75
 76 /* The function set_semvalue initializes the semaphore using the SETVAL command in a
 77  semctl call. We need to do this before we can use the semaphore. */
 78
 79 static int set_semvalue(void)
 80 {
 81     union semun sem_union;
 82
 83     sem_union.val = 1;//设置信号量的值,在使用信号量之前必须这样做
 84     if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0);
 85     return(1);
 86 }
 87
 88 /* The del_semvalue function has almost the same form, except the call to semctl uses
 89  the command IPC_RMID to remove the semaphore‘s ID. */
 90
 91 static void del_semvalue(void)//删除信号量ID
 92 {
 93     union semun sem_union;
 94
 95     if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
 96         fprintf(stderr, "Failed to delete semaphore\n");
 97 }
 98
 99 /* semaphore_p changes the semaphore by -1 (waiting). */
100
101 static int semaphore_p(void)//对信号量做减1操作
102 {
103     struct sembuf sem_b;
104
105     sem_b.sem_num = 0;
106     sem_b.sem_op = -1; /* P() */
107     sem_b.sem_flg = SEM_UNDO;
108     if (semop(sem_id, &sem_b, 1) == -1) {
109         fprintf(stderr, "semaphore_p failed\n");
110         return(0);
111     }
112     return(1);
113 }
114
115 /* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1,
116  so that the semaphore becomes available. */
117
118 static int semaphore_v(void)//使得信号量可用
119 {
120     struct sembuf sem_b;
121
122     sem_b.sem_num = 0;
123     sem_b.sem_op = 1; /* V() */
124     sem_b.sem_flg = SEM_UNDO;
125     if (semop(sem_id, &sem_b, 1) == -1) {
126         fprintf(stderr, "semaphore_v failed\n");
127         return(0);
128     }
129     return(1);
130 }

如果程序启动时带有一个参数,它将在进入和退出临界区域时打印字符X

而程序的其他实例将在进入和退出临界区域时打印字符o

因为在任一时刻,只能有一个进程可以进入临界区域,所以字符x和o应该是成对出现的

执行的效果:

1 [email protected]:~/c_program/544977-blp3e/chapter14$ ./sem1 qwqw & ./sem1
2 [2] 22102
3 OOOOXXOOXXOOXXXXXXOOXXOOXXOOXXOOXXOOOO
4 22103 - finished
5 X[1]   已完成               ./sem1 1
6 jaso[email protected]:~/c_program/544977-blp3e/chapter14$ X
7 22102 - finished
8
9 [email protected]:~/c_program/544977-blp3e/chapter14$ 

共享内存:

允许两个不相关的进程访问同一个逻辑内存

是在两个正在运行的进程之间传递数据的一种非常有效的方式

为在多个进程之间共享和传递数据提供了一种非常有效的方式

共享内存本身并未提供任何同步机制,在第一个进程结束对共享内存的写操作之前,并无自动的机制可以阻止第二个进程开始对它进行读取

对共享内存访问的同步控制必须由程序员来负责。

1 int shmget(key_t key, size_t size, int shmflg);

函数的作用是用来创建共享内存

1 void * shmat(int shm_id, const void * shm_addr, int shmflg);

函数的作用是保证第一次创建共享内存时,不能被任何进程访问,要想启用对共享内存的访问,必须将其连接到一个进程的地址空间

1 int shmdt(const void * shm_addr);

函数的作用是将共享内存凑从当前进程中分离,不是删除。成功返回0

1 int shmctl(int shm_id, int command, struct shmid_ds * buf);

函数的作用是对共享内存的控制

共享内存的例程:

shm_com.h

1 /* A common header file to describe the shared memory we wish to pass about. */
2
3 #define TEXT_SZ 2048//2k
4 //创建一个公共的头文件,定义我们希望分发的共享内存
5 struct shared_use_st {
6     int written_by_you;//通知消费数据的程序
7     char some_text[TEXT_SZ];
8 };

shm1.c

 1 /* Our first program is a consumer. After the headers the shared memory segment
 2  (the size of our shared memory structure) is created with a call to shmget,
 3  with the IPC_CREAT bit specified. */
 4
 5 #include <unistd.h>
 6 #include <stdlib.h>
 7 #include <stdio.h>
 8 #include <string.h>
 9
10 #include <sys/types.h>
11 #include <sys/ipc.h>
12 #include <sys/shm.h>
13
14 #include "shm_com.h"
15
16 int main()
17 {
18     int running = 1;
19     void *shared_memory = (void *)0;
20     struct shared_use_st *shared_stuff;
21     int shmid;
22
23     srand((unsigned int)getpid());
24
25     shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);//创建共享内存段
26
27     if (shmid == -1) {
28         fprintf(stderr, "shmget failed\n");
29         exit(EXIT_FAILURE);
30     }
31
32 /* We now make the shared memory accessible to the program. */
33
34     shared_memory = shmat(shmid, (void *)0, 0);//让程序可以访问这个共享内存
35     if (shared_memory == (void *)-1) {
36         fprintf(stderr, "shmat failed\n");
37         exit(EXIT_FAILURE);
38     }
39
40     printf("Memory attached at %X\n", (int)shared_memory);
41
42 /* The next portion of the program assigns the shared_memory segment to shared_stuff,
43  which then prints out any text in written_by_you. The loop continues until end is found
44  in written_by_you. The call to sleep forces the consumer to sit in its critical section,
45  which makes the producer wait. */
46
47     shared_stuff = (struct shared_use_st *)shared_memory;//将共享内存段分配到shared_stuff
48     shared_stuff->written_by_you = 0;//设置这个量为0
49     while(running) {
50         if (shared_stuff->written_by_you) {
51             printf("You wrote: %s", shared_stuff->some_text);
52             sleep( rand() % 4 ); /* make the other process wait for us ! */
53             shared_stuff->written_by_you = 0;
54             if (strncmp(shared_stuff->some_text, "end", 3) == 0) {
55                 running = 0;//程序一直执行到some_text中有end字符串为止
56             }
57         }
58     }
59
60 /* Lastly, the shared memory is detached and then deleted. */
61
62     if (shmdt(shared_memory) == -1) {//分离共享内存
63         fprintf(stderr, "shmdt failed\n");
64         exit(EXIT_FAILURE);
65     }
66
67     if (shmctl(shmid, IPC_RMID, 0) == -1) {//删除共享内存
68         fprintf(stderr, "shmctl(IPC_RMID) failed\n");
69         exit(EXIT_FAILURE);
70     }
71
72     exit(EXIT_SUCCESS);
73 }

shm2.c

 1 /* The second program is the producer and allows us to enter data for consumers.
 2  It‘s very similar to shm1.c and looks like this. */
 3
 4 #include <unistd.h>
 5 #include <stdlib.h>
 6 #include <stdio.h>
 7 #include <string.h>
 8
 9 #include <sys/types.h>
10 #include <sys/ipc.h>
11 #include <sys/shm.h>
12
13 #include "shm_com.h"
14
15 int main()
16 {
17     int running = 1;
18     void *shared_memory = (void *)0;
19     struct shared_use_st *shared_stuff;
20     char buffer[BUFSIZ];
21     int shmid;
22
23     shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);//连接到一个共享内存段
24
25     if (shmid == -1) {
26         fprintf(stderr, "shmget failed\n");
27         exit(EXIT_FAILURE);
28     }
29
30     shared_memory = shmat(shmid, (void *)0, 0);
31     if (shared_memory == (void *)-1) {
32         fprintf(stderr, "shmat failed\n");
33         exit(EXIT_FAILURE);
34     }
35
36     printf("Memory attached at %X\n", (int)shared_memory);
37
38     shared_stuff = (struct shared_use_st *)shared_memory;
39     while(running) {
40         while(shared_stuff->written_by_you == 1) {
41             sleep(1);            //这个量被设置说明客户端还未完成上一段的读写,继续等待
42             printf("waiting for client...\n");
43         }//要是written_by_you清理之后,
44         printf("Enter some text: ");
45         fgets(buffer, BUFSIZ, stdin);//读入新数据,
46
47         strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
48         shared_stuff->written_by_you = 1;
49
50         if (strncmp(buffer, "end", 3) == 0) {
51                 running = 0;//end匹配之后结束循环
52         }
53     }
54
55     if (shmdt(shared_memory) == -1) {//分离共享内存段
56         fprintf(stderr, "shmdt failed\n");
57         exit(EXIT_FAILURE);
58     }
59     exit(EXIT_SUCCESS);
60 }

程序的执行的效果:

 1 [email protected]:~/c_program/544977-blp3e/chapter14$ ./shm1 & ./shm2
 2 [1] 1704
 3 Memory attached at B77CF000
 4 Enter some text: Memory attached at B7725000
 5 hello
 6 You wrote: hello
 7 waiting for client...
 8 Enter some text: world
 9 You wrote: world
10 waiting for client...
11 waiting for client...
12 Enter some text: ende
13 You wrote: ende
14 [email protected]:~/c_program/544977-blp3e/chapter14$ 

消息队列:

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法

每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据块

msg1.c

 1 /* Here‘s the receiver program. */
 2
 3 #include <stdlib.h>
 4 #include <stdio.h>
 5 #include <string.h>
 6 #include <errno.h>
 7 #include <unistd.h>
 8
 9 #include <sys/types.h>
10 #include <sys/ipc.h>
11 #include <sys/msg.h>
12
13
14 struct my_msg_st {
15     long int my_msg_type;
16     char some_text[BUFSIZ];
17 };
18
19 int main()
20 {
21     int running = 1;
22     int msgid;
23     struct my_msg_st some_data;
24     long int msg_to_receive = 0;
25
26 /* First, we set up the message queue. */
27
28     msgid = msgget((key_t)1234, 0666 | IPC_CREAT);//创建消息队列
29
30     if (msgid == -1) {
31         fprintf(stderr, "msgget failed with error: %d\n", errno);
32         exit(EXIT_FAILURE);
33     }
34
35 /* Then the messages are retrieved from the queue, until an end message is encountered.
36  Lastly, the message queue is deleted. */
37
38     while(running) {
39         if (msgrcv(msgid, (void *)&some_data, BUFSIZ,
40               msg_to_receive, 0) == -1) {
41             fprintf(stderr, "msgrcv failed with error: %d\n", errno);
42             exit(EXIT_FAILURE);//从消息队列中获取消息
43         }
44         printf("You wrote: %s", some_data.some_text);
45         if (strncmp(some_data.some_text, "end", 3) == 0) {
46             running = 0;//直到遇到end为止
47         }
48     }
49
50     if (msgctl(msgid, IPC_RMID, 0) == -1) {
51         fprintf(stderr, "msgctl(IPC_RMID) failed\n");
52         exit(EXIT_FAILURE);//删除消息队列
53     }
54
55     exit(EXIT_SUCCESS);
56 }

msg2.c

 1 /* The sender program is very similar to msg1.c. In the main set up, delete the
 2  msg_to_receive declaration and replace it with buffer[BUFSIZ], remove the message
 3  queue delete and make the following changes to the running loop.
 4  We now have a call to msgsnd to send the entered text to the queue. */
 5
 6 #include <stdlib.h>
 7 #include <stdio.h>
 8 #include <string.h>
 9 #include <errno.h>
10 #include <unistd.h>
11
12 #include <sys/types.h>
13 #include <sys/ipc.h>
14 #include <sys/msg.h>
15
16 #define MAX_TEXT 512
17
18 struct my_msg_st {
19     long int my_msg_type;
20     char some_text[MAX_TEXT];
21 };
22
23 int main()
24 {
25     int running = 1;
26     struct my_msg_st some_data;
27     int msgid;
28     char buffer[BUFSIZ];
29
30     msgid = msgget((key_t)1234, 0666 | IPC_CREAT);//创建消息队列
31
32     if (msgid == -1) {
33         fprintf(stderr, "msgget failed with error: %d\n", errno);
34         exit(EXIT_FAILURE);
35     }
36
37     while(running) {
38         printf("Enter some text: ");
39         fgets(buffer, BUFSIZ, stdin);
40         some_data.my_msg_type = 1;
41         strcpy(some_data.some_text, buffer);
42
43         if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) {//发送输入到消息队列
44             fprintf(stderr, "msgsnd failed\n");
45             exit(EXIT_FAILURE);
46         }
47         if (strncmp(buffer, "end", 3) == 0) {
48             running = 0;//遇到end消息为止
49         }
50     }
51
52     exit(EXIT_SUCCESS);
53 }

程序的执行效果:

 1 [email protected]:~/c_program/544977-blp3e/chapter14$ ./msg2
 2 Enter some text: hello
 3 Enter some text: world
 4 Enter some text: this is not the end?
 5 Enter some text: this is end ?
 6 Enter some text: endend
 7 [email protected]:~/c_program/544977-blp3e/chapter14$ ./msg1
 8 You wrote: hello
 9 You wrote: world
10 You wrote: this is not the end?
11 You wrote: this is end ?
12 You wrote: endend
13 [email protected]:~/c_program/544977-blp3e/chapter14$ 
时间: 2024-08-25 14:57:33

进程间通信第二课--信号量 共享内存 消息队列的相关文章

第二十三天:共享内存.消息队列及mysql数据库使用

共享内存和消息队列也是进程间的通信方式. 共享内存,(和信号量的操作类似) #include<sys/shm.h> 1.int shmget(key_t key,size_t size,int shmflg); 作用:新建一块内存或者返回已建好的内存 参数:key,用于表示开辟一段内存,各进程通过这个标志访问同一块内存 size,内存的大小 shmflg,和文件操作完全相同权限表示,按位或IPC_CREATE表示创建一块内存,如果key表示的内存已经建立,即使加了IPC_CREATE也不会新建

Linux进程间通信(消息队列/信号量+共享内存)

写在前面 不得不说,Deadline果真是第一生产力.不过做出来的东西真的是不堪入目,于是又花了一早上重写代码. 实验内容 进程通信的邮箱方式由操作系统提供形如 send()和 receive()的系统调用来支持,本实验要求学生首先查找资料了解所选用操作系统平台上用于进程通信的系统调用具体形式,然后使用该系统调用编写程序进行进程间的通信,要求程序运行结果可以直观地体现在界面上.在此基础上查找所选用操作系统平台上支持信号量机制的系统调用具体形式,运用生产者与消费者模型设计实现一个简单的信箱,该信箱

进程间通信(三)&mdash;&mdash;共享内存区

1.概述 共享内存区是IPC中最快的,当内存区映射到共享它的进程的地址空间,进程间数据的传递就不再涉及内核. 但是这需要某种形式的同步,最常用的是信号量. 不再涉及内核:进程不再通过执行任何进入内核的系统调用来彼此传递数据.内核必须建立允许各个进程共享该内存区的内存映射关系,然后一值管理该内存区.   管道,FIFO和消息队列的问题是,两个进程要交换信息时,这些信息必须经由内核传递. 共享内存区可以绕过这个问题,但是一般必须同步数据.   使用内存映射文件的特性,所有的I/O都不再有内核直接参与

(转载)linux下的僵尸进程处理SIGCHLD信号Linux环境进程间通信(五): 共享内存(下)

Linux环境进程间通信(五): 共享内存(下) 在共享内存(上)中,主要围绕着系统调用mmap()进行讨论的,本部分将讨论系统V共享内存,并通过实验结果对比来阐述两者的异同.系统V共享内存指的是把所有共享数据放在共享内存区域(IPC shared memory region),任何想要访问该数据的进程都必须在本进程的地址空间新增一块内存区域,用来映射存放共享数据的物理内存页面. 系统调用mmap()通过映射一个普通文件实现共享内存.系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内

嵌入式 Linux进程间通信(八)——共享内存

嵌入式 Linux进程间通信(八)--共享内存 一.共享内存 共享内存允许两个或更多进程共享给定的内存区,数据不需要在不同进程间进行复制,是最快的进程间通信方式.使用共享内存唯一需要注意的是多个进程之间对给定存储区的同步访问,但共享内存本身没有提供同步机制,通常使用信号量来实现对共享内存访问的同步. 共享内存编程流程:创建共享内存.映射共享内存.使用共享内存.撤销映射操作.删除共享内存 1.创建共享内存 #include <sys/ipc.h> #include <sys/shm.h&g

linux进程间通信之System V共享内存详解及代码示例

共享内存是最快最为高效的进程间通信方式,当共享内存映射到共享它的某个进程的地址空间后,进程间的数据传递就不再牵扯到内核,进程可以直接读取内核,不需要通过内核系统调用进行数据拷贝.一般使用情况,从共享内存中写入或读取数据的进程间需要做同步,例如通过信号量,互斥锁去同步. 共享内存有System V 共享内存和Posix共享内存,本文介绍System V 共享内存.System V共享内存头文件及相关函数原型:#include <sys/shm.h> int shmget(key_t key, s

进程间通信(四)—共享内存

我会用几篇博客总结一下在Linux中进程之间通信的几种方法,我会把这个开头的摘要部分在这个系列的每篇博客中都打出来 进程之间通信的方式 管道 消息队列 信号 信号量 共享存储区 套接字(socket) 进程间通信(三)—信号量传送门:http://www.cnblogs.com/lenomirei/p/5649792.html 进程间通信(二)—消息队列传送门:http://www.cnblogs.com/lenomirei/p/5642575.html 进程间通信(一)—管道传送门:http:

Android native进程间通信实例-binder结合共享内存

在android源码的驱动目录下,一般会有共享内存的相关实现源码,目录是:kernel\drivers\staging\android\ashmem.c.但是本篇文章不是讲解android共享内存的功能实现原理,而是讲怎么运用它. 1. 在linux中,不同进程间拥有自己独立的内存空间,32位操作系统中好像一个进程能用的内存大小是4G吧.而且一般不同进程间不能够互相使用各自内存的数据. 当然不同进程间共享数据方法很多,比如之前说的进程间通信binder,socket等等,不过android出了一

linux下多进程+信号量+共享内存编程

此程序实现没有亲缘关系的两个进程间通过共享内存进行数据通信. 同时,使用信号量保证两个进程的读写同步:发送方在写共享内存时,接收方不能读数据:接收方在读数据时,发送方不能写数据. 1.fork创建子进程 2.使用二元信号量,同步读写端 fork_shm.c #include<stdio.h> #include<sys/types.h> #include<unistd.h> #include"send_recv.h" int main(void) {