[Linux system programming] 24. Pipeline, pipe, fifo, inter-process file communication

Table of contents

pipeline

Realization principle

trait

limitation

reading and writing behavior

read pipe

write pipe

buffer size

return value

Advantages and disadvantages

advantage

shortcoming

pipe

parameter pipefd[2]

return value

test code 1

Test Results

Test code 2

Test Results

Test code 3

Test Results

fifo

Creation method

parameter pathname

parameter mode

return value

Test code 4

Test Results

Test code 5

Test Results

interprocess file communication

Test code 6

Test Results

pipeline

Realization principle

The kernel ring ring queue mechanism is implemented using kernel buffers, which is relatively simple.

trait

  1. fake document.

  2. Data in the pipe can only be read once.

  3. Data in the pipeline can only flow in one direction.

limitation

  1. You can only write by yourself, not read by yourself.

  2. Data cannot be read repeatedly.

  3. half-duplex communication.

  4. Available between kinship processes.

reading and writing behavior

read pipe

The pipeline has data: read returns the number of bytes actually read.

Pipeline without data:

  1. All write ends are closed, and the read function returns 0.

  2. The write end is not closed, and the read function blocks and waits.

write pipe

All read ends are closed, abnormal termination, caused by SIGPIPE signal.

The read end is not closed:

  1. The pipeline data is full, blocking waiting.

  2. If the pipeline data is not full, return the number of bytes written.

buffer size

ulimit -a

man 3 fpathconf

 

return value

Success: the size of the pipe.

Failed: -1.

Advantages and disadvantages

advantage

Compared with signals, sockets implement inter-process communication, which is much simpler.

shortcoming

  1. Only one-way communication is possible, and two channels need to be established for two-way communication.

  2. It can only be used for communication between father-son and sibling processes (with a common ancestor). The problem was later solved using fifo named pipes.

pipe

Create and open a pipeline.

man 2 pipe

parameter pipefd[2]

pipefd[0]: read end.

pipefd[1]: write end.

return value

Success: 0

Failed: -1

test code 1

The parent process uses the pipe to write content, the child process uses the pipe to read the content, and outputs the read content to the terminal.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int GuanDao_flag; //管道标志位
    int pipefd[2];
    char data[1024];   //接收的数据
    int leng;          //接收数据的长度
    pid_t JinCheng_ID; //进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建管道!\n");
    GuanDao_flag = pipe(pipefd); //创建管道
    if (GuanDao_flag == -1)
    {
        perror("创建管道错误");
    }
    printf("开始创建进程!\n");
    JinCheng_ID = fork();
    if (JinCheng_ID > 0) //父进程
    {
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), JinCheng_ID);
        close(pipefd[0]);                                             //关闭读端
        write(pipefd[1], "你好,世界!\n", strlen("你好,世界!\n")); //通过管道向子进程发送数据
        sleep(1);
        close(pipefd[1]);
        printf("这是父进程,父进程结束。\n");
    }
    else if (JinCheng_ID == 0) //子进程
    {
        printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
        close(pipefd[1]);                           //子进程关闭写端
        leng = read(pipefd[0], data, sizeof(data)); //接收父进程发送的数据
        write(STDOUT_FILENO, data, leng);           //将数据写到终端上
        close(pipefd[0]);
        printf("这是子进程,子进程结束。\n");
    }
    return 0;
}

Test Results

Test code 2

Implement the ls | wc -l command with pipes.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int GuanDao_flag; //管道标志位
    int pipefd[2];
    char data[1024];   //接收的数据
    int leng;          //接收数据的长度
    pid_t JinCheng_ID; //进程ID

    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());

    printf("开始创建管道!\n");
    GuanDao_flag = pipe(pipefd); //创建管道
    if (GuanDao_flag == -1)
    {
        perror("创建管道错误");
        exit(1);
    }
    printf("创建管道完成!\n");

    printf("开始创建进程!\n");
    JinCheng_ID = fork();
    if (JinCheng_ID == 0) //子进程
    {
        printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
        close(pipefd[1]);              //子进程关闭写端
        dup2(pipefd[0], STDIN_FILENO); //终端输入重定向到管道的读端
        execlp("wc", "wc", "-l", NULL);
        perror("子进程错误");
    }
    else if (JinCheng_ID > 0) //父进程
    {
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), JinCheng_ID);
        close(pipefd[0]);               //关闭读端
        dup2(pipefd[1], STDOUT_FILENO); //终端输出重定向到管道的写端
        execlp("ls", "ls", NULL);
        perror("父进程错误");
    }
    else if (JinCheng_ID == -1)
    {
        perror("创建进程错误");
        exit(1);
    }
    return 0;
}

Test Results

Test code 3

