Golang编程语言知识介绍


  • 首页

  • todo

  • 思考

  • life

  • food

  • OS

  • lua

  • redis

  • Golang

  • C

  • TCP/IP

  • ebpf

  • p4

  • OpenVPN

  • IPSec

  • L2TP

  • DNS

  • distributed

  • web

  • OpenWRT

  • 运维

  • Git

  • 鸟哥的私房菜

  • IT杂谈

  • 投资

  • About Me

  • 友情链接

  • FTP

  • 搜索
close

PIPE 管道

时间: 2024-03-20   |   分类: c     |   阅读: 1291 字 ~3分钟

官方参考手册

https://manpages.courier-mta.org/htmlman7/pipe.7.html

示例代码

// O_NOATIME 宏定义的扩展要求
#define _GNU_SOURCE 1
#include <fcntl.h>
#undef _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/unistd.h>
#include <assert.h>
#include <limits.h>

static int pip[2];

void 
sleep_select(int sec)
{
    struct timeval timeout;

    timeout.tv_usec = 0;
    timeout.tv_sec = sec;

    select(0, NULL, NULL, NULL, &timeout);
}

static void 
*thread_read(void *userdata)
{
    char data[5] = {0};
    int ret;

    printf("p0: %d\n", pip[0]);
    printf("p1: %d\n", pip[1]);

    while (1)
    {
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(pip[0], &fds);
        struct timeval timeout = {10, 0};
        ret = select(pip[0] + 1, &fds, NULL, NULL, &timeout);
        if (ret < 0)
        {
            printf("select error\n");
            break;
        }
        else if (ret == 0)
        {
            // printf("no fd ready\n");
            continue;
        }
        else
        {
            if (FD_ISSET(pip[0], &fds) > 0)
            {
                read(pip[0], data, 5);
                printf("data: %s\n", data);
            }
        }
    }
}

static void *
thread_write(void)
{
    printf("thread_write\n");
    char data[] = "pipe-data";
    while (1)
    {
        sleep_select(1);
        write(pip[1], data, sizeof(data));
        /* 单次写入的数据长度超过 PIPE_BUF 时, 内核不保证写入的原子性*/
        if (sizeof(data) > PIPE_BUF)
        {
            printf("warn data is too long\n");
        }
    }
}

int main(int argc, char *argv[])
{
    char data[] = "data";
    pthread_t id;
    if (pipe(pip))
    {
        printf("pipe error\n");
        exit(-1);
    }

    printf("p0: %d\n", pip[0]);
    printf("p1: %d\n", pip[1]);

    fcntl(pip[0], F_SETFL, O_NOATIME); /* 提高性能 */
    fcntl(pip[1], F_SETFL, O_NOATIME);

    if (pthread_create(&id, NULL, thread_read, NULL))
    {
        printf("create thread error\n");
        exit(-1);
    }

    printf("pthread id = %x\n", id);
    pthread_create(&id, NULL, thread_write, NULL);

    // signal(SIGUSR1, mysignal);
    while (1)
    {
        sleep_select(2);
    }

    pthread_join(id, NULL);
}

基本介绍

无名管道(4):通过管道同步进程
通过管道同步进程
管道其实就是一个文件

PIPE通信应用
通过管道同步进程
管道自带同步互斥机制:
管道的内核实现:fs/pipe.h
通过内核的锁、等待队列等机制实现
Write操作可能会阻塞进程
当内存缓冲区已满被读进程锁定
直到所有数据被写入到管道为止
Read操作进程可能会阻塞进程
读进程可以休眠在等待队列
直到所有子进程都关闭了管道的写入端描述符为止
父进程的写入端描述符也要关闭,否则父进程读管道时也会被阻塞
只有当所有的写端描述符都已关闭,且管道中的数据都被读出,对读端描述符调用read函数才会返回0(即读到EOF标志)
当所有的读取端和写入端都关闭,管道才能被销毁
管道缓冲区设置
管道缓冲区
管道对应的内存缓冲区大小
PIPE_BUF的容量是有限的:默认是65536字节
在不同OS下PIPE_BUF大小设置不同:在limits.h头文件中定义
写入管道的数据超过PIPE_BUF大小,内核会分割几块传输
最大值/proc/sys/fs/pipe-maxsize
查看打开的管道文件:cat /proc/PID/fd
设置缓冲区大小
特权用户:可以修改上限值
设置缓冲区大小:fcntl(fd, F_SETPIPE, size)

参考资料连接

管道的原子性

一个管道的容量是有限的。POSIX规定,少于 PIPE_BUF 的写操作必须原子完成:要写的数据应被连续的写到管道;大于 PIPE_BUF 的写操作可能是非原子的: 内核可能会把此数据与其它进程的对此管道的写操作交替起来。POSIX规定PIPE_BUF至少为512B(linux中为 4096B),具体的语义如下: 其中n为要写的字节数
      n <= PIPE_BUF, O_NONBLOCK无效:原子的写入n个字节。如果管道当前的剩余空间不足以立即写入n个字节,就阻塞直到有足够的空间。
      n <= PIPE_BUF, O_NONBLOCK有效:写入具有原子性,如果有足够的空间写入n个字节,write立即成功返回。否则一个都不写入,返回错误,并设置errno为EAGAIN。
      n >   PIPE_BUF, O_NONBLOCK无效:非原子写。可能会和其它的写进程交替写。write阻塞直到将n个字节写入管道。
      n >   PIPE_BUF, O_NONBLOCK有效:如果管道满,则write失败,返回错误,并将errno设置为 EAGIN。如果不满,则返回写入的字节数为1~n,即部分写入,写入时可能有其他进程穿插写入。

结论:
1、当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
2、当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

参考资料连接

#c#
永安湖城市森林公园-春游
三星观景平台-双流-爬山
shankusu2017@gmail.com

shankusu2017@gmail.com

日志
分类
标签
GitHub
© 2009 - 2025
粤ICP备2021068940号-1 粤公网安备44011302003059
0%