Programação de aplicativos no ambiente Linux (1): Modelo geral de E / S

I. Visão geral

        Todas as chamadas de sistema que realizam operações de E / S referem-se ao arquivo aberto com um descritor de arquivo, um número inteiro não negativo. Os descritores de arquivo são usados ​​para representar todos os tipos de arquivos abertos, incluindo canais, FIFOs, soquetes, terminais, dispositivos e arquivos comuns. A maioria dos programas espera poder usar os três descritores de arquivo padrão a seguir: Quando se referem a esses descritores de arquivo no programa, eles podem ser representados por números (0,1,2).

Dois: controle de E / S de arquivo básico (sem cache)

1. Operações básicas de arquivo

(1) Abra um arquivo: abra

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

nome do caminho: para abrir o arquivo, a chamada retorna com sucesso um descritor de arquivo, que é usado para referir-se ao arquivo em chamadas de função subsequentes. Se ocorrer um erro, ele retorna -1 e define errno para o sinalizador de erro correspondente.

sinalizadores: máscara de bits, usada para especificar o modo de acesso ao arquivo.

Quando aberto cria um novo arquivo, o modo especifica as permissões de acesso do arquivo. Se open não especificar o sinalizador O_CREAT, você pode omitir o parâmetro de modo.

The following symbolic constants are provided for mode:

S_IRWXU  00700  user  (file owner) has read, write, and execute permis‐sion

S_IRUSR  00400 user has read permission

S_IWUSR  00200 user has write permission

S_IXUSR  00100 user has execute permission

S_IRWXG  00070 group has read, write, and execute permission

S_IRGRP  00040 group has read permission

S_IWGRP  00020 group has write permission

S_IXGRP  00010 group has execute permission

S_IRWXO  00007 others have read, write, and execute permission

S_IROTH  00004 others have read permission

S_IWOTH  00002 others have write permission

S_IXOTH  00001 others have execute permission

According to POSIX, the effect when other  bits  are  set  in  mode  is unspecified.  On Linux, the following bits are also honored in mode:

S_ISUID  0004000 set-user-ID bit

S_ISGID  0002000 set-group-ID bit (see inode(7)).

S_ISVTX  0001000 sticky bit (see inode(7)).

(2) Leia o conteúdo do arquivo: leia

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

(3) Gravar dados no arquivo: gravar

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

(4), feche o arquivo: fechar

#include <unistd.h>

int close(int fd);

(5) Deslocamento de arquivo: lseek

#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

O parâmetro offset especifica um valor em bytes. O parâmetro whence indica qual ponto de base deve ser referido para interpretar o parâmetro de deslocamento. Quando o parâmetro whence é SEEK_SET, o deslocamento deve ser um número não negativo, caso contrário, pode ser um número positivo ou negativo. lseek não pode ser aplicado a pipes, FIFOs, sockets ou terminais, caso contrário, a chamada falhará e errno será definido como ESPIPE.

SEEK_SET
    The file offset is set to offset bytes.

SEEK_CUR
    The file offset is set to its current location plus offset bytes.

SEEK_END
    The file offset is set to the size of the file plus offset bytes.

2. Bloqueio de arquivo