Implement ls | wc -l command using sibling interprocess.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    int GuanDao_flag; //管道标志位
    int pipefd[2];
    pid_t JinCheng_ID; //进程ID
    int i;

    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());

    printf("开始创建管道!\n");
    GuanDao_flag = pipe(pipefd); //创建管道
    if (GuanDao_flag == -1)
    {
        perror("创建管道错误");
        exit(1);
    }
    printf("创建管道完成!\n");

    printf("开始创建进程!\n");
    for (i = 0; i < 2; i++)
    {
        JinCheng_ID = fork();
        if (JinCheng_ID == -1)
        {
            perror("创建进程错误");
            exit(1);
        }
        else if (JinCheng_ID == 0) //子进程
        {
            break;
        }
    }
    if(i==0){
        printf("这是子1进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
        close(pipefd[0]);               //子1进程关闭读端
        dup2(pipefd[1], STDOUT_FILENO); //终端输出重定向到管道的写端
        execlp("ls", "ls", NULL);
        perror("子1进程错误");
    }
    else if(i==1){
        printf("这是子2进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
        close(pipefd[1]);              //子2进程关闭写端
        dup2(pipefd[0], STDIN_FILENO); //终端输入重定向到管道的读端
        execlp("wc", "wc", "-l", NULL);
        perror("子进程错误");
    }
    else if(i==2){
        printf("这是父进程,当前进程的ID是%d。\n", getpid());
        close(pipefd[0]);	//父进程关闭读端、写端,保证兄弟进程之间形成单向通信
        close(pipefd[1]);
        wait(NULL);
        wait(NULL);
        printf("父进程结束。\n");
    }
    return 0;
}

Test Results

fifo

        FIFOs are often called named pipes to distinguish them from pipes. Pipes can only be used between "blood related" processes. But through FIFO, unrelated processes can also exchange data. FIFO files have no data blocks on disk and are only used to identify a channel in the kernel. Each process can open this file for read/write. In fact, it is reading and writing the kernel channel to realize inter-process communication.

Creation method

mkfifo 管道文件名
man 3 mkfifo

parameter pathname

Pipeline filename.

parameter mode

Pipeline file permissions.

return value

Success: 0

Failed: -1

Test code 4

Use mkfifo to create a pipeline file.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

int main(int argc, char *argv[])
{
    int flag;
    flag=mkfifo("GuanDao",0664);
    if(flag==-1){
        perror("创建管道文件错误");
    }
    return 0;
}

Test Results

Test code 5

Use pipes to connect and communicate with two unrelated processes.

/*
CeShi5_1.c
	接收CeShi5_2进程的数据
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int fd, leng;
    char data[4096];
    printf("程序开始运行。\n");
    printf("开始打开管道文件的读端。\n");
    fd = open(argv[1], O_RDONLY);
    if (fd == -1)
    {
        perror("打开管道文件错误");
        exit(1);
    }
    printf("打开管道文件的读端完成。\n");

    printf("开始读取管道文件读端的数据。\n");
    while (1)
    {
        leng = read(fd, data, sizeof(data));
        if (leng > 0)
        {
            //printf("读取到数据为:");
            write(STDOUT_FILENO, "读取到数据为:", strlen("读取到数据为:"));
            write(STDOUT_FILENO, data, leng);
        }
    }
    close(fd);
    return 0;
}
/*
CeShi5_2.c
	向CeShi5_1进程发送数据
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int fd, i;
    char data[4096];
    printf("程序开始运行。\n");
    printf("开始打开管道文件的写端。\n");
    fd = open(argv[1], O_WRONLY);
    if (fd == -1)
    {
        perror("打开管道文件错误");
        exit(1);
    }
    printf("打开管道文件的写端完成。\n");

    printf("开始向管道文件写端写数据。\n");
    i = 1;
    while (1)
    {
        sprintf(data, "你好,世界!这是写进程第%d次向管道写端写数据。\n", i);
        write(fd, data, strlen(data));
        printf("第%d次写成功。\n", i);
        i++;
        sleep(1);
    }
    close(fd);
    return 0;
}

Test Results

interprocess file communication

Use files to complete communication between unrelated processes.

Test code 6

/*
CeShi6_1.c
优先执行数据的写入文件
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int fd, flag;
    char *data = "你好,世界!这是CeShi6_1进程写的。\n";
    char data1[1024];
    int leng;
    printf("程序开始运行。\n");
    printf("开始打开文件。\n");
    fd = open("temp.txt", O_RDWR | O_TRUNC | O_CREAT, 0664);
    if (fd == -1)
    {
        perror("打开文件错误");
        exit(1);
    }
    printf("打开文件完成。\n");

    printf("开始文件写数据。\n");
    write(fd, data, strlen(data));
    printf("写的数据是:%s", data);
    printf("文件写数据完成。\n");

    printf("开始睡大觉。\n");
    sleep(5);
    printf("睡醒了,康康文件的数据。\n");
    lseek(fd, 0, SEEK_SET); //设置偏移量从头开始
    leng = read(fd, data1, sizeof(data1));
    flag=write(STDOUT_FILENO,data1,leng);
    if(flag==-1){
        perror("输出错误");
        exit(1);
    }
    printf("我真是服了,把我数据给改了,CeShi6_2你个老6。\n");
    close(fd);
    return 0;
}
/*
CeShi6_2.c
读取文件数据和修改文件数据
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int fd, flag;
    char *data = "你好,世界!这是CeShi6_2进程写的。\n";
    char data1[1024];
    int leng;
    printf("程序开始运行。\n");

    printf("开始睡大觉。\n");
    sleep(2);
    printf("睡醒了,我是老6,把你CeShi6_1进程写的数据给改了。\n");

    printf("开始打开文件。\n");
    fd = open("temp.txt", O_RDWR);
    if (fd == -1)
    {
        perror("打开文件错误");
        exit(1);
    }
    printf("打开文件完成。\n");

    printf("让我康康你写了啥。\n");
    leng = read(fd, data1, sizeof(data1));
    write(STDOUT_FILENO, data1, leng);
    printf("哦豁,写了这玩意。\n");

    printf("开始文件写数据。\n");
    lseek(fd, 0, SEEK_SET); //设置偏移量从头开始
    write(fd, data, strlen(data));
    printf("写的数据是:%s", data);
    printf("文件写数据完成。\n");

    close(fd);
    return 0;
}

Test Results

Guess you like

Origin blog.csdn.net/CETET/article/details/132267338