IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、streams等。其中Socket和Streams支持不同主机上的两个进程IPC.
管道的创建
管道是由调用pipe函数来创建
#include <unistd.h>
int pipe (int fd[2]);
//返回:成功返回0,出错返回-1
fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。
管道如何实现进程间的通信
(1)父进程创建管道,得到两个⽂件描述符指向管道的两端
(2)父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道。
(3)父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管道写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。
一、管道
管道,通常指无名管道,是 UNIX 系统IPC最古老的形式。
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <errno.h>
5
6 // int mkfifo(const char *pathname, mode_t mode);
7
8
9 int main()
10 {
11 if((mkfifo("./file",0600) == -1) && errno == EEXIST){
12 printf("create file field\n");
13 perror("why");
14 }
15
16 return 0;
17 }
二、FIFO
FIFO,也称为命名管道,它是一种文件类型。
#include <sys/types.h>
3 #include <sys/stat.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7
8
9 // int mkfifo(const char *pathname, mode_t mode);
10
11
12 int main()
13 {
14 char buf[30];
15
16
17 if((mkfifo("./file",0600) == -1) && errno != EEXIST){
18 printf("create file field\n");
19 perror("why");
20 }
21
22 int fd = open("./file",O_RDONLY);
23 printf("open successful\n");
24
25 int nread = read(fd,buf,30);
26 printf("read %d byte,context %s\n",nread,buf);
27
28 close(fd);
29 return 0;
30 }
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <errno.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <string.h>
8
9
10
11 int main()
12 {
13 char *buf="write successful";
14
15
16
17 int fd = open("./file",O_WRONLY);
18 printf("write successful\n");
19
20 int nwrite = write(fd,buf,strlen(buf));
21 printf("read %d byte,context %s\n",nwrite,buf);
22
23 close(fd);
24 return 0;
25 }
三、消息队列
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 #include <stdio.h>
5
6 // int msgget(key_t key, int msgflg);
7 //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
8
9 //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
10 struct msgbuf {
11 long mtype; /* message type, must be > 0 */
12 char mtext[128]; /* message data */
13 };
14
15
16 int main()
17 {
18 struct msgbuf sendBuf = {888,"this is msgget from quen"};
19
20 struct msgbuf readBuf;
21
22 key_t key; //huo qu jian zhi
23 key = ftok(".",'z'); //"."(dangqianmulu),'z'(limiankeyiweirenyizhi)
24
25 int msgID = msgget(key,IPC_CREAT|0777);
26
27 if(msgID == -1){
28
29 printf("create msgID filed\n");
30 }
31
32 msgsnd(msgID,&sendBuf,sizeof(sendBuf.mtext),0);
33
34 msgrcv(msgID,&readBuf,sizeof(readBuf.mtext),988,0);
35 printf("return read context %s\n",readBuf.mtext);
36 msgctl(key,IPC_RMID,NULL); //guan diao dui lie
37
38 return 0;
39 }
1 #include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/msg.h>
4 #include <stdio.h>
5
6 // int msgget(key_t key, int msgflg);
7 //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
8
9 //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
10 struct msgbuf {
11 long mtype; /* message type, must be > 0 */
12 char mtext[128]; /* message data */
13 };
14
15
16 int main()
17 {
18 struct msgbuf readBuf;
19 key_t key;
20 key = ftok(".",'z');
21
22 int msgID = msgget(key,IPC_CREAT|0777);
23
24
25 if(msgID == -1){
26
27 printf("create msgID filed\n");
28 }
29
30 msgrcv(msgID,&readBuf,sizeof(readBuf.mtext),888,0);
31 printf("read context %s\n",readBuf.mtext);
32
33 struct msgbuf sendBuf= {988,"think recive"};
34 msgsnd(msgID,&sendBuf,sizeof(sendBuf.mtext),0);
35 msgctl(key,IPC_RMID,NULL);
36
37 return 0;
38 }
四、共享内存编程
共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区。
1 #include <sys/shm.h>
2 // 创建或获取一个共享内存:成功返回共享内存ID,失败返回-1
3 int shmget(key_t key, size_t size, int flag);
4 // 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-1
5 void *shmat(int shm_id, const void *addr, int flag);
6 // 断开与共享内存的连接:成功返回0,失败返回-1
7 int shmdt(void *addr);
8 // 控制共享内存的相关信息:成功返回0,失败返回-1
9 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
1 #include <sys/ipc.h>
2 #include <sys/shm.h>
3 #include <sys/types.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8
9 // int shmget(key_t key, size_t size, int shmflg);
10
11 int main()
12 {
13 char *shmddr;
14 int shmID;
15 key_t key;
16 key = ftok(".",1);
17
18 shmID = shmget(key,1024*4,0);
19 if(shmID == -1){
20
21 printf("create failed\n");
22 exit(0);
23 }
24 shmddr = shmat(shmID,0,0);
25
26 printf("shmmad ok\n");
27 printf("data %s\n",shmddr);
28 shmdt(shmddr);
29
30 printf("quit");
31 return 0;
32 }
1 #include <sys/ipc.h>
2 #include <sys/shm.h>
3 #include <sys/types.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8
9 // int shmget(key_t key, size_t size, int shmflg);
10
11 int main()
12 {
13 char *shmddr;
14 int shmID;
15 key_t key;
16 key = ftok(".",1);
17
18 shmID = shmget(key,1024*4,IPC_CREAT|0666);
19 if(shmID == -1){
20
21 printf("create failed\n");
22 exit(0);
23 }
24 shmddr = shmat(shmID,0,0);
25
26 printf("shmmad ok\n");
27 strcpy(shmddr,"chujunpeng");
28 sleep(5);
29
30 shmdt(shmddr);
31
32 shmctl(shmID,IPC_RMID,0);
33 printf("quit");
34 return 0;
35 }
五、信号
入门级:
#include <stdio.h>
2 #include <signal.h>
3
4 // typedef void (*sighandler_t)(int);
5
6 // sighandler_t signal(int signum, sighandler_t handler);
7
8
9 void handler(int signum)
10 {
11 printf("signum is %d",signum);
12 switch(signum){
13
14 case 2:
15 printf("SIGINT");
16 break;
17 case 9:
18 printf("SIGKILL");
19 break;
20 case 10:
21 printf("SIGUSR1");
22 break;
23
24
25 }
26 }
27
28
29 int main()
30 {
31
32 signal(SIGINT,handler);
33 signal(SIGKILL,handler);
34 signal(SIGUSR1,handler);
35 while(1);
36 return 0;
37 }
1 #include <stdio.h>
2 #include <signal.h>
3 #include <sys/types.h>
4 #include <stdlib.h>
5 // typedef void (*sighandler_t)(int);
6
7 // sighandler_t signal(int signum, sighandler_t handler);
8
9
10
11 int main(int argc ,char** argv)
12 {
13 int signum;
14 int pid;
15
16 signum = atoi(argv[1]); // shi zhifuchuan suoyi atoi jiang int zhuan huan
17 pid = atoi(argv[2]);
18
19 printf("num = %d,pid = %d\n",signum,pid);
20
21 kill(pid,signum);
22
23 printf("send ok\n");
24
25 return 0;
26 }
高级:
1 #include <signal.h>
2 #include <stdio.h>
3 #include <sys/types.h>
4 #include <unistd.h>
5
6 // int sigaction(int signum, const struct sigaction *act,
7 // struct sigaction *oldact);
8
9 // void (*sa_sigaction)(int, siginfo_t *, void *);
10
11 void handler(int signum,siginfo_t *info,void *context)
12 {
13 printf("signum is %d\n",signum);
14 if(context != NULL){
15
16
17 printf("data is %d\n",info->si_int);
18 // printf("data is %d\n",info.si);
19 printf("from:%d\n",info->si_pid);
20
21 }
22
23 }
24
25 int main()
26 {
27 struct sigaction act;
28 printf("pid = %d\n",getpid());
29
30 act.sa_sigaction = handler;
31 act.sa_flags = SA_SIGINFO;
32
33 sigaction(SIGUSR1,&act,NULL);
34
35 while(1);
36 return 0;
37 }
1 #include <signal.h>
2 #include <stdio.h>
3 #include <sys/types.h>
4 #include <unistd.h>
5 #include <stdlib.h>
6 // int sigqueue(pid_t pid, int sig, const union sigval value);
7
8 int main(int argc,char **argv)
9 {
10 int signum;
11 int pid;
12
13 signum = atoi(argv[1]);
14 pid = atoi(argv[2]);
15
16 union sigval value;
17 value.sival_int = 100;
18
19 sigqueue(pid,signum,value);
20 printf("%d,send ok\n",getpid());
21
22
23 return 0;
24 }
六、信号量
#include <sys/types.h>
2 #include <sys/ipc.h>
3 #include <sys/sem.h>
4 #include <unistd.h>
5 #include <stdio.h>
6 // int semget(key_t key, int nsems, int semflg);
7 // int semctl(int semid, int semnum, int cmd, ...);
8 // int semop(int semid, struct sembuf *sops, size_t nsops);
9
10 union semun {
11 int val; /* Value for SETVAL */
12 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
13 unsigned short *array; /* Array for GETALL, SETALL */
14 struct seminfo *__buf; /* Buffer for IPC_INFO
15 (Linux-specific) */
16 };
17
18 void pGetkey(int id)
19 {
20 struct sembuf set;
21 set.sem_num = 0;
22 set.sem_op = -1;
23 set.sem_flg = SEM_UNDO;
24
25 semop(id,&set,1);
26 printf("get key\n");
27 }
28
29
30 void vPutkey(int id)
31 {
32 struct sembuf set;
33 set.sem_num = 0;
34 set.sem_op = 1;
35 set.sem_flg = SEM_UNDO;
36
37 semop(id,&set,1);
38 printf("put key\n");
39 }
int main()
41 {
42 key_t key;
43 int semId;
44
45 key = ftok(".",2);
46 //信号量集中有一个信号
47 semId = semget(key,1,IPC_CREAT|0666);
48
49 union semun initsem;
50 initsem.val = 0; //不可以拿锁状态
51 semctl(semId,0, SETVAL,initsem); //初始化信号量
52 //操作第0个信号量 //SETVAL设置信号量的值设置为initsem
53 int pid = fork();
54 if(pid > 0){
55 pGetkey(semId);
56 printf("this is father\n");
57 vPutkey(semId);
58 }
59
60 else if(pid == 0){
61 printf("this is children\n");
62 vPutkey(semId);
63 }else{
64 printf("error\n");
65 }
66
67 return 0;
68 }
特点总结
1.管道:速度慢,容量有限,只有父子进程能通讯
2.FIFO:任何进程间都能通讯,但速度慢
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
4.信号量:不能传递复杂消息,只能用来同步
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存