文章目录
引言
进程间通信(IPC,InterProcess Communication)是指在不同的进程之间传播或交换信息。
前7种IPC通常限于同一台主机的各个进程间的IPC。然而套接字和STREAMS,是仅有的两种支持不同主机上各个进程间IPC的类型。
1.管道
管道是UNIX系统IPC的最古老形式,并且所有UNIX系统都提供此种通信机制。
管道的特点(也是两种局限性):
- 历史上,它们是半双工的(即数据只在一个方向上流动)。具有固定的读端与写端。现在,有些系统提供了全双工管道,但是为了最佳的可移植性,我们决不预先假定系统使用此特性。
- 只能在具有亲缘关系之间的通信(父子或兄弟之间)。
1.1 无名管道
- 无名管道特点:
- 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
- 只能在具有亲缘关系之间的通信
- 管道是创建在内存中(只存在于内存中),进程结束空间释放,管道不复存在。对于它的读写可以使用普通的read、write等函数.(管道中的数据,读走就没有了)
- 函数原型
经由参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输人。
调用fork之后做什么取决于我们想要有的数据流的方向。对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1])。图15-3显示了在此之后描述符的安排。
为了构造从子进程到父进程的管道,父进程关闭fd[1](写端),子进程关闭fd[0](读端)。 记忆:通常喜欢念0 1对应这里的 读 写。
3.例子:
数据流从父进程流入子进程,父进程打开写端,关闭读端。子进程则是打开读端,关闭写端。反之,如果数据流从子进程流向父进程也是一样。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int fd[2];
int pid;
char buf[128];
// int pipe(int pipefd[2]);
if(pipe(fd) == -1){
printf("creat pipe failed\n");
}
pid = fork();
if(pid<0){
printf("creat child failed\n");
}
else if(pid > 0){
sleep(3);
printf("this is father\n");
close(fd[0]);
write(fd[1],"hello from father",strlen("hello form father"));
wait();
}else{
printf("this is child\n");
close(fd[1]);
read(fd[0],buf,128);
printf("read from father: %s\n",buf);
exit(0);
}
return 0;
}
运行效果:
1.2 命名管道(FIFO)
- 特点:
- 命名管道可以在无关的进程之间交换数据,与无名管道不同。(通过FIFO,不相关的进程也能交换数据)
- 命名管道有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。(创建FIFO类似于创建文件。确实,FIFO的路径名存在于文件系统中。)
- 函数原型:
一旦已经用mkfifo创建了一个FIFO,就可用open打开它。其实,一般的文件I/O函数(close、read、write、unlink等)都可用于FIFO。
FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清楚数据,并且”先进先出“。
3. FIFO有下面两种用途;
(1) FIFO由shell命令使用以便将数据从一条管道线传送到另一条,为此无需创建中间临时文件。
(2) FIFO用于客户进程-服务器进程应用程序中,以在客户进程和服务器进程之间传递数据。我们各用一个例子来说明这两种用途。
代码段:
先运行 read.c,再运行 write.c ;运行之后读端先阻塞起来等待写端的写入,写入完之后读端开始运行。
读端代码块:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
//int mkfifo(const char *pathname, mode_t mode);
int main()
{
char buf[30] = {0};
int nread = 0;
if( (mkfifo("./file",0600) == -1) && errno!=EEXIST){
printf("mkfifo failuer\n");
perror("why");
}
int fd = open("./file",O_RDONLY);
printf("open success\n");
while(1){
nread = read(fd,buf,30);
printf("read %d byte from fifo,context:%s\n",nread,buf);
}
close(fd);
return 0;
}
写端代码块:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
//int mkfifo(const char *pathname, mode_t mode);
int main()
{
int cnt = 0;
char *str = "message from fifo";
int fd = open("./file",O_WRONLY);
printf("write open success\n");
while(1){
write(fd, str, strlen(str));
sleep(1);
if(cnt == 5){
break;
}
}
close(fd);
return 0;
}
2.消息队列
消息队列是消息的链接表存放在内核中并由消息队列标识符标识。
2.1 特点:
- 消息队列是面向记录的,其中的消息具有特地那个的格式以及特定的优先级。
- 消息队列独立于发送与接收进程。进程终止时,消息队列及内容并不会删除。
- 消息队列可以实现信息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
2.2 函数原型:
从队列中取消息,并不一定要以先进先出次序,也可以按信息的类型字段取消息。
函数msgget中key值很关键,相当于一个索引。进程通过这个键值,在Linux内核中找到相应的队列(确保了队列是同一个) 在以下两种情况中,msgget 将创建一个新的消息队列:
- 如果没有与键值相对应的消息队列,并且flag中包含了 IPC_CREAT 标志位。
- key 参数为 IPC_PRIVATE。
函数msgrcv在读取消息队列时,type参数有下面几种情况:
- type == 0 ,返回队列中的第一个消息;
- type > 0 ,返回队列中消息类型为 type 的第一个消息;
- type < 0 ,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。
代码端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
// int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
// int msgflg);
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
};
int main()
{
//1.huoqu
struct msgbuf readBuf;
//获取key值
key_t key;
key = ftok(".",'m');// “.” 表示当前路径,第二个参数随意,可以是数字,可以是字符
printf("key=%x\n",key);
// 获取/创建消息队列
int msgId = msgget(key, IPC_CREAT|0777);//0777 可读可写可执行
if(msgId == -1 ){
printf("get que failuer\n");
}
memset(&readBuf,0,sizeof(struct msgbuf));
//接收数据
msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),888,0); //第三个参数:消息类型,第四个参数:0 的意思是默认的方式接收消息,接收不到消息会一直阻塞
printf("read from que:%s\n",readBuf.mtext);
//发送数据
struct msgbuf sendBuf = {988,"thank you for reach"};
msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
//关闭数据
msgctl(msgId,IPC_RMID,NULL);//第三个参数,通常设置为NULL
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
// int msgget(key_t key, int msgflg);
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
// int msgflg);
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[256]; /* message data */
};
int main()
{
//1.huoqu
struct msgbuf sendBuf = {888,"this is message from quen"}; //第一个参数:消息的类型
struct msgbuf readBuf;
memset(&readBuf,0,sizeof(struct msgbuf));
//获取key值
key_t key;
key = ftok(".",'m');// “.” 表示当前路径
printf("key=%x\n",key);
//创建消息队列
int msgId = msgget(key, IPC_CREAT|0777);//0777 可读可写可执行
if(msgId == -1 ){
printf("get que failuer\n");
}
//发送数据
msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
printf("send over\n");
//接收数据
msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),988,0);//第四个参数:0 的意思是默认的方式接收消息,接收不到消息会一直阻塞
printf("reaturn from get:%s\n",readBuf.mtext);
//关闭消息队列
msgctl(msgId,IPC_RMID,NULL);
return 0;
}
运行效果:
3.共享存储
共享存储允许两个或更多进程共享给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC。
- 特点:
- 共享存储是最快的一种IPC,因为进程是直接对内存进行存储,而不需要任何数据的拷贝。
- 它有一个特性:只能单独一个进程写或读,如果A和B进程同时写,会造成数据的混乱,(所以需要搭配信号量来使用,比较好)
2. 函数原型
//创建或获取一个共享内存:成功返回共享内存ID,失败返回 -1
int shmget(key_t key, size_t size, int shmflg);
//连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回 -1
void *shmat(int shmid, const void *shmaddr, int shmflg);
//断开与共享内存的连接:成功返回0,失败返回 -1
int shmdt(const void *shmaddr);
//控制共享内存的相关信息:成功返回0,失败返回 -1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
部分参数解释
代码段:
编程思路:
- 创建/打开共享存储
- 连接映射共享存储
- 写入数据 strcpy
- 断开释放共享存储
- 干掉共享存储
shm_write
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//int shmget(key_t key, size_t size, int shmflg);
int main()
{
int shmid;
char *shmaddr;
key_t key;
key = ftok(".",1);
shmid = shmget(key,1024*4,IPC_CREAT|0666);
if(shmid == -1){
printf("shmget noOk\n");
exit(-1);
}
shmaddr = shmat(shmid,0,0);
printf("shmat ok\n");
strcpy(shmaddr,"chenlichen");
sleep(5);
shmdt(shmaddr);
shmctl(shmid, IPC_RMID, 0);
printf("quit\n");
return 0;
}
shm_get
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
//int shmget(key_t key, size_t size, int shmflg);
int main()
{
int shmid;
char *shmaddr;
key_t key;
key = ftok(".",1);
shmid = shmget(key,1024*4,0);
if(shmid == -1){
printf("shmget noOk\n");
exit(-1);
}
shmaddr = shmat(shmid,0,0);
printf("shmat ok\n");
printf("data: %s\n:",shmaddr);
shmdt(shmaddr);
printf("quit\n");
return 0;
}
运行效果:
前3种通信方式概况图:
4.信号
2.Linux——信号_单片机爱好者之家的博客-CSDN博客