进程间通信(二)
@[toc]
一、管道的读写规则
-
1.当管道没有数据可以读取时:
-
O_NONBLOCK disable :read调用阻塞,即进程暂停执行,一直等到有数据到来位置
-
O_NONBLOCK enable :read调用返回-1,error为EAGIN
-
2.当管道满的时候,往管道中写数据:
-
O_NONBLOCK disable :write调用会阻塞,直到有进程读取数据
-
O_NONBLOCK enable : write调用返回-1, error为EAGIN
-
3.如果所有管道写端对应的文件描述符被关闭,则read返回0;
-
4.如果所有管道的读端对应的文件描述符都被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出
-
5.当要写入的数据量不大于PIPE_BUF时,linux保证写入的原子性
-
6.当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。
-
7.管道的特点:
-
只能用于具有共同祖先的进程(具有亲缘关系的进程)之间的通信
,通常一个管道有一个进程创建,然后该进程调用fork函数,这样父子进程都可以使用该管道,就可以完成父子进程间的通信 -
管道提供流式的服务
-
一般而言,进程退出,管道释放,所以
管道的生命周期随进程。
-
注意,所有引用管道的进程都退出,管道才能释放
-
一般而言,
内核会对管道操作进行同步与互斥
-
管道是半双工的,数据只能单方向流动;需要双向流动时,需要建立两个
管道
二、命名管道
-
1.命名管道的引入
-
根据上面可以得知管道的一个限制条件是只能用于具有亲缘关系的进程之间进行通信。
-
如果我们想在不相关的进程间进行通信,进行数据交换,可以使用FIFO文件完成,即命名管道。
-
命名管道是一种特殊的管道文件(P)。
-
2.创建命名管道
-
a.程序内部创建:
int mkfifo(const char *pathname, mode_t mode);
- b.命令行创建:
mkfifo filename
- 3.命名管道与匿名管道的区别
命名管道 | 匿名管道 |
---|---|
可以有命令行和mkfifo函数创建,打开用open | 由pipe函数创建 |
适用于同主机上的任意两个进程间的通信 | 只适用于具有亲缘关系的进程之间进行通信 |
命名管道和匿名管道的主要区别还是在于它们的创建和打开方式不同,一旦这些工作完成,那么它们之间具有相同的语义。
三、命名管道的写入规则
-
1.如果当前打开操作是为读而打开FIFO时
-
O_NONBLOCK disable : 阻塞到有相应进程为写而打开FIFO
-
O_NONBLOCK enable : 立即返回成功
-
2.如果当前打开操作是为写而打开FIFO时
-
O_NONBLOCK disable :阻塞到有相应进程为读而打开FIFO
-
O_NONBLOCK enable : 立即返回失败,错误码为ENXIO
四、举例
- 1.用命名管道实现文件的拷贝:
把文件的内容写入到管道
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
//读取文件,写入命名管道
mkfifo("tp",0664);
int infd = open("abc", O_RDONLY);
if(infd == -1)
{
//ERR_EXIT("open");
perror("open is error");
exit(0);
}
int outfd = open("tp",O_WRONLY);
if(outfd == -1)
{
//ERR_EXIT("open");
perror("open is error");
exit(1);
}
char buf[10];
int n = read(infd,buf,10);
if(n > 0 )
{
write(outfd,buf,n);
}
close(infd);
close(outfd);
return 0;
}
把管道的内容写到文件
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
//读取管道,写入目标文件
int outfd = open("abc.back",O_WRONLY | O_TRUNC,0664);
if(outfd == -1)
{
perror("open is error");
exit(1);
}
int infd = open("tp",O_RDONLY);
if(infd == -1)
{
perror("open is error");
exit(1);
}
char buf[10];
int n = read(infd,buf,10);
if(n >0)
{
write(outfd,buf,n);
}
close(infd);
close(outfd);
unlink("tp");
return 0;
}
- 2.用命名管道实现server&client通信
server端:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
int main()
{
umask(0);
if(mkfifo("mypipe",0664) < 0)
{
perror("make fifo is error");
exit(1);
}
int rfd = open("mypipe",O_RDONLY);
if(rfd < 0)
{
perror("open pipe is perror");
exit(1);
}
char buf[1024];
while(1)
{
buf[0] = 0;
printf("please wait ...!\n");
ssize_t s = read(rfd,buf,sizeof(buf)-1);
if(s > 0)
{
buf[s-1] = 0;
printf("client say: %s",buf);
}
else if (s == 0)
{
printf("client is quit!\n");
exit(1);
}
else
{
perror("read is orrer");
exit(1);
}
}
close(rfd);
return 0;
}
client端:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int wfd = open("mypipe",O_WRONLY);
if(wfd < 0)
{
perror("open pipe is error");
exit(1);
}
char buf[1024];
while(1)
{
buf[0] = 0;
printf("Please Enter!\n");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s <= 0)
{
perror("read is error");
continue;
}
else if(s > 0)
{
buf[s] = 0;
write(wfd,buf,sizeof(buf));
}
}
close(wfd);
return 0;
}
评论区