进程间通信(IPC)
英文名IPC,因为每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户地址空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
管道:
管道是Unix中最古老的进程间通信的形式,是一种最基本的IPC机制。
我们把从一个进程连接到另一个进程的数据流称为一个“管道”。
特点:
(1)只能用于有公共祖先的进程之间通信。
(2)管道提供流式服务
(3)生命周期随进程
(4)内核会对管道进行同步与互斥
(5)管道是半双工的,需要双方通信时,须建立两个管道。
匿名管道
#include<unistd.h>
int pipe(int fd[2]);
//成功返回0,失败返回错误代码
fd[0]为读而打开,fd[1]为写而打开
fd[1]的输出是fd[0]的输入。
单个进程中的管道几乎没有任何用处。管道只能在具有公共祖先的两个进程之间使用。通常,一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父进程和子进程之间使用了。
父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端,父进程调用fork创建了子进程,子进程也有两个文件描述符指向同一管道。
因为管道是单向通信的,所以父进程关闭写端,子进程关闭读端。子进程可以往管道里写,父进程可以在管道里读。
代码实现:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{
//(1)进程创建管道
int fds[2] = {0,0};
int ret = pipe(fds);
if(ret < 0)
{
perror("pipe");
return 1;
}
//(2)创建子进程
pid_t id = fork();
//父进程关闭写端,子进程关闭读端
if( id == 0)//子进程——>写
{
close(fds[0]);
char* msg = "hello father,I am your child";
while(1)
{
write(fds[1],msg,strlen(msg));
sleep(1);
}
}
else//父进程——>读
{
char buf[1024];
close(fds[1]);
while(1)
{
ssize_t s = read(fds[0],buf,sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("%s\n",buf);
}
}
}
return 0;
}
[a@localhost ~]$ ./a.out
hello father,I am your child
hello father,I am your child
hello father,I am your child
hello father,I am your child
命名管道
匿名管道只能用于有血缘关系的进程。命名管道(FIFO)的提出就突破了管道的限制,它可以以不同的方式方法调用(可以跨平台,跨语言,跨权限),只要知道命名管道的名字,发送到命名管道的信息可以被一切拥有指定授权的程序读取。
创建:
//(1)命令行上创建
$ mkfifo filename
//从程序里创建
int mkfifo(const char *filename,mode_t mode);//成功返回0,失败返回-1
创建命名管道
int main(int argc, char *argv[])
{
mkfifo("p2",0644);
return 0;
}
#include<stdio.h>
#include<sys/stat.h>
int main()
{
int ret = mkfifo("./myfifo",0666);
if(ret < 0)
{
perror("mkfifo");
return 1;
}
printf("mkfifo ok\n");
return 0;
}
[a@localhost ~]$ gcc fifo.c
[a@localhost ~]$ ./a.out
mkfifo ok
用命名管道实现server&client通信
server.c
client.c
makefile
结果: