1.管道
管道用来在两个进程之间传递数据。
1.1有名管道
有名管道可以在任意两个进程之间通信 利用mkfifo + 文件名 来促使系统调用创建管道文件
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode)
1.1.1示例1:
进程 a 将从键盘获取的数据循环传递给另一个进程 b
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <assert.h>
7 int main()
8 {
9 int fd=open("fifo",O_WRONLY);
10 assert(fd!=-1);
11
12 while(1)
13 {
14 char buff[128]={0};
15 fgets(buff,128,stdin);
16 write(fd,buff,strlen(buff)-1);
17 if(strncmp(buff,"end",3)==0)
18 {
19 break;
20 }
21 }
22 close(fd);
23 exit(0);
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <assert.h>
5 #include <fcntl.h>
6 #include <string.h>
7 int main()
8 {
9 int fd=open("fifo",O_RDONLY);
10 assert(fd!=-1);
11
12 while(1)
13 {
14 char buff[128]={0};
15 int n=read(fd,buff,128);
16 if(n<=0)
17 {
18 break;
19 }
20 printf("%s\n",buff);
21 }
22 close(fd);
23 exit(0);
24 }
程序运行结果: (打开两个会话窗口,同时运行a,b两个程序)
1.2无名管道
无名管道主要用于父子进程间的通信 无名管道的创建如下:
#include <unistd.h>
int pipe(int fds[2]);
1.2.1示例2:
父进程向管道中写入内容,子进程从管道中读取内容并输出到屏幕
1 #include <stdlib.h>
2 #include <fcntl.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <assert.h>
7 int main()
8 {
9 int fd[2];
10 int res=pipe(fd);
11 assert(res!=-1);
12 pid_t pid=fork();
13 assert(pid!=-1);
14 if(pid==0)
15 {
16 char buff[128]={0};
17 read(fd[0],buff,strlen(buff)-1);
18 printf("%s\n",buff);
19 }
20 else
21 {
22 char buff[128]={0};
23 fgets(buff,128,stdin);
24 write(fd[1],buff,strlen(buff)-1);
25 }
26
27 close(fd[0]);
28 close(fd[1]);
29 exit(0);
30 }
程序运行结果:
1.3管道的特点:
①写入管道的数据都在内存中 ②管道是一种半双工通信方式 ③有名可以在任意进程间使用,而无名主要在父子进程间
2.信号量
用于管理对资源的访问 信号量是一种特殊的变量,只允许对它进行等待和发送信号这两种操作。 P(信号量变量):用于等待。(对信号量变量进行-1操作) V(信号量变量):用于发送信号。(对信号量变量进行+1操作)
2.1信号量的接口介绍
2.1.1semget函数
用于创建或者获取已存在的信号量 成功时返回信号量标识符,失败返回-1
int semget(key_t key,int num_sems,int sem_flags)
第一个参数:是整数值,不相关的进程可以通过key来访问同一个信号量。返回一个信号量的标识符,以供其他的信号量函数使用。 第二个参数:指定需要的信号量数目 第三个参数:作用类似于创建文件的时候所设置的文件访问权限。我们可以联合使用标志IPC_CREAT和IPC_EXCL来创建一个新的、唯一的信号量。也可以在“或”操作设置权限 例如:semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
2.1.2semop函数
用于对信号量进行改变P/V操作 成功返回0,失败返回-1
int semop(int sem_id,struct sembuf *sem_ops,size_t num_sem_ops);
第一个参数:是由semget返回的信号量标识符 第二个参数:指向一个结构数组的指针,这个数组成员如下:
struct sembuf
{
short sem_num;
short sem_op;
short sem_flg;
}
第三个参数:一般写为1
2.1.3semctl函数
控制信号量 成功返回0,失败返回-1
int semctl(int sem_id,int sem_num,int command,...)
第一个参数:是由semget返回的信号量标识符 第二个参数:信号量的编号,表示这个第几个信号量,编号从0开始 第三个参数:表示将要采取的动作,有以下两个常用的: ①SETVAL:用来把信号量初始化为一个已知的值,这个值通过union_semun中的val成员设置。 ②IPC_RMID:用于删除一个已经无需继续使用的信号量标识符 第四个参数:如果第三个参数被设置为SETVAL,那么第四个参数将会是一个union_semun结构。至少有一下成员:
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}
2.2 信号量的使用
2.2.1 示例3(使用一个信号量的例子):
进程 a 和进程 b 模拟访问打印机,进程 a 输出第一个字符‘a’表示开始使用打印机,输出第二个字符‘a’表示结束使用,b 进程操作与 a 进程相同。(由于打印机同一时刻只能被一个进程使用,所以输出结果不应该出现 abab)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <sys/sem.h>
5 union semun
6 {
7 int val;
8 };
9 int sem_init();
10 void sem_p();
11 void sem_v();
12 void sem_destory();
1 #include "ab.h"
2
3 static int semid=-1;
4 int sem_init()
5 {
6 semid=semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);
7 if(semid==-1)
8 {
9 semid=semget((key_t)1234,1,0600);
10 if(semid==-1)
11 {
12 printf("semget err\n");
13 return -1;
14 }
15 }
16 else
17 {
18 union semun a;
19 a.val=1;
20 if(semctl(semid,0,SETVAL,a)==-1)
21 {
22 printf("semctl err\n");
23 return -1;
24 }
25 }
26 }
27 void sem_p()
28 {
29 struct sembuf buf;
30 buf.sem_num=0;
31 buf.sem_op=-1;
32 buf.sem_flg=SEM_UNDO;
33 if(semop(semid,&buf,1)==-1)
34 {
35 printf("sem_p err\n");
36 }
37 }
38 void sem_v()
39 {
40 struct sembuf buf;
41 buf.sem_num=0;
42 buf.sem_op=1;
43 buf.sem_flg=SEM_UNDO;
44 if(semop(semid,&buf,1)==-1)
45 {
46 printf("sem_v err\n");
47 }
48
49 }
50 void sem_destory()
51 {
52 if(semctl(semid,0,IPC_RMID)==-1)
53 {
54 printf("sem_destory err\n");
55 }
56 }
1 #include <unistd.h>
2 #include "ab.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5 int main()
6 {
7 sem_init();
8 for(int i=0;i<10;++i)
9 {
10 sem_p();
11 printf("a");
12 fflush(stdout);
13 int n=rand()%3;
14 sleep(n);
15 printf("a");
16 fflush(stdout);
17 sem_v();
18 n=rand()%3;
19 sleep(n);
20 }
21 return 0;
22 }
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include "ab.h"
5 int main()
6 {
7 sem_init();
8 for(int i=0;i<10;++i)
9 {
10 sem_p();
11 printf("b");
12 fflush(stdout);
13 int n=rand()%3;
14 sleep(n);
15 printf("b");
16 fflush(stdout);
17 sem_v();
18 n=rand()%3;
19 sleep(n);
20 }
21 return 0;
22 }
23
程序运行结果:
2.2.2 示例4(使用三个信号量的例子):
:三个进程 a、b、c 分别输入“A”、“B”、“C”,要求输出结果必须是“ABCABCABC…”
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <sys/sem.h>
5 #define SEM_NUM 3
6 #define SEM1 0
7 #define SEM2 1
8 #define SEM3 2
9 union semun
10 {
11 int val;
12 };
13 int sem_init();
14 void sem_p(int index);
15 void sem_v(int index);
16 void sem_destory();
17
1 #include "sem.h"
2 static int semid=-1;
3 int sem_init()
4 {
5 semid=semget((key_t)1235,SEM_NUM,IPC_CREAT|IPC_EXCL|0600);
6 if(semid==-1)
7 {
8 semid=semget((key_t)1235,SEM_NUM,0600);
9 if(semid==-1)
10 {
11 printf("semget err\n");
12 return -1;
13 }
14 }
15 else
16 {
17 int arr[SEM_NUM]={1,0,0};
18 for(int i=0;i<SEM_NUM;++i)
19 {
20 union semun a;
21 a.val=arr[i];
22 if(semctl(semid,i,SETVAL,a)==-1)
23 {
24 printf("semctl err\n");
25 return -1;
26 }
27 }
28 }
29 }
30 void sem_p(int index)
31 {
32 if(index<0||index>=SEM_NUM)
33 {
34 return;
35 }
36 struct sembuf buf;
37 buf.sem_num=index;
38 buf.sem_op=-1;
39 buf.sem_flg=SEM_UNDO;
40
41 if(semop(semid,&buf,1)==-1)
42 {
43 printf("sem_p err\n");
44 return ;
45 }
46 }
47 void sem_v(int index)
48 {
49 if(index<0||index>=SEM_NUM)
50 {
51 return;
52 }
53 struct sembuf buf;
54 buf.sem_num=index;
55 buf.sem_op=1;
56 buf.sem_flg=SEM_UNDO;
57
58 if(semop(semid,&buf,1)==-1)
59 {
60 printf("sem_v err\n");
61 return;
62 }
63 }
64 void sem_destory()
65 {
66 if(semctl(semid,0,IPC_RMID)==-1)
67 {
68 printf("sem_destroy err\n");
69 return;
70 }
71 }
1 #include <stdio.h>
2 #include "sem.h"
3 #include <unistd.h>
4 int main()
5 {
6 int res=sem_init();
7 if(res==-1)
8 {
9 return 0;
10 }
11 for(int i=0;i<5;++i)
12 {
13 sem_p(SEM1);
14 printf("a");
15 fflush(stdout);
16 sem_v(SEM2);
17 }
18
19
20
21 return 0;
22 }
1 #include <stdio.h>
2 #include "sem.h"
3 #include <unistd.h>
4 int main()
5 {
6 int res=sem_init();
7 if(res==-1)
8 {
9 return 0;
10 }
11 for(int i=0;i<5;++i)
12 {
13 sem_p(SEM2);
14 printf("b");
15 fflush(stdout);
16 sem_v(SEM3);
17 }
18
19
20
21 return 0;
22 }
23
1 #include <unistd.h>
2 #include <stdio.h>
3 #include "sem.h"
4 int main()
5 {
6 int res=sem_init();
7 if(res==-1)
8 {
9 return 0;
10 }
11
12 for(int i=0;i<5;++i)
13 {
14 sem_p(SEM3);
15 printf("c");
16 fflush(stdout);
17 sem_v(SEM1);
18 }
19
20 sem_destory();
21 return 0;
22 }
程序运行结果:
3.共享内存
用于在程序之间高效地共享数据 共享内存是先在物理内存上申请一块空间,多个进程可以将其映射到自己的虚拟地址空间中。
共享内存为在多个进程之间共享和传递数据提供了一种有效的方式,因为它并未提供同步机制,所以我们通常需要用信号量来同步对共享内存的访问。
3.1共享内存的接口介绍
3.1.1shmget函数
用于创建或者获取共享内存 成功返回共享内存标识符,失败返回-1
int shmget(key_t key,size_t size,int shmflg);
第一个参数:提供一个参数key,有效的为共享内存段命名。shmget函数返回一个共享内存标识符,用于后续的共享内存函数。 第二个参数:以字节为单位指定需要共享的内存容量。 第三个参数:设置权限。由IPC_CREAT和权限标志或才能创建一个新的共享内存段。
3.1.2shmat函数
将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上 成功返回一个指向共享内存第一字节的指针,失败返回-1
void* shmat(int shm_id,const void* shm_addr,int shmflg);
第一个参数:由shmget返回的共享内存标识符 第二个参数:指定的是共享内存连接到当前进程中的地址位置,通常是一个空指针,表示让系统来选择共享内存出现的地址。 第三个参数:一组位标志。两个可能取值如下: ①SHM_RND:这个标志和shm_addr联合使用,用来控制共享内存链接的地址 ②SHM_RDONLY:它使得连接的内存只读。
3.2.3shmdt函数
shmdt()断开当前进程的shmaddr指向的共享内存映射 成功返回0,失败返回-1
int shmdt(const void* shmaddr)
第一个参数:是shmat()函数返回的地址指针
3.2.4shmctl函数
shmctl()控制共享内存 成功返回0,失败返回-1
int shmctl(int shm_id,int command,struct shmid_ds *buf);
第一个参数:是shmget返回的共享内存标识符 第二个参数:要采取的工作,它可以取三个值: ①IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值 ②IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值 ③IPC_RMID:删除共享内存段 第三个参数:buf是一个指针,它指向包含共享内存模式和访问权限的结构 struct shmid_ds结构至少包含以下成员:
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}
3.2共享内存的使用
3.2.1示例5:
进程 a 向共享内存中写入数据,进程 b 从共享内存中读取数据并显示
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <assert.h>
4 #include <stdlib.h>
5 #include <sys/shm.h>
6 #include <string.h>
7 int main()
8 {
9
10 int shmid=shmget((key_t)1234,128,IPC_CREAT|0600);
11 assert(shmid!=-1);
12
13
14 char* s=shmat(shmid,NULL,0);
15 assert(s!=NULL);
16
17 strcpy(s,"hello");
18
19 shmdt(s);
20
21 return 0;
22 }
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <sys/shm.h>
7 int main()
8 {
9 int shmid=shmget((key_t)1234,128,IPC_CREAT|0600);
10 assert(shmid!=-1);
11
12 char*s =shmat(shmid,NULL,0);
13 assert(s!=NULL);
14
15 printf("%s\n",s);
16
17 shmdt(s);
18
19 shmctl(shmid,IPC_RMID,NULL);
20
21 return 0;
22 }
程序运行结果:
3.2.2示例6:
进程 a 从键盘循环获取数据并拷贝到共享内存中,进程 b 从共享内存中获取并打印数据。进程 a 输入一次,进程 b 输出一次,进程 a 不输入,进程 b 也不输出。
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <assert.h>
4 #include <sys/sem.h>
5 #include <sys/shm.h>
6 #include <string.h>
7 #include <stdlib.h>
8 union semun
9 {
10 int val;
11 };
12 #define SEM_W 0
13 #define SEM_R 1
14 int sem_init(int key,int sembuf[],int semmum);
15 int sem_p(int semid,int index);
16 int sem_v(int semid,int index);
17 int sem_destory(int semid);
1 #include "sem.h"
2 int sem_init(int key,int sembuf[],int semnum)
3 {
4 int semid=semget((key_t)key,semnum,0664);
5 if(semid==-1)
6 {
7 semid=semget((key_t)key,semnum,IPC_CREAT|0664);
8 if(semid==-1)
9 {
10 printf("semget err\n");
11 return 0;
12 }
13 }
14 else
15 {
16 for(int i=0;i<semnum;++i)
17 {
18 union semun a;
19 a.val=sembuf[i];
20 if(semctl(semid,i,SETVAL,a)==-1)
21 {
22 printf("semctl err\n");
23 return -1;
24 }
25 }
26 }
27 return semid;
28 }
29 int sem_p(int semid,int index)
30 {
31 struct sembuf buf;
32 buf.sem_num=index;
33 buf.sem_op=-1;
34 buf.sem_flg=SEM_UNDO;
35
36 if(semop(semid,&buf,1)==-1)
37 {
38 printf("semop err\n");
39 return -1;
40 }
41 return 0;
42 }
43 int sem_v(int semid,int index)
44 {
45 struct sembuf buf;
46 buf.sem_num=index;
47 buf.sem_op=1;
48 buf.sem_flg=SEM_UNDO;
49
50 if(semop(semid,&buf,1)==-1)
51 {
52 printf("semop err\n");
53 return -1;
54 }
55 return 0;
56 }
57 int sem_destory(int semid)
58 {
59 if(semctl(semid,0,IPC_RMID)==-1)
60 {
61 printf("sem_destory err\n");
62 return -1;
63 }
64 return 0;
65 }
1 #include "sem.h"
2 int main()
3 {
4 int shmid=shmget((key_t)1357,128,IPC_CREAT|0664);
5 assert(shmid!=-1);
6
7 char* s=shmat(shmid,NULL,0);
8 assert(s!=NULL);
9
10 int sembuf[]={1,0};
11 int semid=sem_init(1000,sembuf,2);
12
13 while(1)
14 {
15 sem_p(semid,SEM_R);
16 if(strncmp(s,"end",3)==0)
17 {
18 break;
19 }
20 printf("%s",s);
21 sem_v(semid,SEM_W);
22 }
23 shmdt(s);
24 shmctl(shmid,IPC_RMID,NULL);
25 sem_destory(semid);
26 return 0;
27 }
1 #include "sem.h"
2 int main()
3 {
4 int shmid=shmget((key_t)1357,128,IPC_CREAT|0664);
5 assert(shmid!=-1);
6
7 char* s=shmat(shmid,NULL,0);
8 assert(shmat!=NULL);
9
10 int sembuf[]={1,0};
11 int semid=sem_init(1000,sembuf,2);
12
13 while(1)
14 {
15 printf("请输入:\n");
16 char buff[128]={0};
17 fgets(buff,128,stdin);
18
19 sem_p(semid,SEM_W);
20 strcpy(s,buff);
21 sem_v(semid,SEM_R);
22
23 if(strncmp(buff,"end",3)==0)
24 {
25 break;
26 }
27 }
28
29 shmdt(s);
30 shmctl(shmid,IPC_RMID,NULL);
31 return 0;
32
33 }
程序运行结果:
4、消息队列
在程序之间传递数据地一种简单方法
4.1消息队列的接口介绍
4.1.1msgget函数
用来创建和访问一个消息队列 成功返回一个正整数,失败返回-1
int msgget(key_t key,int msgflg);
第一个参数:提供一个键值来命名某个特定的队列 第二个参数:设置权限,由IPC_CREAT定义的一个特殊位必须和权限标志位或才能创建一个新的消息队列,如果消息队列已有,则IPC_CREAT标志就会被悄悄忽略掉。
4.1.2msgsnd函数
用来把消息添加到消息队列中 成功返回0,失败返回-1
int msgsnd(int msqid,const void *msg_ptr,size_t msg_sz,int msgflg);
第一个参数:由msgget函数返回的消息队列队列标识符 第二个参数:指向准备发送消息的指针。 消息的结构为:
struct msgbuf
{
long mtype;
char mtext[1];
};
第三个参数:msg_ptr指向的消息的长度 第四个参数:控制在当前消息队列满或队列消息达到系统范围的限制时将要发生的事情。如果设置IPC_NOWAIT的标志,函数将立刻返回,不发送消息并且返回值为-1,如果msgflg中IPC_NOWAIT标志被清除,则发送进程将被挂起以等待队列中腾出可用空间。
4.1.3msgrcv函数
从消息队列中获取消息 成功时返回放到接收缓冲区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的消息。失败返回-1 。
int msgcrv(int msqid,void *msg_ptr,size_t msg_sz,long int msgtype,int msgflg);
第一个参数:消息队列标识符 第二个参数:指向准备接收消息的指针 第三个参数:msg_ptr指向的消息长度 第四个参数:实现接收优先级。 msgtype=0:获取队列中的第一个可用消息 msgtype>0:获取具有相同消息类型的第一个消息 msgtype<0:获取消息类型等于或小与msgtype的绝对值的第一个消息。 第五个参数:用于控制当队列中没有相应类型的消息可以接收时将发生的事情。
4.1.4msgctl函数
控制消息队列 成功返回0,失败返回-1
int msgctl(int msqid,int command,struct msqid_ds *buf);
第一个参数:消息队列标识符 第二个参数:将要采取的动作 ①IPC_STAT:把msqid_ds结构中的数据设置为消息队列的当前关联值 ②IPC_SET:如果进程有足够的权限,九八消息队列的当前关联值设置为msqid_ds结构中给出的值 ③IPC_RMID:删除消息队列 第三个参数:
struct msqid_ds{
uid_t msg_perm.uid;
uid_t msg_perm.gid;
mode_t msg_perm.mode;
}
4.2消息队列的使用
4.2.1示例7:
进程 a 发送一条消息,进程 b 读取消息。
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <assert.h>
4 #include <string.h>
5 #include <sys/msg.h>
6 typedef struct my_message
7 {
8 long mytype;
9 char mytext[128];
10 }myssage;
11 int main()
12 {
13 int msgid=msgget((key_t)2468,IPC_CREAT|0664);
14 assert(msgid!=-1);
15
16 myssage data;
17 memset(&data,0,sizeof(data));
18 data.mytype=1;
19 strcpy(data.mytext,"hello");
20
21 msgsnd(msgid,&data,128,0);
22
23 return 0;
24 }
1 #include <stdio.h>
2 #include <assert.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/msg.h>
6 #include <unistd.h>
7 typedef struct my_myssage
8 {
9 long mytype;
10 char mytext[128];
11 }myssage;
12 int main()
13 {
14 int msgid=msgget((key_t)2468,IPC_CREAT|0664);
15 assert(msgid!=-1);
16
17 myssage data;
18 memset(&data,0,sizeof(data));
19
20 msgrcv(msgid,&data,128,1,0);
21 printf("%ld\n",data.mytype);
22 printf("%s\n",data.mytext);
23
24 msgctl(msgid,IPC_RMID,NULL);
25 return 0;
26
27 }
程序运行结果:
5.ipcs\ipcrm
ipcs可以查看消息队列、共享内存、信号量的使用情况 ipcrm可以进行删除操作
|