(1) descrição da função fcntl ()

       No caso em que o arquivo foi compartilhado, ou seja, quando vários usuários o utilizam juntos para operar um arquivo, esta é a forma que o Linux costuma utilizar para travar o arquivo para evitar competição por recursos compartilhados.

       Os bloqueios de arquivo incluem bloqueios de aviso e bloqueios obrigatórios. Os bloqueios sugeridos requerem que cada processo que bloqueia um arquivo deve verificar se há um bloqueio e respeitar os bloqueios existentes.Em geral, o kernel e o sistema não usam bloqueios consultivos. O bloqueio obrigatório é um bloqueio executado pelo kernel.Quando um arquivo é bloqueado para gravação, o kernel impede que outros arquivos sejam lidos e gravados. O uso de bloqueios obrigatórios tem um grande impacto no desempenho e cada operação de leitura e gravação deve verificar se há um bloqueio.

       No Linux, as funções que implementam o bloqueio de arquivo são lockf () e fcntl (), onde lockf () é usado para impor bloqueios consultivos aos arquivos e fcntl () pode não apenas impor bloqueios consultivos, mas também impor bloqueios obrigatórios. Ao mesmo tempo, fcntl () também pode bloquear um determinado registro do arquivo, ou seja, o bloqueio de registro.

       Os bloqueios de registro podem ser divididos em bloqueios de leitura e bloqueios de gravação. Os bloqueios de leitura também são chamados de bloqueios compartilhados, que permitem que vários processos estabeleçam bloqueios de leitura na mesma parte de um arquivo. O bloqueio de gravação também é chamado de bloqueio exclusivo.Somente um processo pode estabelecer um bloqueio de gravação em uma determinada parte do arquivo a qualquer momento.

      Você não pode estabelecer um bloqueio de leitura e um bloqueio de gravação ao mesmo tempo na mesma parte do arquivo.

(2) formato da função fcntl ()

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock );

cmd: {

    F_DUPFD:复制文件描述符
    F_GETFD:获得 fd 的 close-on-exec 标志,若标志未设置,则文件经过 exec()函
数之后仍保持打开状态
    F_SETFD:设置 close-on-exec 标志,该标志由参数 arg 的 FD_CLOEXEC 位决定
    F_GETFL:得到 open 设置的标志
    F_SETFL:设置文件状态标志(其中 O_RDONLY,O_WRONLY,O_RDWR,O_CREAT,O_EXCL ,
O_NOCTTY 和 O_TRUNC 不受影响,可以更改的标志有 O_APPEND,O_ASYNC,
O_DIRECT,O_NOATIME 和 O_NONBLOCK)
    F_GETOWN:检索将收到 SIGIO 和 SIGURG 信号的进程号或进程组号
    F_SETOWN:设置进程号或进程组号
    F_GETLK:根据 lock 参数值,决定是否上文件锁
    F_SETLK:设置 lock 参数值的文件锁
    F_SETLKW:这是 F_SETLK 的阻塞版本(命令名中的 W 表示等待(wait))。
在无法获取锁时,会进入睡眠状态;如果可以获取锁或者捕捉到信号则会返回
}

lock:{
    
    struct flock {
        short l_type;     Type of lock: F_RDLCK(共享锁), F_WRLCK(排斥锁), F_UNLCK解锁
        short l_whence;   How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END 
        off_t l_start;    Starting offset for lock 
        off_t l_len;      Number of bytes to lock 
        pid_t l_pid;      PID of process blocking our lock (set by F_GETLK and F_OFD_GETLK)
    };
}

(3) Controle de arquivo comum de fcntl

Obtenha o sinalizador de status do arquivo aberto: F_GETFL

int flags;

flags = fcntl(fd, F_GETFL);

if(flags & O_SYNC)
    printf("文件是以同步方式打开");

Julgar o modo de acesso é um pouco complicado, porque as três constantes O_RDONLY, O_WRONLY e O_RDWR não correspondem a um único bit no sinalizador de status de arquivo aberto. É necessário usar a máscara O_ACCMODE e sinalizar para comparar o resultado com 3 constantes:

acceccMode = flags & O_ACCMODE;

if(accessMode == O_WRONLY || accessMode == O_RDWR)
    printf("file is writable\n");

Modifique o sinalizador de status do arquivo aberto: F_SETFL

Os sinalizadores permitidos são: O_APPEND, O_NONBLOCK, O_NOATIME, O_ASYNC e O_DIRECT. O sistema irá ignorar a modificação de outros sinais.

int flags;

flags = fcntl(fd, F_GETFL);
if(flags == -1)
    return -1;
flags |= O_APPEND;
if(fcntl(fd, F_SETFL, flags) == -1)
    return -1;

3. Multiplexação

