进程运行时时具有独立性的,让它本身通信是困难的,所以进程通信的前提条件是让进程看到同一份资源(通常指的是某一块内存)
- 数据传输:一个进程将它的数据发送给另一个进程。
- 资源共享:多个进程间共享相同的资源。
- 通知事件:一个进程需要向另一个进程发送消息,通知它发送某种事件,例如操作系统发送信号给子进程。
- 进程控制:有些进程希望能完全控制另一个进程,此时可直接从希望能够拦截另一个进程的所有异常,并且知道它的状态改变。
- 管道
- 匿名管道pipe
- 命名管道
- System V IPC
- System V 消息队列
- System V 共享内存
- System V 信号量
- POSIX IPC
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
ls | grep“hell”
命令格式:命令A|命令B,即命令1的正确输出作为命令B的操作对象(下图应用别人的图片)
例如: ps aux | grep “test” 在 ps aux中的結果中查找test。
管道可以分为匿名管道与命名管道
- 匿名管道
用于具有亲缘关系进程的通信
利用pipe函数来实现
#include<string.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd[2] = {0};
pipe(fd);
//printf("%d %d",fd[0],fd[1]);
pid_t id = fork();
if(id == 0)
{
close(fd[0]);
char message[] = "i'm a child process";
while(1)
{
write(fd[1],message,strlen(message));
sleep(1);
}
}
else
{
close(fd[1]);
while(1){
char buf[1024];
ssize_t s = read(fd[0],buf,sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("i'm a father,gotchild message:%s\n",buf);
}
}
}
return 0;
}
管道读写规则
- 当没有数据可读时
- O_NONBLOCK disable:read调用阻塞,进程暂停执行,一直到有数据来
- O_NONBLOCK enable:read调用返回-1
- 当管道满时
- O_NONBLOCK disable:write调用阻塞,直到有进程取走数据。
- O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
- 如果所有管道写端对应的文件描述符被关闭,则返回-1
- 如果所有管道读端对应的文件描述符被关闭,则操作会产生信号SIGPIPE,可能会导致write进程退出
- 当要写入的数据量不大于PIPE_BUF时,linux保证操作的原子性。
(PIPE_BUF与平台版本有关系,一般为4到8k)
管道特点
- 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创键,然后fork出子进程,此后父子进程就能用该管道。
- 一般而言,管道的生命周期和进程相同,内核会对管道操作进行互斥与同步
- 管道是半双工的,数据只能从一个方向流动。
这里解释几个概念
多进程共享的内存资源叫临界资源,把访问临界资源的代码叫临界区在任何一个时刻只允许一个进程访问临界资源的行为叫做互斥。
互斥可以保证安全性,一个进程每次访问完临界资源就要排在等待队列的末尾。
在保证临界资源的安全的情况下(通常是互斥),让多进程访问临界资源具有一定的顺序行,我们叫做同步(协同同步步调,避免饥饿问题),此处与IO模型的同步异步模型无任何关系。
原子性:通常对临界资源的访问只有访问或不访问,没有中间态
半双工数据在一个时刻只能从一个方向发往另一个方向而不能同时双方发送,人的聊天也是半双工的,毕竟没有你说你的,我说我的的聊天方式(笑:D)
如果我们想要让两个进程完成双向通信,可以用两对管道实现具体方案为,一方读一方写,另一方写一方读。
- 命名管道
匿名管道的一个特点是只能在有亲缘关系的进程间通信,如果想在不相干的进程间交换数据,可以用FIFO命令为 mkfifo+文件名
同时我们可以mkfifo函数创建,再用read和write函数来让两进程间通信
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
//#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
//用来写入数据的
int fd = open("fifo",O_WRONLY);//客户端 client
if(fd < 0){
printf("mkfifo error");
}
char buf[1024];
while(1)
{
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s] = 0;
write(fd,buf,strlen(buf));
}
if(s == 0)
{
printf("celient end");
}
if(s < 0)
{
printf("read error");
}
}
}
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
//用来读数据的
mkfifo("fifo",0644);
int fd = open("fifo",O_RDONLY);
if(fd < 0){
printf("mkfifo error");
exit(1);
}
char buf[1024];
while(1)
{
int s =read(fd,buf,strlen(buf));
if(s>0)
{
buf[s] = 0;
printf("%s",buf);
}
if(s == 0)
{
printf("celient end");
}
if(s < 0)
{
printf("read error");
}
}
}
Makefile文件为
.PHONY:all
all:client serve
client:client.c
gcc -o $@ $^
serve:serve.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f client serve
匿名管道和命名管道的用法相似,且命名管道的用法更加简单。
所以可以看到,数据并没有拷贝,而是让两个进程看到同一份资源,系统所作的一共有两个步骤
- 在物理内存中开辟一块空间,使用函数shmget实现
- 将开辟的内存空间与进程的虚拟地址空间产生映射关系