进程间通信----共享内存、管道(有名、无名)、信号、信号量、消息队列、套接字
一、共享内存
共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问
1.shmget
该函数用来创建共享内存, 成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.
int shmget(key_t key, size_t size, int shmflg);
第一个参数,key 可以用ftok产生, 也可以自己写; 它有效的为共享内存段命名,shmget成功时返回一个与key相关的共享内存标识符
第二个参数,size以字节为单位,指定共享内存的大小
第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存
2.shmat 函数
第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。
void *shmat(int shmid, const void *shmaddr, int shmflg);
第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。
、shmdt函数
该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型如下:
int shmdt(const void *shmaddr);
参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.
4、shmctl函数
与信号量的semctl函数一样,用来控制共享内存,
int semctl(int semid, int semnum, int cmd, ...);
第一个参数,shm_id是shmget函数返回的共享内存标识符。
第二个参数,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
示例1:shmwrite.c
1 #include <stdio.h>
2 #include <sys/ipc.h>
3 #include <sys/shm.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <string.h>
7
8 typedef struct{
9 char name[8];
10 int age;
11 }people;
12
13 int main(int argc, char**argv){
14 int shm_id, i;
15 key_t key;
16 char temp[8];
17 people *p_map;
18 char pathname[30];
19
20 strcpy(pathname, "/tmp");
21 key = ftok(pathname, 0x03);
22 if(key == -1)
23 {
24 perror("ftok error");
25 return -1;
26 }
27 printf("key== %d\n", key);
28 shm_id = shmget(key, 4096, IPC_CREAT|IPC_EXCL|0600);
29 if(shm_id == -1)
30 {
31 perror("shmget err");
32 return -1;
33 }
34 printf("shm_id == %d\n", shm_id);
35 p_map = (people*)shmat(shm_id, NULL, 0);
36 memset(temp, 0x00, sizeof(temp));
37 strcpy(temp, "test");
38 temp[4]= '0';
39 for(i=0; i< 3; i++)
40 {
41 temp[4]+=1;
42 strncpy((p_map+i)->name, temp, 5);
43 }
44 shmdt(p_map);
45 return 0;
46 }
46,1 底端
shmread.c
1 #include <stdio.h>
2 #include <sys/ipc.h>
3 #include <sys/shm.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <string.h>
7
8 typedef struct{
9 char name[8];
10 int age;
11 }people;
12
13 int main(int argc, char**argv){
14 int shm_id, i;
15 key_t key;
16 char temp[8];
17 people *p_map;
18 char pathname[30];
19
20 strcpy(pathname, "/tmp");
21 key = ftok(pathname, 0x03);
22 if(key == -1)
23 {
24 perror("ftok error");
25 return -1;
26 }
27 printf("key== %d\n", key);
28 shm_id = shmget(key, 0, 0);
29 if(shm_id == -1)
30 {
31 perror("shmget err");
32 return -1;
33 }
34 printf("shm_id == %d\n", shm_id);
35 p_map = (people*)shmat(shm_id, NULL, 0);
36 for(i=0; i< 3; i++)
37 {
38 printf("name: %s\n", (*(p_map+i)).name);
39 printf("age: %d\n", (*(p_map+i)).age);
40 }
41 shmdt(p_map);
42 return 0;
43 }
编译、执行:
[zn@localhost shm1]$ gcc -o shmwrite shmwrite.c
[zn@localhost shm1]$ gcc -o shmread shmread.c
[zn@localhost shm1]$ ./shmwrite
key== 50755649
shm_id == 3440645
[zn@localhost shm1]$ ./shmread
key== 50755649
shm_id == 3440645
name: test1
age: 0
name: test2
age: 0
name: test3
age: 0
[zn@localhost shm1]$ ./shmwrite
key== 50755649
shmget err: File exists
[zn@localhost shm1]$ ipcrm -m 3440645
[zn@localhost shm1]$ ./shmwrite
key== 50755649
shm_id == 3473413
[zn@localhost shm1]$
示例2:
shmdata.h
1 #ifndef __SHMDATA_H_HEADER
2 #define __SHMDATA_H_HEADER
3
4 #define TEXT_SZ 2048
5 struct shared_use_st
6 {
7 int written; //标志位,非0:read;0:write
8 char text[TEXT_SZ];
9 };
10 #endif
shmwrite.c
1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <sys/shm.h>
6 #include "./shmdata.h"
7
8 int main()
9 {
10 int running = 1;
11 void *shm = NULL;
12 struct shared_use_st * shared = NULL;
13 char buffer[BUFSIZ + 1];
14 int shmid;
15
16 shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
17 if(shmid == -1)
18 {
19 fprintf(stderr, "shmget failed\n");
20 exit(EXIT_FAILURE);
21 }
22
23 shm = shmat(shmid, (void*)0, 0);
24 if(shm == (void*)-1)
25 {
26 fprintf(stderr, "shmat err\n");
27 exit(EXIT_FAILURE);
28 }
29
30 shared = (struct shared_use_st *)shm;
31 while(running)
32 {
33 while(shared->written == 1)
34 {
35 sleep(1);
36 printf("Waiting...\n");
37 }
38
39 printf("Enter some text: ");
40 fgets(buffer, BUFSIZ, stdin);
41 strncpy(shared->text, buffer, TEXT_SZ);
42
43 shared->written = 1;
44 if(strncmp(buffer, "end", 3) == 0)
45 running = 0;
46 }
47 if(shmdt(shm) == -1)
48 {
49 exit(EXIT_FAILURE);
50 }
51 exit(EXIT_SUCCESS);
52 }
shmread.c
1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <sys/shm.h>
5 #include "shmdata.h"
6
7
8 int main()
9 {
10 int running = 1;
11 void *shm = NULL;
12 struct shared_use_st *shared;
13 int shmid;
14
15 //创建共享内存
16 shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666| IPC_CREAT);
17 if(shmid == -1)
18 {
19 fprintf(stderr, "shmget err\n");
20 exit(EXIT_FAILURE);
21 }
22
23 //将共享内存连接到当前进程的地址空间
24 shm = shmat(shmid, 0, 0);
25 if(shm == (void *)-1)
26 {
27 fprintf(stderr, "shmat err\n");
28 exit(EXIT_FAILURE);
29 }
30 shared = (struct shared_use_st*)shm;
31 shared->written = 0;
32 while(running){
33 if(shared->written != 0)
34 {
35 printf("You wrote: %s", shared->text);
36 sleep(rand()%3);
37 shared->written = 0;
38 if(strncmp(shared->text, "end", 3) == 0)
39 running = 0;
40 }
41 else
42 sleep(1);
43
44 }
45
46 if(shmdt(shm) == -1)
47 {
48 fprintf(stderr, "shmdt err\n");
49 exit(EXIT_FAILURE);
50 }
51
52 if(shmctl(shmid, IPC_RMID, 0) == -1)
53 {
54 fprintf(stderr, "shmctl(IPC_RM)\n");
55 exit(EXIT_FAILURE);
56 }
57 exit(EXIT_SUCCESS);
58 }
二、消息队列
1. msgget 函数
用来创建和访问一个消息队列
int msgget(key_t key, int msgflg);
第一个参数key, 用来命名某个特定的消息队列, 可以ftok产成,也可以自己写
第二个参数msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或操作
2.msgsend函数
将消息添加到消息队列中,成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1.
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
第一个参数是,msgget 函数返回的消息队列标识符。
第二个参数,msg_ptr是一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定消息的类型
第三个参数, msg_sz是msg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。
第四个参数, msgflg用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情
3、msgrcv 函数
该函数用来从一个消息队列获取消息, 调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1.
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msgid, msg_ptr, msg_st的作用也函数msgsnd函数的一样。
msgtype可以实现一种简单的接收优先级。如果msgtype为0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。如果它小于零,就获取类型等于或小于msgtype的绝对值的第一个消息。
msgflg用于控制当队列中没有相应类型的消息可以接收时将发生的事情。
4. msgctl 函数
该函数用来控制消息队列,它与共享内存的shmctl函数相似,它的原型为