Compreender o 'buffer' em C

Estou tendo muita dificuldade em entender as profundezas do buffering especialmente na programação C E procurei por muito tempo neste tópico, mas não encontrei algo satisfatório até agora.

Vou ser um pouco mais específico.: Eu entendo o conceito por trás dele (isto é, coordenação de operações por diferentes dispositivos de hardware e minimizando a diferença na velocidade desses dispositivos), mas eu apreciaria uma explicação mais completa destas e outras razões potenciais para buffering (e por completo eu quero dizer cheio quanto mais longo e mais profundo melhor) também seria realmente bom dar alguns exemplos concretos de como buffering é implementado em fluxos de E/S.

as outras perguntas seriam que eu notei que algumas regras no buffer flushing não são seguidas pelos meus programas tão estranhamente como isto soa como o seguinte fragmento simples:

#include <stdio.h>

int main(void)
{
    FILE * fp = fopen("hallo.txt", "w");

    fputc('A', fp);
    getchar();
    fputc('A', fp);
    getchar();

    return 0;
}

o programa destina-se a demonstrar que a entrada iminente irá descarregar o fluxo arbitrário imediatamente quando a primeira getchar() é chamado, mas isso simplesmente não acontece tão frequentemente como eu experimente e com muitas modificações, como eu quero - ele simplesmente não acontecer como por stdout (com printf(), por exemplo), o fluxo é liberado sem qualquer entrada solicitada, também, afastando a regra, portanto, estou entendendo essa regra de forma errada ou há outra coisa a considerar

estou a usar o Gnu GCC no Windows 8.1.

actualização:

Esqueci-me de perguntar que li em alguns sites como as pessoas se referem a literais de cordas, por exemplo. como buffers ou mesmo arrays como buffers; isto está correto ou estou perdendo alguma coisa? Por favor, explique também este ponto.

Author: Jonathan Leffler, 2015-01-17

3 answers

A palavra buffer é usada para muitas coisas diferentes na ciência da computação. No sentido mais geral, é qualquer pedaço de memória onde os dados são armazenados temporariamente até que sejam processados ou copiados para o destino final (ou outro buffer).

Como insinuou na pergunta, há muitos tipos de tampões, mas como um grupo amplo:
  1. Buffers de Hardware: estes são buffers onde os dados são armazenados antes de serem movidos para um dispositivo HW. Ou tampões de choque os dados são armazenados enquanto são recebidos do dispositivo HW até que sejam processados pela aplicação. Isto é necessário porque a operação de E / S geralmente tem requisitos de memória e tempo, e estes são cumpridos pelo buffer. Pense em dispositivos DMA que lêem / escrevem diretamente para a memória, se a memória não estiver configurada corretamente o sistema pode falhar. Ou dispositivos de som que devem ter precisão sub-microssegundo ou ele vai funcionar mal.

  2. 'buffers' de 'Cache': são 'buffers' em que os dados estão agrupados antes de escrever em/ler a partir de um arquivo/Dispositivo de modo que o desempenho é geralmente melhorado.

  3. Buffers auxiliares: você move os dados para/de tal buffer, porque é mais fácil para o seu algoritmo.

O Caso # 2 é o do teu FILE* Exemplo. Imagine que uma chamada para o escrever chamada de Sistema (WriteFile() em Win32) leva 1ms para apenas a chamada mais 1us para cada byte (tenha paciência, as coisas são mais complicadas no mundo real). Então, se você do:

FILE *f = fopen("file.txt", "w");
for (int i=0; i < 1000000; ++i)
    fputc('x', f);
fclose(f);
Sem buffering, este código demoraria cerca de 1000 segundos. No entanto, com um buffer de 10000 bytes, haverá apenas 100 chamadas de Sistema, 10000 bytes cada. Isso seria 100 * (1ms + 10000us) São só 0,1 segundos!

