进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。
今天记录一下我从中学的几种通信方式:无名管道、有名管道、消息队列、共享存储、信号、信号量。
一、管道(无名管道和命名管道):
无名管道:
管道一般用于两个不同进程之间的通信。当一个进程创建一个管道,并调用fork创建自己的一个子进程后,父进程关闭读管道端,子进程关闭写管道端,这样就提供了两个进程之间数据流动的一种方式。
#include<stdio.h>
#include<unistd.h>
int main()
{
int fd[2];
pid_t pid;
char buf[50];
if(pipe(fd) < 0) // 创建管道
printf("Create failed!\n");
if((pid = fork()) < 0) // 创建子进程
printf("Fork failed!\n");
else if(pid > 0) // 父进程
{
close(fd[0]); // 关闭读端
write(fd[1], "This message from ray", 23);
}
else
{
close(fd[1]); // 关闭写端
read(fd[0], buf, );
printf("%s", buf);
}
return 0;
命名管道:
“命名管道”又名“命名管线”(Named Pipes),是一种简单的进程间通信(IPC)机制。命名管道(NamedPipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。
命名管道代码:
writedemo.c代码:
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int main(){
int cnt=0;
char *str="This message from ray"; // 定义一个*str
int fd= open("./pipetest",O_WRONLY); //以可读可写的方式打开pipetest文件
printf("write open successfull!\n");
while(1){
write(fd,str,strlen(str)); //不断写入数据直到cnt等于五的时候跳出循环
sleep(1);
if(cnt == 5){
break;
}
}
close(fd);
return 0;
}
readdemo.c代码:
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
int main(){
char buf[23]={0};
int nread=0;
if(mkfifo("./pipetest",0600) == -1 && errno!=EEXIST){ //创建命名管道,如果不存在pipetest的返回值会等于-1,并且错误类型不是文件已经存在的情况下
printf("mkfifo failed!");
perror("why");
}
int fd= open("./pipetest",O_RDONLY); //以只读方式打开文件
printf("read successfull!\n");
while(1){
nread = read(fd,buf,23); //用循环不断读取文件里的内容
printf("read %d byte from fifo,context:%s\n",nread,buf);
}
close(fd);
return 0;
}
第二、消息队列:
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的。由于消息队列用的不多,在这里就不po代码了,操作和上面类似。
第三、信号与信号量:
对于 Linux来说,实际信号是软中断,许多重要的程序都需要处理信号。信号,为 Linux 提供了一种处理异步事件的方法。比如,终端用户输入了 ctrl+c 来中断程序,会通过信号机制停止一个程序。
信号概述
信号的名字和编号:
每个信号都有一个名字和编号,这些名字都以“SIG”开头,例如“SIGIO ”、“SIGCHLD”等等。
信号定义在signal.h头文件中,信号名都定义为正整数。
具体的信号名称可以使用kill -l来查看信号的名字以及序号,信号是从1开始编号的,不存在0号信号。kill对于信号0又特殊的应用。
信号的处理有三种方法,分别是:忽略、捕捉和默认动作。
信号量:
什么是信号量:
信号量是一种特殊的变量,访问具有原子性。只允许对它进行两个操作:
1)等待信号量
当信号量值为0时,程序等待;当信号量值大于0时,信号量减1,程序继续运行。
2)发送信号量
将信号量值加1。
信号量用例代码:
semaphore.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
void pGetTheKey(int id){
struct sembuf set;
set.sem_num = 0;
set.sem_op = -1;//拿钥匙
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("Got the key!\n");
}
void vPutBackTheKey(int id){
struct sembuf set;
set.sem_num = 0;
set.sem_op = 1;//拿钥匙
set.sem_flg = SEM_UNDO;
semop(id,&set,1);
printf("Put back the key!\n");
}
int main(int argc, char const *argv[])
{
key_t key;
int semid;
key =ftok(".",2);
semid = semget(key,1,IPC_CREAT|0666); //创建获取信号量
union semun initsem;
initsem.val =0;
semctl(semid,0,SETVAL,initsem); //初始化信号量
//SETVAL设置信号量的值,设置为initsem
int pid =fork();
if(pid > 0){
pGetTheKey(semid);
printf("This is father!\n");
vPutBackTheKey(semid);
}else if(pid == 0){
printf("This is child!\n");
vPutBackTheKey(semid);
}else{
printf("Fork error!\n");
}
return 0;
}
第四、共享存储:
共享存储区(Share Memory)是Linux系统中通信速度最高的通信机制。该机制中共享内存空间和进程的虚地址空间满足多对多的关系。即一个共享内存空间可以映射多个进程的虚地址空间,一个进程的虚地址空间又可以连接多个共享存储区。当进程间预利用共享存储区通信时,先要在主存中建立一个共享存储区,然后将它附接到自己的虚地址空间。该机制只为进程提供了用于实现通信的共享存储区和对共享存储区进行操作的手段,然而并未提供对该区进行互斥访问及进程同步的措施。
sharedmemorywrite.c代码:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(){
char *shmaddr;
int shmid;
key_t key;
key = ftok(".",1); //创建信号量
shmid = shmget(key,1024*3,IPC_CREAT|0666); //获取信号量
if(shmid == -1){
printf("shmget create failed!\n");
exit(-1);
}
shmaddr = shmat(shmid,0,0); //shmat获取信号量的地址
printf("shmget is success!\n");
strcpy(shmaddr,"Ray");
sleep(5);
shmdt(shmaddr);
shmctl(shmid,IPC_RMID,0); //shmctl控制信号量,IPC_RMID删除信号量
printf("quit\n");
return 0;
}
sharedmemoryread.c代码:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(){
char *shmaddr;
int shmid;
key_t key;
key = ftok(".",1); //创建信号量
shmid = shmget(key,1024*3,0); //获取信号量
if(shmid == -1){
printf("shmget create failed!\n");
exit(-1);
}
shmaddr = shmat(shmid,0,0); //找到信号量所在的地址
printf("shmget is success!\n");
printf("data: %s\n:",shmaddr);
shmdt(shmaddr);
printf("quit\n");
return 0;
}
以上。
链接https://www.jianshu.com/p/f445bfeea40a