(1) Descrição da função

Existem 5 modelos de processamento de I / O, consulte para detalhes: https://www.cnblogs.com/f-ck-need-u/p/7624733.html

Bloqueio: Neste modelo, se a função de I / O chamada não completar a função relevante, o processo será suspenso e não retornará até que os dados relevantes sejam alcançados. Essa situação geralmente ocorre durante a leitura e gravação em equipamentos de dutos, equipamentos terminais e equipamentos de rede.

Sem bloqueio: Neste modelo, quando a operação de E / S solicitada não pode ser concluída, o processo não pode dormir e retorna imediatamente. E / S sem bloqueio permite aos usuários chamar operações de E / S sem bloqueio, como open (), write () e read (). Se a operação não puder ser concluída, um erro ou 0 será retornado imediatamente.

Reutilizar: Neste modelo, se a operação de I / O solicitada estiver bloqueada, e não estiver bloqueando realmente I / O, mas para deixar uma das funções esperar, durante este período, I / O pode realizar outras operações. As funções select () e poll () pertencem a este modelo.

Sinal: Neste modelo, ao instalar um programa de processamento de sinal, o sistema pode capturar automaticamente a chegada de um sinal específico para iniciar o I / O. Isso é determinado por uma operação de E / S que o kernel informa ao usuário quando pode iniciar.

Assíncrono: Neste modelo, quando um descritor está pronto para iniciar o I / O, o processo irá notificar o kernel.

       O modelo de multiplexação de E / S de select () e poll () é uma maneira eficiente de lidar com a multiplexação de E / S. Ele pode definir especificamente as condições de cada descritor de arquivo em questão no programa, o tempo de espera, etc. Ao retornar das funções select () e poll (), o kernel irá notificar o usuário sobre o número de descritores de arquivo que foram preparados . Condições preparadas etc. Usando os resultados de retorno das funções select () e poll (), você pode chamar as funções de processamento de E / S correspondentes.

(2) Formato de função

selecione () :

#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>


int select(int numfds, fd_set *readfds, fd_set *writefds,
         fd_set *exeptfds, struct timeval *timeout)

numfds:该参数值为需要监视的文件描述符的最大值加 1
readfds:由 select()监视的读文件描述符集合
writefds:由 select()监视的写文件描述符集合
exeptfds:由 select()监视的异常处理文件描述符集合
timeout:{
    NULL:永远等待,直到捕捉到信号或文件描述符已准备好为止
    具体值:struct timeval 类型的指针,若等待了 timeout 时间还没有检测到
任何文件描符准备好,就立即返回
        struct timeval
        {
            long tv_sec; /* 秒 */
            long tv_unsec; /* 微秒 */
        }
    0:从不等待,测试所有指定的描述符并立即返回
}
返回值:大于 0:成功,返回准备好的文件描述符的数目
0:超时;1:出错

Função de processamento do descritor de arquivo (macro):

FD_ZERO(fd_set *set)           清除一个文件描述符集
FD_SET(int fd, fd_set *set)    将一个文件描述符加入文件描述符集中
FD_CLR(int fd, fd_set *set)    将一个文件描述符从文件描述符集中清除
FD_ISSET(int fd, fd_set *set)  如果文件描述符 fd 为 fd_set集中的一个元素,则返回非零值,可以用于调用 select()之后测试文件描述符集中的文件描述符是否有变化

De modo geral, antes de usar a função select (), primeiro use FD_ZERO e FD_SET () para inicializar o conjunto de descritores de arquivo. Quando a função select () é usada, você pode usar FD_ISSET () para testar o conjunto de descritores em um loop. a operação do descritor de arquivo relacionado, use FD_CLR () para limpar o conjunto de descritores.

Votação():

#include <sys/types.h>
#include <poll.h>

int poll(struct pollfd *fds, int numfds, int timeout)