Note também que o SO irá fazer o seu próprio buffering, para que os dados sejam escritos no dispositivo real usando o tamanho mais eficiente. Isso será um buffer HW e cache ao mesmo tempo!

Sobre o seu problema com o rubor, os ficheiros são normalmente corado quando fechado ou corado manualmente. Alguns arquivos, como stdout são corados em linha, ou seja, são corados sempre que um '\n' é escrito. Também o stdin/stdout é especial: quando ler de stdin, então stdout é corado. Outros arquivos não foram tocados, apenas stdout. Isso é útil se você estiver escrevendo um programa interativo.

O meu caso #3 é, por exemplo, quando o fazes:

FILE *f = open("x.txt", "r");
char buffer[1000];
fgets(buffer, sizeof(buffer), f);
int n;
sscanf(buffer, "%d", &n);

Você usa o buffer para manter uma linha do arquivo, e então você analisa os dados da linha. Sim, podias. ligue fscanf() directamente, mas em outras APIs pode não haver a função equivalente, e além disso você tem mais controlo desta forma: você pode analisar o tipo se Linha, ignorar comentários, contar linhas...

Ou imagine que recebe um byte de cada vez, por exemplo, de um teclado. Você vai apenas acumular caracteres em um buffer e processar a linha quando a tecla Enter é pressionada. Isso é o que a maioria dos programas de console interativo fazem.
 6
Author: rodrigo, 2015-01-16 23:31:29
A primeira pergunta é um pouco ampla demais. Buffer é usado em muitos casos, incluindo armazenamento de mensagens antes do uso real, Uso de DMA, usos speedup e assim por diante. Em resumo, toda a coisa do buffering pode ser resumida como"salve meus dados, Deixe-me continuar a execução enquanto você faz algo com os dados".

Algumas vezes você pode modificar buffers depois de passá-los para funções, outras vezes não. Às vezes buffers são hardware, às vezes software. Às vezes eles residem em RAM, às vezes em outros tipos de memória.

Então, por favor, faça uma pergunta mais específica. Como um ponto para começar, use a wikipedia, é quase sempre útil: wiki

Quanto à amostra de código, não encontrei qualquer menção de todos os tampões de saída a serem descarregados {[[[0]}. Os Buffers para ficheiros são geralmente lavados em três casos:

  1. fflush() ou equivalente
  2. o ficheiro está fechado
  3. o buffer está transbordado.

Uma vez que nenhum destes casos é verdadeiro para ti, o ficheiro não é flushed (note que a terminação da aplicação é Não nesta lista).

 1
Author: Aneri, 2015-01-16 23:07:13

O substantivo "buffer" refere-se realmente a um uso, não a uma coisa distinta. Qualquer bloco de armazenamento pode servir como um buffer. O termo é intencionalmente usado neste sentido geral em conjunto com várias funções I / O, embora os documentos para as funções C I/O stream tendem a evitar isso. Tomando a função POSIX read() como exemplo, no entanto: "read() tenta ler até count bytes do descritor de ficheiro fd para o buffer a partir de buf". Tampao" nesse caso, significa simplesmente o bloco de memória no qual os bytes lidos serão gravados; é normalmente implementado como um char[] ou um bloco dinamicamente alocado.

Usa-se um buffer especialmente em conjunto com I/O porque alguns dispositivos (especialmente discos rígidos) são lidos de forma mais eficiente em pedaços de tamanho médio a grande, onde como os programas muitas vezes querem consumir esses dados em pedaços menores. Algumas outras formas de E / S, como a rede E/S, podem inerentemente vir em pedaços, de modo que você deve gravar cada bloco inteiro (em um buffer) ou então perder essa parte que você não está imediatamente pronto para consumir. Considerações semelhantes se aplicam à saída.

Quanto ao comportamento do seu programa de testes, a "regra" que esperava demonstrar é específica para consolar I/O, mas apenas um dos fluxos envolvidos Está ligado ao console.
 1
Author: John Bollinger, 2015-01-16 23:16:53