Pergunta

Estou interessado em escrever os módulos do programa separadas que funcionam como linhas independentes que eu poderia enganchar junto com tubos. A motivação seria a de que eu poderia escrever e testar cada módulo de forma totalmente independente, talvez até mesmo escrevê-los em diferentes línguas, ou executar os diferentes módulos em máquinas diferentes. Há uma grande variedade de possibilidades aqui. Eu tenho tubulação usado por um tempo, mas estou familiarizado com as nuances de seu comportamento.

  • Parece que o fim de recepção irá bloquear à espera de entrada, que eu seria de esperar, mas o bloco final de envio, por vezes, à espera de alguém para ler a partir do fluxo?
  • Se eu escrever um EOF para o fluxo pode Eu continuo continuar a escrever a esse fluxo até eu fechá-lo?
  • Existem diferenças no comportamento chamado e tubos sem nome?
  • Será que isso importa qual extremidade do tubo abro primeiro com pipes nomeados?
  • é o comportamento de tubos consistentes entre sistemas Linux diferentes?
  • Será que o comportamento dos tubos dependem do shell que estou usando ou a forma como eu configurei ele?
  • Existem outras perguntas que eu deve estar se perguntando ou questões que eu deveria estar ciente de se eu quiser usar tubos desta forma?
Foi útil?

Solução

Uau, isso é um monte de perguntas. Vamos ver se eu posso cobrir tudo ...

Parece que a vontade fim de recepção bloquear à espera de entrada, o que eu faria esperar

Você espera corretamente um real 'ler' chamada será bloqueado até que algo está lá. No entanto, eu acredito que há algumas funções C que lhe permitirá 'espiada' o que (e quanto) está esperando no tubo. Infelizmente, não me lembro se isso bloqueia bem.

será o bloco final de envio, por vezes, esperando alguém para ler a partir do fluxo

Não, o envio nunca deve bloquear. Pense nas ramificações se isso fosse um tubo através da rede para outro computador. Você quer esperar (através possivelmente alta latência) para o outro computador para responder que ele recebeu? Agora, este é um caso diferente se o identificador leitor do destino foi fechado. Neste caso, você deve ter alguma verificação de erros para lidar com isso.

Se eu escrever um EOF para o fluxo posso manter continuar a escrever para esse fluxo até eu fechá-lo

Eu acho que isso depende do que a linguagem que você está usando e sua implementação de tubos. Em C, eu não digo. Em um shell linux, eu diria que sim. Alguém com mais experiência teria que responder a isso.

Existem diferenças no comportamento nomeados e tubos sem nome? Tanto quanto eu sei, sim. No entanto, eu não tenho muita experiência com o nome vs sem nome. Eu acredito que a diferença é:

  • sentido único vs comunicação bidirecional
  • leitura e escrita para o "in" e "out" streams de um fio

Será que isso importa qual extremidade do tubo I abra primeiro com pipes nomeados?

Geralmente não, mas você pode executar em problemas na inicialização tentando criar e ligar os fios entre si. Você precisaria ter uma thread principal que cria todos os sub-tópicos e sincroniza seus respectivos tubos uns com os outros.

É o comportamento dos tubos consistentes entre diferentes sistemas Linux?

Novamente, isso depende do que a linguagem, mas geralmente sim. Já ouviu falar de POSIX? Esse é o padrão (pelo menos para Linux, Windows faz a sua própria coisa).

Será que o comportamento dos tubos depende na casca eu estou usando ou a maneira que eu tenho configurou-lo?

Esta é entrar em um pouco mais de uma área cinzenta. A resposta deve haver uma vez que o shell deve ser essencialmente a fazer chamadas do sistema. No entanto, tudo até aquele ponto é para ganhar.

Existem outras questões que eu deveria estar perguntando

As perguntas que você pediu mostra que você tem uma compreensão razoável do sistema. Manter a pesquisar e foco em que nível você vai estar trabalhando (shell, C, etc.). Você vai aprender muito mais por apenas tentando embora.

Outras dicas

Isso tudo é baseado em um sistema UNIX-like; Eu não estou familiarizado com o comportamento específico de versões recentes do Windows.

Parece que o fim de recepção irá bloquear à espera de entrada, que eu seria de esperar, mas o bloco final de envio, por vezes, à espera de alguém para ler a partir do fluxo?

Sim, embora em uma máquina moderna pode não acontece muitas vezes. O tubo tem um buffer intermediário que pode potencialmente encher. Se isso acontecer, o lado de gravação do tubo vai realmente bloquear. Mas se você pensar sobre isso, não há um monte de arquivos que são grandes o suficiente para arriscar isso.

Se eu escrever um EOF para o fluxo pode Eu continuo continuar a escrever a esse fluxo até eu fechá-lo?

Um, você quer dizer como um CTRL-D, 0x04? Claro, desde que o fluxo é configurado dessa forma. Viz.

506 # cat | od -c
abc
^D
efg
0000000    a   b   c  \n 004  \n   e   f   g  \n                        
0000012

Existem diferenças no comportamento chamado e tubos sem nome?