fds:struct pollfd 结构的指针,用于描述需要对哪些文件的哪种类型的操作进行监控。
struct pollfd
{
int fd; /* 需要监听的文件描述符 */
short events; /* 需要监听的事件 */
short revents; /* 已发生的事件 */
}
    events 成员描述需要监听哪些类型的事件,可以用以下几种标志来描述。
    {
        POLLIN:文件中有数据可读,下面实例中使用到了这个标志
        POLLPRI::文件中有紧急数据可读
        POLLOUT:可以向文件写入数据
        POLLERR:文件中出现错误,只限于输出
        POLLHUP:与文件的连接被断开了,只限于输出
        POLLNVAL:文件描述符是不合法的,即它并没有指向一个成功打开的文件
    }

numfds:需要监听的文件个数,即第一个参数所指向的数组中的元素数目
timeout:表示 poll 阻塞的超时时间(毫秒)。如果该值小于等于 0,则表示无限等待
返回值:成功:返回大于 0 的值,表示事件发生的 pollfd 结构的个数
               0:超时;-1:出错

Três: programação de E / S padrão

       A chamada do sistema mencionada na segunda seção é uma interface de função fornecida diretamente pelo sistema operacional. Porque ao executar chamadas do sistema, o Linux deve alternar do modo de usuário para o modo kernel, executar a solicitação correspondente e, em seguida, retornar ao modo de usuário, então o número de chamadas do sistema deve ser minimizado para melhorar a eficiência do programa e I / O padrão fornece buffer de fluxo O objetivo é minimizar o número de chamadas de sistema, como read () e write (). E / S padrão fornece três tipos de armazenamento de buffer.

Buffer cheio: neste caso, a operação de E / S real é realizada quando o buffer de E / S padrão é preenchido. Os arquivos armazenados no disco geralmente são totalmente armazenados em buffer pela biblioteca de E / S padrão. Ao realizar a primeira operação de E / S em um fluxo, geralmente chamar malloc () é para usar o cache completo.

Buffer de linha: neste caso, quando os caracteres de finalização de linha são encontrados na entrada e na saída, a biblioteca de E / S padrão executa operações de E / S. Isso nos permite gerar um caractere por vez (como a função fputc), mas somente depois de escrever uma linha antes de realizar a operação de E / S real. A entrada e a saída padrão são exemplos típicos do uso de buffer de linha.

Sem buffer: A biblioteca de E / S padrão não faz buffer de caracteres. Se você usar a função de E / S padrão para gravar vários caracteres no fluxo sem buffer, é equivalente a usar a função de gravação de chamada do sistema para gravar todos esses caracteres no arquivo aberto. O erro padrão stderr geralmente não é armazenado em cache, o que permite que as mensagens de erro sejam exibidas o mais rápido possível, independentemente de conterem um terminador de linha.

1. Operação básica

(1) Abra o arquivo

        Existem três funções padrão para abrir arquivos, a saber: fopen (), fdopen () e freopen (). Eles podem ser abertos em modos diferentes, mas todos retornam
um ponteiro para FILE, que aponta para o fluxo de E / S correspondente. Depois disso, a leitura e a escrita do arquivo são feitas através deste ponteiro FILE
. Entre eles, fopen () pode especificar o caminho e modo do arquivo aberto, fdopen () pode especificar o descritor de arquivo e o modo
a ser aberto , e freopen () pode especificar um fluxo de I / O específico além do arquivo aberto e modo.

#include <stdio.h>
FILE * fopen(const char * path, const char * mode)

Valor de entrada da função
Path: contém o caminho e o nome do arquivo a ser aberto
modo: estado de abertura do arquivo (será descrito mais tarde)
valor de retorno da função
Sucesso: ponteiro para FILE
Falha: NULL

#include <stdio.h>
FILE * fdopen(int fd, const char * mode)

A função passa no valor
fd: o descritor do arquivo a ser aberto
modo: o estado de abertura do arquivo (que será descrito mais tarde)
valor de retorno da função
Sucesso: ponteiro para o FILE
Falha: NULL

