[리눅스 시스템 프로그래밍] 24. 파이프라인, 파이프, FIFO, 프로세스 간 파일 통신

목차

관로

실현원리

특성

한정

읽기와 쓰기 행동

파이프 읽기

파이프 쓰기

버퍼 크기

반환 값

장점과 단점

이점

결점

파이프

매개변수 파이프fd[2]

반환 값

테스트 코드 1

시험 결과

테스트 코드 2

시험 결과

테스트 코드 3

시험 결과

FIFO

생성방법

매개변수 경로명

매개변수 모드

반환 값

테스트 코드 4

시험 결과

테스트 코드 5

시험 결과

프로세스 간 파일 통신

테스트 코드 6

시험 결과

관로

실현원리

커널 링 링 큐 메커니즘은 비교적 간단한 커널 버퍼를 사용하여 구현됩니다.

특성

  1. 가짜 문서.

  2. 파이프의 데이터는 한 번만 읽을 수 있습니다.

  3. 파이프라인의 데이터는 한 방향으로만 흐를 수 있습니다.

한정

  1. 혼자만 쓸 수 있고 혼자 읽을 수는 없습니다.

  2. 데이터를 반복해서 읽을 수 없습니다.

  3. 반이중 통신.

  4. 친족 과정 사이에서 사용 가능합니다.

읽기와 쓰기 행동

파이프 읽기

파이프라인에는 데이터가 있습니다. read는 실제로 읽은 바이트 수를 반환합니다.

데이터가 없는 파이프라인:

  1. 모든 쓰기 끝이 닫히고 읽기 함수는 0을 반환합니다.

  2. 쓰기 끝이 닫히지 않고 읽기 기능이 차단되고 대기합니다.

파이프 쓰기

SIGPIPE 신호로 인해 모든 읽기 끝이 닫히고 비정상 종료됩니다.

읽기 끝이 닫히지 않습니다.

  1. 파이프라인 데이터가 꽉 차서 대기가 차단됩니다.

  2. 파이프라인 데이터가 가득 차지 않은 경우 쓴 바이트 수를 반환합니다.

버퍼 크기

ulimit -a

man 3 fpathconf

 

반환 값

성공: 파이프의 크기.

실패: -1.

장점과 단점

이점

신호와 비교하여 소켓은 프로세스 간 통신을 구현하므로 훨씬 간단합니다.

결점

  1. 단방향 통신만 가능하며, 양방향 통신을 위해서는 두 개의 채널을 구축해야 합니다.

  2. 아버지-아들 프로세스와 형제 프로세스(공통 조상 포함) 간의 통신에만 사용할 수 있습니다. 이 문제는 나중에 fifo 명명된 파이프를 사용하여 해결되었습니다.

파이프

파이프라인을 만들고 엽니다.

man 2 pipe

매개변수 파이프fd[2]

Pipefd[0]: 읽기 끝.

Pipefd[1]: 쓰기 끝.

반환 값

성공: 0

실패: -1

테스트 코드 1

상위 프로세스는 파이프를 사용하여 내용을 작성하고 하위 프로세스는 파이프를 사용하여 내용을 읽고 읽은 내용을 터미널에 출력합니다.

#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;
}

시험 결과

테스트 코드 2

파이프를 사용하여 ls | wc -l 명령을 구현하십시오.

#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;
}

시험 결과

테스트 코드 3

형제 간 프로세스를 사용하여 ls | wc -l 명령을 구현합니다.

#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;
}

시험 결과

FIFO

        FIFO는 파이프와 구별하기 위해 흔히 명명된 파이프라고 불립니다. 파이프는 "혈액 관련" 프로세스 사이에서만 사용할 수 있습니다. 그러나 FIFO를 통해 관련되지 않은 프로세스도 데이터를 교환할 수 있습니다. FIFO 파일은 디스크에 데이터 블록이 없으며 커널에서 채널을 식별하는 데만 사용됩니다. 각 프로세스는 읽기/쓰기를 위해 이 파일을 열 수 있으며 실제로 프로세스 간 통신을 실현하기 위해 커널 채널을 읽고 씁니다.

생성방법

mkfifo 管道文件名
man 3 mkfifo

매개변수 경로명

파이프라인 파일 이름.

매개변수 모드

파이프라인 파일 권한.

반환 값

성공: 0

실패: -1

테스트 코드 4

mkfifo를 사용하여 파이프라인 파일을 생성합니다.

#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;
}

시험 결과

테스트 코드 5

파이프를 사용하여 관련되지 않은 두 프로세스를 연결하고 통신합니다.

/*
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;
}

시험 결과

프로세스 간 파일 통신

관련되지 않은 프로세스 간의 통신을 완료하려면 파일을 사용하십시오.

테스트 코드 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;
}

시험 결과

추천

출처blog.csdn.net/CETET/article/details/132267338