Sim, mas eles são sutis e implementação dependentes. O maior deles é que você pode escrever para um pipe nomeado antes da outra extremidade está em execução; com tubos sem nome, os descritores de arquivo são compartilhadas durante o processo de fork / exec, então não há nenhuma maneira de acessar o buffer transitória sem os processos sendo up.

Será que isso importa qual extremidade do tubo abro primeiro com pipes nomeados?

Não.

é o comportamento de tubos consistente entre diferentes sistemas Linux?

Dentro da razão, sim. Tampão tamanhos etc podem variar.

Será que o comportamento dos tubos dependem do shell que estou usando ou a forma como eu configurei ele?

No. Quando você cria um tubo, debaixo das cobertas o que acontece é o seu processo pai (a casca) cria um tubo que tem um par de descritores de arquivos, em seguida, faz um exec fork como este pseudocódigo:

pai :

create pipe, returning two file descriptors, call them fd[0] and fd[1]
fork write-side process
fork read-side process

Write-side :

close fd[0]
connect fd[1] to stdout
exec writer program

Leia-side :

close fd[1]
connect fd[0] to stdin
exec reader program

Existem outras perguntas que eu deve estar se perguntando ou questões que eu deveria estar ciente de se eu quiser usar tubos desta forma?

É tudo o que você quer fazer realmente vai colocar para fora em uma linha como esta? Se não, você pode querer pensar em uma arquitetura mais geral. Mas a percepção de que ter muitos processos separados que interagem através do "estreitar" interface de um tubo é desejável é uma boa.

[Atualizado: Eu tive o arquivo de índices descritor revertida em primeiro lugar. Eles estão corretos agora, ver man 2 pipe.]

Como Dashogun e Charlie Martin observou, esta é uma grande questão. Algumas partes de suas respostas são imprecisos, então eu vou responder também.

Estou interessado em escrever os módulos do programa separadas que funcionam como linhas independentes que eu poderia enganchar junto com tubos.

Seja cauteloso de tentar usar pipes como um mecanismo de comunicação entre segmentos de um único processo. Porque você teria que ambas as extremidades de leitura e gravação do aberto da tubulação em um único processo, você nunca iria obter o EOF (zero bytes) indicação.

Se você estava realmente referindo-se a processos, então esta é a base da abordagem clássica Unix para a construção de ferramentas. Muitos dos programas padrão do Unix são filtros que lêem da entrada padrão, transformá-lo de alguma forma, e escrever o resultado na saída padrão. Por exemplo, tr, sort, grep e cat são todos os filtros, para citar apenas alguns. Este é um excelente paradigma a seguir quando os dados que você está manipulando o permite. Nem todas as manipulações de dados são favoráveis ??a esta abordagem, mas há muitos que são.

A motivação seria a de que eu poderia escrever e testar cada módulo de forma totalmente independente, talvez até mesmo escrevê-los em diferentes línguas, ou executar os diferentes módulos em máquinas diferentes.

pontos positivos. Esteja ciente de que não há realmente um mecanismo de tubo entre as máquinas, mas você pode chegar perto dele com programas como rsh ou (melhor) ssh. No entanto, internamente, tais programas podem ler dados locais a partir de tubos e enviar esses dados para máquinas remotas, mas eles se comunicam entre as máquinas mais soquetes, não utilizando tubos.

Há uma grande variedade de possibilidades aqui. Eu tenho tubulação usado por um tempo, mas estou familiarizado com as nuances de seu comportamento.

OK; fazer perguntas é um (bom) maneira de aprender. Experimentar é outra, é claro.

Parece que o fim de recepção irá bloquear à espera de entrada, que eu seria de esperar, mas o bloco final de envio, por vezes, à espera de alguém para ler a partir do fluxo?

Sim. Existe um limite para o tamanho de um tampão de tubo. Classicamente, este foi muito pequeno - 4096 ou 5120 eram valores comuns. Você pode achar que a moderna Linux utiliza um valor maior. Você pode usar fpathconf() e _PC_PIPE_BUF para descobrir o tamanho de um buffer pipe. POSIX requer apenas o tampão ser de 512 (isto é, é _POSIX_PIPE_BUF 512).

Se eu escrever um EOF para o fluxo pode Eu continuo continuar a escrever a esse fluxo até eu fechá-lo?

Tecnicamente, não há nenhuma maneira de escrever EOF para um fluxo; você fechar o descritor de tubulação para indicar EOF. Se você está pensando em controle-D ou controle-Z como um caractere EOF, em seguida, essas são apenas personagens regulares na medida em que os tubos estão em causa - eles só têm um efeito como EOF quando digitado em um terminal que está sendo executado no modo canônico (cozido ou normal).

Existem diferenças no comportamento chamado e tubos sem nome?