#include <stdio.h>
FILE * freopen(const char *path, const char * mode, FILE * stream)


Caminho do valor de entrada da função : contém o caminho e o nome do arquivo a ser aberto
modo: estado de abertura do arquivo (será descrito mais tarde)
stream: ponteiro
para o arquivo aberto valor de retorno da função
sucesso: ponteiro para
falha de FILE : NULL

(2) Feche o arquivo

A função para fechar o arquivo de fluxo padrão é fclose (). Esta função grava todos os dados no buffer no arquivo e libera os recursos de arquivo fornecidos pelo sistema.

#include <stdio.h>
int fclose(FILE * stream)

Fluxo de valor de entrada da função: ponteiro
para o
sucesso do valor de retorno da função do arquivo aberto : 0
falha: EOF

(3) Ler arquivos

Depois que o fluxo de arquivo é aberto, o fluxo de arquivo pode ser lido e escrito, e a função da operação de leitura é fread ().

#include <stdio.h>
size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)

Valor de entrada da função
ptr: o buffer para armazenar o
tamanho do registro de leitura : o tamanho do registro de leitura
nmemb: o número de registros de
fluxo de leitura : o fluxo de arquivo a ser lido
função de retorno valor sucesso: retornar o número de nmemb realmente lido falhou: EOF

(4) Gravar arquivos

A função fwrite () é usada para gravar no fluxo de arquivo especificado.

#include <stdio.h>
size_t fwrite(const void * ptr,size_t size, size_t nmemb, FILE * stream)

O valor de entrada da função
ptr: o buffer para armazenar o
tamanho do registro escrito : o tamanho do registro escrito
nmemb: o número de registros escritos
stream: o fluxo do arquivo a ser escrito
valor de retorno da função
Sucesso: retorna o número de registros realmente escritos
Falha: EOF

(5) Deslocamento de arquivo

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);

Quatro: operações atômicas e condições de corrida

1. Crie um arquivo exclusivamente

       Quando O_EXCL e O_CREATE são especificados como sinalizadores de abertura, se o arquivo a ser aberto já existe, open retornará um erro. Isso fornece um mecanismo para garantir que o processo seja o criador do arquivo aberto.

2. Anexe dados ao final do arquivo

        Quando dois processos competem entre si para gravar dados no final de um arquivo, isso fará com que os dados que foram gravados sejam substituídos. Para evitar o problema de gravação suja do arquivo mencionado acima, você pode adicionar o sinalizador O_APPEND ao abrir o arquivo para garantir isso.

Cinco: Copie o descritor de arquivo

#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd, int newfd);

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <fcntl.h>              /* Obtain O_* constant definitions */
#include <unistd.h>

int dup3(int oldfd, int newfd, int flags);

dup (): Chame para copiar um descritor de arquivo aberto oldfd e retornar um novo descritor, ambos apontando para o mesmo identificador de arquivo aberto. O sistema garantirá que o novo descritor deve ser o descritor de arquivo não utilizado com o menor valor numérico.

dup2 (): A chamada do sistema criará uma cópia do descritor de arquivo especificado pelo parâmetro oldfd e seu número é especificado pelo parâmetro newfd. Se o descritor de arquivo com o número especificado pelo parâmetro newfd já estiver aberto, dup2 o fechará primeiro.

dup3 (): O trabalho de conclusão é o mesmo que dup2, mas um sinalizador de parâmetro adicional é adicionado, que é uma máscara de bits que pode modificar o comportamento das chamadas do sistema. Atualmente, apenas um sinalizador é suportado, O_CLOEXEC, que solicitará ao kernel para definir o sinalizador close-on-exec para o novo descritor de arquivo.

newfd = fcntl(oldfd, F_DUPFD, startfd);

Esta chamada cria uma cópia de oldfd e usará o menor valor não utilizado maior ou igual a startfd como o número do descritor.

Acho que você gosta

Origin blog.csdn.net/qq_34968572/article/details/109444660
Recomendado
Clasificación