管道

文章目录

    • 1 无名管道
      • 1.1 用法
      • 1.2 使用示例
    • 2 有名管道
      • 2.1 mkfifo函数介绍
      • 2.2 示例

1 无名管道

1.1 用法

pipe函数:

头文件:

#include 

函数原型:

int pipe(int pipefd[2]);

返回值:

  • 成功:0
  • 失败:-1

特点:

  • 特殊文件(没有名字),无法使用open,但是可以使用close。
  • 只能通过子进程继承文件描述符的形式来使用
  • write和read操作可能会阻塞进程
  • 所有文件描述符被关闭之后,无名管道被销毁

使用步骤:

  • 父进程pipe无名管道
  • fork子进程
  • close无用端口
  • write/read读写端口
  • close读写端口

1.2 使用示例

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX_DATA_LEN 256
#define DELAY_TIME 1

int main()
{
    pid_t pid;
    int pipe_fd[2];
    char buf[MAX_DATA_LEN];
    const char data[] = "Pipe Test Program";
    int real_read, real_write;

    memset((void*)buf, 0, sizeof(buf));

    /* 创建管道 */
    if (pipe(pipe_fd) < 0)
    {
        printf("pipe create error\n");
        exit(1);
    }

    /* 创建一子进程 */
    if ((pid = fork()) == 0)
    {
        /* 子进程关闭写描述符,并通过使子进程暂停 3s 等待父进程已关闭相应的读描述符 */
        close(pipe_fd[1]);
        sleep(DELAY_TIME * 3);

        /* 子进程读取管道内容 */
        if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
        {
            printf("%d bytes read from the pipe is '%s'\n", real_read, buf);
        }

        /* 关闭子进程读描述符 */
        close(pipe_fd[0]);

        exit(0);
    }
    
    else if (pid > 0)
    {
        /* 父进程关闭读描述符,并通过使父进程暂停 1s 等待子进程已关闭相应的写描述符 */
        close(pipe_fd[0]);

        sleep(DELAY_TIME);
        
        if((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
        {
            printf("Parent write %d bytes : '%s'\n", real_write, data);
        }
        
        /*关闭父进程写描述符*/
        close(pipe_fd[1]);

        /*收集子进程退出信息*/
        waitpid(pid, NULL, 0);

        exit(0);
    }
}


2 有名管道

2.1 mkfifo函数介绍

mkfifo函数:

头文件:

#include 
#include 

函数原型:

int mkfifo(const char *filename,mode_t mode)

返回值:

  • 成功:0
  • 失败:-1

特点:

  • 有文件名,可以使用open函数打开
  • 任意进程间数据传输
  • write和read操作可能会阻塞进程
  • write具有"原子性"

使用步骤:

  • 第一个进程mkfifo有名管道
  • open有名管道,write/read数据
  • close有名管道
  • 第二个进程open有名管道,read/write数据
  • close有名管道

2.2 示例

/**fifo.c */

/**
 * 无名管道,它只能用于具有亲缘关系的进程之间,这就大大地限制了管道的使用。有名管道的出现突破了这种限制, 它可以使互不相关的两个进程实现彼此通信。 
 * 该管道可以通过路径名来指出,并且在文件系统中是可见的。在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,使用非常方便。
 * 不过 FIFO 是严格地遵循先进先出规则的,对管道及 FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾,它们不支持如 lseek()等文件定位操作。
 * 有名管道的创建可以使用函数 mkfifo(),该函数类似文件中的 open()操作,可以指定管道的路径和打开的模式
 * 
 * 在创建管道成功之后,就可以使用 open()、 read()和 write()这些函数了。与普通文件的开发设置一样,对于为读而打开的管道可在 open()中设置 O_RDONLY,
 * 对于为写而打开的管道可在 open()中设置 O_WRONLY,在这里与普通文件不同的是阻塞问题。由于普通文件的读写时不会出现阻塞问题,而在管道的读写中却有阻塞的可能,
 * 这里的非阻塞标志可以在 open()函数中设定为 O_NONBLOCK。下面分别对阻塞打开和非阻塞打开的读写进行讨论。
 * (1)对于读进程。
 *       若该管道是阻塞打开,且当前 FIFO 内没有数据,则对读进程而言将一直阻塞到有数据写入。
 *       若该管道是非阻塞打开,则不论 FIFO 内是否有数据,读进程都会立即执行读操作。即如果 FIFO内没有数据,则读函数将立刻返回 0。
 * (2)对于写进程。
 *       若该管道是阻塞打开,则写操作将一直阻塞到数据可以被写入。
 *       若该管道是非阻塞打开而不能写入全部数据,则读操作进行部分写入或者调用失败
 * 
 * 函数原型 int mkfifo(const char *filename,mode_t mode)
 * 函数传入值 filename:要创建的管道名字(包含路径)
 * 函数传入值 mode:
 *       O_RDONLY:读管道
 *       O_WRONLY:写管道
 *       O_RDWR:读写管道
 *       O_NONBLOCK:非阻塞
 *       O_CREAT:如果该文件不存在,那么就创建一个新的文件,并用第三个参数为其设置权限
 *       O_EXCL:如果使用 O_CREAT 时文件存在,那么可返回错误消息。这一参数可测试文件是否存在
 * 函数返回值:
 *       0:成功
 *       EACCESS:参数 filename 所指定的目录路径无可执行的权限
 *       EEXIST:参数 filename 所指定的文件已存在
 *       ENAMETOOLONG:参数 filename 的路径名称太长
 *       ENOENT:参数 filename 包含的目录不存在
 *       ENOSPC:文件系统的剩余空间不足
 *       ENOTDIR:参数 filename 路径中的目录存在但却非真正的目录
 *       EROFS:参数 filename 指定的文件存在于只读文件系统内
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define MYFIFO "myfifo"    /* 有名管道文件名*/

#define MAX_BUFFER_SIZE PIPE_BUF /* 4096 定义在于 limits.h 中*/


void fifo_read(void)
{
    char buff[MAX_BUFFER_SIZE];
    int fd;
    int nread;

    printf("***************** read fifo ************************\n");
    /* 判断有名管道是否已存在,若尚未创建,则以相应的权限创建*/
    if (access(MYFIFO, F_OK) == -1)
    {
        if ((mkfifo(MYFIFO, 0666) < 0) && (errno != EEXIST))
        {
            printf("Cannot create fifo file\n");
            exit(1);
        }
    }

    /* 以只读阻塞方式打开有名管道 */
    fd = open(MYFIFO, O_RDONLY);
    if (fd == -1)
    {
        printf("Open fifo file error\n");
        exit(1);
    }

    memset(buff, 0, sizeof(buff));

    if ((nread = read(fd, buff, MAX_BUFFER_SIZE)) > 0)
    {
        printf("Read '%s' from FIFO\n", buff);
    }

   printf("***************** close fifo ************************\n");

    close(fd);

    exit(0);
}


void write_fifo(void)
{
    int fd;
    char buff[] = "this is a fifo test demo";
    int nwrite;

    sleep(2);   //等待子进程先运行

    /* 以只写阻塞方式打开 FIFO 管道 */
    fd = open(MYFIFO, O_WRONLY | O_CREAT, 0644);
    if (fd == -1)
    {
        printf("Open fifo file error\n");
        exit(1);
    }

    printf("Write '%s' to FIFO\n", buff);
    /*向管道中写入字符串*/
    nwrite = write(fd, buff, MAX_BUFFER_SIZE);
    
    if(wait(NULL))  //等待子进程退出
    {
        close(fd);
        exit(0);
    }

}


int main()
{
    pid_t result;
    /*调用 fork()函数*/
    result = fork();

    /*通过 result 的值来判断 fork()函数的返回情况,首先进行出错处理*/
    if(result == -1)
    {
        printf("Fork error\n");
    }


    else if (result == 0) /*返回值为 0 代表子进程*/
    {
        fifo_read();
    }

    else /*返回值大于 0 代表父进程*/
    {
        write_fifo();
    }

    return result;
}




参考资料:

  1. [野火]i.MX Linux开发实战指南

你可能感兴趣的:(所学所思所想)