[Programação do sistema Linux] 24. Pipeline, pipe, fifo, comunicação de arquivos entre processos

Índice

gasoduto

Princípio de realização

característica

limitação

comportamento de leitura e escrita

ler tubo

escrever cachimbo

tamanho do buffer

valor de retorno

Vantagens e desvantagens

vantagem

deficiência

cano

parâmetro pipefd[2]

valor de retorno

código de teste 1

Resultado dos testes

Código de teste 2

Resultado dos testes

Código de teste 3

Resultado dos testes

fifo

Método de criação

nome do caminho do parâmetro

modo de parâmetro

valor de retorno

Código de teste 4

Resultado dos testes

Código de teste 5

Resultado dos testes

comunicação de arquivos entre processos

Código de teste 6

Resultado dos testes

gasoduto

Princípio de realização

O mecanismo de fila do anel do kernel é implementado usando buffers do kernel, o que é relativamente simples.

característica

  1. documento falso.

  2. Os dados no pipe só podem ser lidos uma vez.

  3. Os dados no pipeline só podem fluir em uma direção.

limitação

  1. Você só pode escrever sozinho, não ler sozinho.

  2. Os dados não podem ser lidos repetidamente.

  3. comunicação semi-duplex.

  4. Disponível entre processos de parentesco.

comportamento de leitura e escrita

ler tubo

O pipeline contém dados: read retorna o número de bytes realmente lidos.

Pipeline sem dados:

  1. Todas as extremidades de gravação são fechadas e a função de leitura retorna 0.

  2. O final da gravação não é fechado e a função de leitura é bloqueada e aguarda.

escrever cachimbo

Todas as extremidades de leitura são fechadas, terminação anormal, causada pelo sinal SIGPIPE.

O final da leitura não está fechado:

  1. Os dados do pipeline estão cheios, bloqueando a espera.

  2. Se os dados do pipeline não estiverem completos, retorne o número de bytes gravados.

tamanho do buffer

ulimit -a

man 3 fpathconf

 

valor de retorno

Sucesso: o tamanho do tubo.

Falha: -1.

Vantagens e desvantagens

vantagem

Comparados aos sinais, os soquetes implementam comunicação entre processos, que é muito mais simples.

deficiência

  1. Somente a comunicação unidirecional é possível e dois canais precisam ser estabelecidos para a comunicação bidirecional.

  2. Só pode ser utilizado para comunicação entre processos pai-filho e irmãos (com um ancestral comum). O problema foi resolvido posteriormente usando pipes nomeados fifo.

cano

Crie e abra um pipeline.

man 2 pipe

parâmetro pipefd[2]

pipefd[0]: fim da leitura.

pipefd[1]: fim da gravação.

valor de retorno

Sucesso: 0

Falha: -1

código de teste 1

O processo pai usa o pipe para escrever conteúdo, o processo filho usa o pipe para ler o conteúdo e envia o conteúdo lido para o 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;
}

Resultado dos testes

Código de teste 2

Use pipes para implementar o comando 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;
}

Resultado dos testes

Código de teste 3

Implemente o comando ls | wc -l usando interprocesso irmão.

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

Resultado dos testes

fifo

        Os FIFOs são frequentemente chamados de pipes nomeados para distingui-los dos pipes. Pipes só podem ser usados ​​entre processos "relacionados ao sangue". Mas através do FIFO, processos não relacionados também podem trocar dados. Os arquivos FIFO não possuem blocos de dados no disco e são usados ​​apenas para identificar um canal no kernel. Cada processo pode abrir este arquivo para leitura/gravação. Na verdade, ele está lendo e gravando o canal do kernel para realizar a comunicação entre processos.

Método de criação

mkfifo 管道文件名
man 3 mkfifo

nome do caminho do parâmetro

Nome do arquivo do pipeline.

modo de parâmetro

Permissões de arquivo de pipeline.

valor de retorno

Sucesso: 0

Falha: -1

Código de teste 4

Use mkfifo para criar um arquivo de pipeline.

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

Resultado dos testes

Código de teste 5

Use pipes para conectar e comunicar-se com dois processos não relacionados.

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

Resultado dos testes

comunicação de arquivos entre processos

Use arquivos para completar a comunicação entre processos não relacionados.

Código de teste 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;
}

Resultado dos testes

Acho que você gosta

Origin blog.csdn.net/CETET/article/details/132267338
Recomendado
Clasificación