有名管道支持无亲缘关系的进程之间通信,缺陷是只支持单机(socket扩展性好),一般用于c++后台进程和C守护进程之间通信。管道是内核维护的缓冲区,可以认为是文件机制,维护文件读写而不是socket。一个管道对应一个文件,管道名对两端的进程透明,是CS模式,服务器端负责维护和监听管道,客户端负责写入管道。管道一般支持单次通信的最大字节数是4096字节,可以用ulimit -a命令查看,如下:
[root@1dot1dot1dot1 pipe]# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 30507
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 65535
pipe size (512 bytes, -p) 8
管道大小是512 * 8 = 4096字节。
管道设计:一个server进程,多个client进程,server和每个client之间都需要一条管道,管道名是唯一的,一般用client的pid作为后缀用来区分不同client。管道是半双工的,若一个server进程想和一个client进程交互,一般设计是使用两条管道防止由于设计不当导致通信失败,其中一条管道用来client向server写数据,一条用来server向client写数据。
阻塞的几种情况:
1.阻塞只读open,进程在这里阻塞,直到有写端open
2.非阻塞只读open,执行ok
3.阻塞只写open,进程在这里阻塞,直到有写端open
4.非阻塞只写open,执行failed,原因是没有读端,不允许只有写端
总结:只有读端允许非阻塞读,因为没人写无所谓,等着有人写就行了,但是没有读端不允许写,因为写了没人接收
管道通信的一个常见错误:
Resource temporarily unavailable:这是全局变量error中保存的错误信息,是在非阻塞模式下调用了阻塞操作导致的,比如以非阻塞模式open,然后调用read读,但是read是从文件中读取的,会阻塞,那么这个时候,若读取不到数据,设置全局error
CS模式源代码如下,包含initialize.c,server.c,client.c,一般server负责初始化管道,这里单独提取出来,放到initialize.c中,只初始化一次,后续使用即可
initialize.c:
#include
#include
#include
#include
#include
#include
void main()
{
if (access("/tmp/server_write", F_OK) != 0)
{
if (mkfifo("/tmp/server_write", 0777) < 0)
{
printf("create /tmp/server_write failed\n");
return;
}
}
if (access("/tmp/server_read", F_OK) != 0)
{
if (mkfifo("/tmp/server_read", 0777) < 0)
{
printf("create /tmp/server_read failed\n");
return;
}
}
printf("create all FIFO success\n");
}
server.c
#include
#include
#include
#include
#include
void main()
{
int fd[2];
char buf[100] = {0};
int n;
printf("before open\n");
/* 打开读管道 */
fd[0] = open("/tmp/server_read", O_NONBLOCK | O_RDONLY);
if (fd[0] < 0)
{
printf("server open fd[0] failed\n");
return;
}
printf("after open\n");
/* 循环从读管道中读取数据 */
while (1)
{
n = read(fd[0], buf, 100);
if (n == 0)
{
printf("no data, sleep 1s\n");
sleep(1);
}
else if (n == -1)
{
printf("n is -1, sleep(1)\n");
sleep(1);
}
/* 若读取到了数据,打开写管道,回复客户端 */
else
{
printf("data from client, %s\n", buf);
/* 写通道需要阻塞获取,因为可能没有读端在监听 */
fd[1] = open("/tmp/server_write", O_WRONLY);
if (fd[1] < 0)
{
printf("server open fd[1] failed\n");
close(fd[0]);
return;
}
/* 打开后说明有人读了,写入数据 */
n = write(fd[1], "llllll", 6);
if (n < 0)
{
printf("server write failed\n");
}
sleep(1);
}
}
close(fd[0]);
close(fd[1]);
}
client.c
#include
#include
#include
#include
#include
void main()
{
int fd[2];
char buf[100] = {0};
int n;
/* 打开写管道,这里阻塞,否则若没有读端打开,open会返回失败 */
fd[1] = open("/tmp/server_read", O_WRONLY);
if (fd[1] < 0)
{
printf("client open fd[1] failed\n");
return;
}
/* 写入数据 */
n = write(fd[1], "abc123", 6);
if (n < 0)
{
printf("client write failed\n");
return;
}
/* 打开读通道 */
fd[0] = open("/tmp/server_write", O_RDONLY);
if (fd[0] < 0)
{
printf("server open fd[0] failed\n");
close(fd[1]);
return;
}
/* 从读通道读取数据 */
while (1)
{
if (read(fd[0], buf, 100))
{
printf("data from server is %s\n", buf);
break;
}
sleep(1);
}
memset(buf, 0, 100);
close(fd[0]);
close(fd[1]);
}