一、进程间通信简介
在Linux系统中,进程间通信(Inter Process Communication,IPC)是实现多个进程之间数据交换的重要手段。正如名称所示,IPC旨在实现不同进程之间的相互通信,使得进程之间可以进行数据交换,完成相应的任务。
常见的进程间通信方式包括进程信号、共享内存、消息队列、信号量和管道等。
二、共享内存
共享内存是指两个或者多个进程共享同一段物理内存的方式。
共享内存的优点在于速度快,因为共享内存的本质是一块内存空间,进程之间不需要进行数据拷贝和操作系统的干预,直接读取该内存空间中的数据,因此效率很高。
下面是使用共享内存进行进程间通信的示例代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #define SHM_SIZE 1024 #define SHM_MODE 0666 int main(){ int shmid; char *shmptr; char buff[SHM_SIZE]; if((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0){ printf("shmget error\n"); exit(1); } if((shmptr = shmat(shmid, 0, 0)) == (void *)-1){ printf("shmat error\n"); exit(1); } while(1){ fgets(buff, SHM_SIZE, stdin); if(strncmp(buff, "quit", 4) == 0) break; strncpy(shmptr, buff, SHM_SIZE); } if(shmdt(shmptr) < 0){ printf("shmdt error\n"); exit(1); } if(shmctl(shmid, IPC_RMID, 0) < 0){ printf("shmctl error\n"); exit(1); } exit(0); }
上面的代码示例创建一个共享内存区域,并且通过fgets函数从标准输入中读取数据,将数据写入该共享内存空间。
下面是另一个进程访问该共享内存的代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #define SHM_SIZE 1024 #define SHM_MODE 0666 int main(){ int shmid; char *shmptr; if((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0){ printf("shmget error\n"); exit(1); } if((shmptr = shmat(shmid, 0, 0)) == (void *)-1){ printf("shmat error\n"); exit(1); } while(1){ if(*shmptr != '\0'){ printf("receive %s", shmptr); memset(shmptr, 0, SHM_SIZE); } sleep(1); } if(shmdt(shmptr) < 0){ printf("shmdt error\n"); exit(1); } exit(0); }
上面的代码示例访问上面示例中创建的共享内存空间,并且每隔1秒钟输出访问到的数据。
三、消息队列
消息队列是多个进程之间传递数据的一种方式,类似于生产者-消费者模型。
在发送进程将数据写入消息队列中之后,接收进程从该消息队列中读取数据。
下面是使用消息队列进行进程间通信的示例代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/msg.h> #define MSG_SIZE 1024 struct msgbuf{ long mtype; char mtext[MSG_SIZE]; }; int main(){ key_t key; int msgid; struct msgbuf msg; if((key = ftok(".", 'a')) == -1){ printf("ftok error\n"); exit(1); } if((msgid = msgget(key, IPC_CREAT | 0666)) < 0){ printf("msgget error\n"); exit(1); } while(1){ fgets(msg.mtext, MSG_SIZE, stdin); msg.mtype = 1; msgsnd(msgid, &msg, strlen(msg.mtext) + 1, 0); if(strncmp(msg.mtext, "quit", 4) == 0) break; } msgctl(msgid, IPC_RMID, 0); exit(0); }
上面的代码示例创建一个消息队列,并且通过fgets函数从标准输入中读取数据,将数据写入该消息队列中。
下面是另一个进程访问该消息队列的代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/msg.h> #define MSG_SIZE 1024 struct msgbuf{ long mtype; char mtext[MSG_SIZE]; }; int main(){ key_t key; int msgid; struct msgbuf msg; if((key = ftok(".", 'a')) == -1){ printf("ftok error\n"); exit(1); } if((msgid = msgget(key, IPC_CREAT | 0666)) < 0){ printf("msgget error\n"); exit(1); } while(1){ msgrcv(msgid, &msg, MSG_SIZE, 1, 0); if(strncmp(msg.mtext, "quit", 4) == 0) break; printf("receive %s", msg.mtext); } msgctl(msgid, IPC_RMID, 0); exit(0); }
上面的代码示例访问上面示例中创建的消息队列,并且输出访问到的数据。
四、管道
管道是一种进程间通信方式,通过在不同的进程中使用文件描述符来实现进程间的通信。
管道分为匿名管道和有名管道两种方式。匿名管道只能在有父子关系的进程之间使用,有名管道可以在不同的进程之间使用。
下面是使用匿名管道进行进程间通信的示例代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define PIPE_SIZE 1024 int main(){ int fd[2]; pid_t pid; char buff[PIPE_SIZE]; if(pipe(fd) < 0){ printf("pipe error\n"); exit(1); } if((pid = fork()) < 0){ printf("fork error\n"); exit(1); }else if(pid > 0){ close(fd[0]); while(1){ fgets(buff, PIPE_SIZE, stdin); if(strncmp(buff, "quit", 4) == 0) break; write(fd[1], buff, PIPE_SIZE); } close(fd[1]); }else{ close(fd[1]); while(1){ if(read(fd[0], buff, PIPE_SIZE) == 0) break; printf("receive %s", buff); } close(fd[0]); exit(0); } exit(0); }
上面的代码示例创建一个匿名管道,并且通过fgets函数从标准输入中读取数据,将数据写入该管道中。
下面是另一个进程访问该管道的代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define PIPE_SIZE 1024 int main(){ int fd[2]; pid_t pid; char buff[PIPE_SIZE]; if(pipe(fd) < 0){ printf("pipe error\n"); exit(1); } if((pid = fork()) < 0){ printf("fork error\n"); exit(1); }else if(pid > 0){ close(fd[0]); while(1){ if(read(fd[1], buff, PIPE_SIZE) == 0) break; printf("receive %s", buff); } close(fd[1]); wait(NULL); }else{ close(fd[1]); while(1){ fgets(buff, PIPE_SIZE, stdin); if(strncmp(buff, "quit", 4) == 0) break; write(fd[0], buff, PIPE_SIZE); } close(fd[0]); exit(0); } exit(0); }
上面的代码示例访问上面示例中创建的匿名管道,并且输出访问到的数据。