Sim, e não. As maiores diferenças são que os tubos não identificadas devem ser configurados por um processo e só pode ser usado por esse processo e as crianças que compartilham esse processo como um ancestral comum. Por outro lado, pipes nomeados podem ser usados ??por processos não associados anteriormente. O próximo grande diferença é uma consequência da primeira; com um tubo sem nome, você recebe de volta dois descritores de arquivos de uma única função (sistema) chamada para pipe(), mas você abrir um pipe FIFO ou nomeado usando a função open() regular. (Alguém deve criar um FIFO com a chamada mkfifo() antes de abri-lo; sem nome tubos não precisa de nenhum tipo de configuração prévia.) No entanto, quando você tem um descritor de arquivo aberto, não é precioso pouca diferença entre uma chamadatubo e um tubo sem nome.

Será que isso importa qual extremidade do tubo abro primeiro com pipes nomeados?

No. O primeiro processo para abrir a FIFO bloqueará (normalmente) até que haja um processo com a outra extremidade aberta. Se você abri-lo para ler e escrever (aconventional mas possível), então você não será bloqueado; se você usar o sinalizador O_NONBLOCK, você não será bloqueado.

É o comportamento dos tubos consistentes entre sistemas Linux diferentes?

Sim. Eu não ouvi falar de ou experimentou problemas com tubos em qualquer um dos sistemas onde eu usei-los.

Será que o comportamento dos tubos dependem do shell que estou usando ou a forma como eu configurei ele?

No: tubos e FIFOs são independentes do shell que você usa

.

Existem outras perguntas que eu deve estar se perguntando ou questões que eu deveria estar ciente de se eu quiser usar tubos desta forma?

Basta lembrar que você deve fechar o fim da leitura de um tubo no processo que vai ser escrito, e no final da escrita do tubo no processo que estará lendo. Se você quiser comunicação bidirecional sobre pipes, usar dois tubos separados. Se você criar arranjos de canalização complicados, cuidado com impasse - é possível. Um gasoduto linear não impasse, no entanto (embora se o primeiro processo nunca fecha sua saída, os processos a jusante pode aguardar indefinidamente).


I observado acima e em comentários a outras respostas que buffers tubos são classicamente limitados a tamanhos muito pequenos. @Charlie Martin contra-comentou que algumas versões do Unix têm buffers de tubos dinâmicas e estes podem ser muito grandes.

Eu não tenho certeza que aqueles que ele tem em mente. Eu usei o programa de teste que se segue no Solaris, AIX, HP-UX, MacOS X, Linux e Cygwin / Windows XP (resultados abaixo):

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

static const char *arg0;

static void err_syserr(char *str)
{
    int errnum = errno;
    fprintf(stderr, "%s: %s - (%d) %s\n", arg0, str, errnum, strerror(errnum));
    exit(1);
}

int main(int argc, char **argv)
{
    int pd[2];
    pid_t kid;
    size_t i = 0;
    char buffer[2] = "a";
    int flags;

    arg0 = argv[0];

    if (pipe(pd) != 0)
        err_syserr("pipe() failed");
    if ((kid = fork()) < 0)
        err_syserr("fork() failed");
    else if (kid == 0)
    {
        close(pd[1]);
        pause();
    }
    /* else */
    close(pd[0]);
    if (fcntl(pd[1], F_GETFL, &flags) == -1)
        err_syserr("fcntl(F_GETFL) failed");
    flags |= O_NONBLOCK;
    if (fcntl(pd[1], F_SETFL, &flags) == -1)
        err_syserr("fcntl(F_SETFL) failed");
    while (write(pd[1], buffer, sizeof(buffer)-1) == sizeof(buffer)-1)
    {
        putchar('.');
        if (++i % 50 ==  0)
            printf("%u\n", (unsigned)i);
    }
    if (i % 50 !=  0)
        printf("%u\n", (unsigned)i);
    kill(kid, SIGINT);
    return 0;
}

Eu seria curioso para obter resultados extras de outras plataformas. Aqui estão os tamanhos I encontrados. Todos os resultados são maiores do que eu esperava, confesso, mas Charlie e eu posso estar debatendo o significado de 'muito grande' quando se trata de tampão tamanhos.

  • 8196 - HP-UX 11.23 para IA-64 (fcntl (F_SETFL) falhou)
  • 16384 - Solaris 10
  • 16384 - MacOS X 10.5 (O_NONBLOCK não funcionou, embora fcntl (F_SETFL) não falhou)
  • 32768 - AIX 5.3
  • 65536 - Cygwin / Windows XP (O_NONBLOCK não funcionou, embora fcntl (F_SETFL) não falhou)
  • 65536 - SuSE Linux 10 (e CentOS) (fcntl (F_SETFL) falhou)

Um ponto que fica claro a partir desses testes é que O_NONBLOCK trabalha com tubos em algumas plataformas e não em outros.

O programa cria um tubo, e garfos. A criança fecha a extremidade gravação do tubo, e depois vai dormir até que ele recebe um sinal - é o que pause () faz. O pai então fecha a extremidade leitura do tubo, e define os sinalizadores no descritor de gravação de modo que não irá bloquear em uma tentativa de gravação em um tubo cheio. Em seguida, laços, escrever um carácter de cada vez, e imprimir um ponto para cada personagem escrito, e uma contagem e nova linha cada 50 caracteres. Quando detecta um problema write (buffer cheio, já que a criança não está lendo uma coisa), ele pára o loop, escreve a contagem final, e mata a criança.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top