linux有名管道

有名管道支持无亲缘关系的进程之间通信,缺陷是只支持单机(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]);
}

 

你可能感兴趣的:(linux)