1. Cinco modelos IO típicos
- IO com bloqueio, IO sem bloqueio, IO acionado por sinal, IO assíncrono, modelo de multiplexação.
- O processo de IO:
inicie uma chamada de IO, aguarde até que a condição de IO esteja pronta e, em seguida, copie os dados para o buffer para processamento - espere/copie.
1. Bloqueio de E/S:
- Para completar o IO, uma chamada IO é iniciada. Se as condições de chamada não forem atendidas neste momento, ele aguardará até que as condições sejam atendidas e a chamada IO seja concluída.
- O processo é muito simples. Somente após a conclusão de um IO é que a próxima chamada de IO pode ser feita. Os recursos não são totalmente utilizados e ficam em estado de espera na maior parte do tempo.
2. IO sem bloqueio:
- Para concluir o IO, uma chamada é iniciada. Se as condições de IO não forem atendidas no momento, um erro será retornado imediatamente. (Normalmente retorne para realizar outras operações e inicie novamente as chamadas IO).
- Em comparação com o bloqueio de IO, a utilização de recursos é mais completa, mas as operações de IO não são em tempo real.
3. IO acionado por sinal:
- Defina o método de processamento de IO do sinal, execute operações de IO no método de processamento, notifique o processo com o sinal de IO pronto e faça chamadas de IO quando o IO estiver pronto.
- Para IO orientado por sinal, o IO é mais em tempo real e utiliza totalmente os recursos, mas o processo de chamada é complicado.
4. E/S assíncrona:
- Informe ao sistema operacional por meio de chamadas de E/S assíncronas quais dados de E/S são copiados e para onde. O processo de espera e o processo de cópia são concluídos pelo sistema operacional.
- Os recursos são totalmente utilizados e o processo é mais complexo.
Nota:
①Bloqueio: Para concluir uma função, inicie uma chamada. Se as condições de conclusão não forem atendidas no momento, ela aguardará para sempre.
② Sem bloqueio: Para completar uma função, inicie uma chamada. Se as condições de conclusão não forem atendidas no momento, um erro será retornado diretamente.
③A diferença entre bloqueio e não bloqueio: se deve esperar quando a chamada é iniciada e a chamada não é concluída.
④Sincronização: Processo de processamento, processamento sequencial, após a conclusão de um, o outro é concluído. Todas as funções são concluídas pelo próprio processo.
⑤Assíncrono: No processo de processamento, o pedido é incerto porque as funções são concluídas pelo sistema operacional.
⑥Bloqueio assíncrono: a função é concluída por outras pessoas e a chamada está aguardando a conclusão de outras pessoas.
⑦Sem bloqueio assíncrono: a função é concluída por outros e a chamada retorna imediatamente.
2. E/S multiplexada
- O monitoramento centralizado de eventos IO para um grande número de descritores pode informar ao programador/processo quais descritores estão prontos para quais eventos. Neste momento, o programador/processo pode responder diretamente aos descritores dos eventos correspondentes que estão prontos para evitar a execução de operações IO em operadores que não estão prontos resulta na redução da eficiência/bloqueio do fluxo do programa.
- Eventos IO: eventos legíveis, eventos graváveis, eventos anormais;
- Modelo IO de multiplexação: select/poll/epoll; usado para monitorar descritores.
1. selecione o modelo:
(1) Processo de operação :
① O programador define um conjunto de descritores para um determinado evento (conjunto de descritores para eventos legíveis/conjunto de descritores para eventos graváveis/conjunto de descritores para eventos de exceção), inicializa e limpa o conjunto e determina qual descritor se preocupa com qual evento. Este descritor é adicionado ao conjunto de descritores do evento correspondente.
②Copie a coleção para o kernel para monitoramento. O princípio do monitoramento é a votação e o julgamento transversal. Pronto para eventos legíveis: o tamanho dos dados no buffer de recebimento é maior que o limite mínimo (padrão quantificado – geralmente o padrão é um byte); Pronto para eventos graváveis: o tamanho do espaço restante no buffer de envio é maior do que o limite mínimo (padrão quantificado – geralmente o padrão é um byte) Geralmente o padrão é um byte); prontidão para eventos de exceção: se o descritor gera uma exceção.
③A chamada de monitoramento retorna, indicando um erro de monitoramento/descritor pronto/tempo limite de espera de monitoramento; e quando a chamada retorna, os descritores não prontos no conjunto de descritores de monitoramento de eventos são removidos do conjunto - (apenas pronto é retido no descritor de conjunto). Como a coleção é modificada ao retornar, será necessário adicionar operadores à coleção novamente na próxima vez que monitorar.
④O programador pesquisa para determinar em qual conjunto o descritor ainda está, determina se o descritor está pronto para um determinado evento e, em seguida, executa a operação correspondente ao evento; select não retorna diretamente o usuário ao descritor pronto para operação direta, mas Ele retorna um conjunto de descritores virtuais, portanto o programador precisa fazer um julgamento.
(2) Operação de código :
① Defina o conjunto - struct fd_set - O membro é um array, usado como um bitmap binário - adicionar um descritor significa definir a posição do bit correspondente ao valor do descritor como 1, então o número de descritores que o seclect pode monitorar depende dos bits de o bitmap binário O número de bits — o número de bits depende da macro (_FD_SETSIZE, o padrão é 1024);
-
void FD_ZERO(fd_set* set);—初始化清空集合。
-
void FD_SET(int fd, fd_set* set);—将操作符fd增加到set集合中。
②Iniciar interface de chamada:
-
int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout); nfds:当前监控的集合中最大的描述符+1;减少遍历次数。 readfds/writefd/exceptfds:可读/可写/异常三种事件的集合。 timeout:时间的结构体。struct{tv_sec;tv_usec;},通过这个事件决定select阻塞/非阻塞/限制超时阻塞; -若timeout为NULL,则表示阻塞监控,直到描述符就绪,或者监控出错才会返回。 -若timeout中的成员数据为0,则表示非阻塞,监控的时候若没有操作符就绪,则就立即超时返回。 -若timeout中成员数据不为0,则在指定时间内,没有就虚则超时返回。 返回值:返回值大于0表示就绪的描述符个数;返回值等于0表示没有棉袄舒服就绪,超时返回;返回值小于0表示监控出错。
③A chamada retorna e devolve ao programador um conjunto de descritores prontos. O programador se desvia do julgamento de qual descritor ainda está no conjunto, qual é qual evento está pronto.
-
int FD_ISSET(int fd, fd_set* set);—判断fd描述符是否在集合中;
- Como a coleção será modificada quando select retornar, o descritor deverá ser adicionado novamente sempre que for monitorado.
④Se você não quiser mais monitorar o descritor, remova o descritor fd do conjunto
-
void FD_CLR(int fd, fd_set* set);--从set集合中删除描述符fd;
(3) Selecione para monitorar a entrada padrão:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<sys/select.h>
int main()
{
// 对标准输入进行监控
// 1.定义指定事件的集合
fd_set rfds;
while(1)
{
printf("开始监控\n");
//selent(maxfsd+1,可读集合,可写集合,异常集合,超时时间)
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
FD_ZERO(&rfds);//初始化清空集合
FD_SET(0,&rfds);// 将0号描述符添加到集合中
int res = select(0+1,&rfds,NULL,NULL,&tv);
if(res < 0){
perror("select error");
return -1;
}else if(res == 0){
printf("wait timeout\n");
continue;
}
if(FD_ISSET(0,&rfds)){
//判断描述符是否在集合中判断是否就绪了事件
printf("准备从标准输入读取数据:...\n");
char buf[1024] = {
0};
int ret = read(0,buf,1023);
if(ret<0)
{
perror("read error");
FD_CLR(0,&rfds);//移除描述符从集合中
return -1;
}
printf("read buf:[%s]\n",buf);
}
}
return 0;
}
(4) Análise de vantagens e desvantagens:
- Desvantagens :
① Select tem um limite máximo de número para descritores de monitoramento, e o limite superior depende da macro - _FD - SETSIZE, com tamanho padrão de 1024; ② O monitoramento
no kernel é obtido através do julgamento de polling traversal, e o desempenho aumentará com o descritor. Aumentar e diminuir
③ só pode retornar o conjunto pronto, e o processo precisa percorrer o conjunto para saber qual descritor está pronto para qual evento.
④Cada monitoramento requer a readicionamento de descritores à coleção, e cada monitoramento requer a nova cópia da coleção para o kernel. - Vantagens :
A portabilidade entre plataformas é relativamente boa e segue os padrões POSIX.
2. modelo de pesquisa:
(1) Processo de operação:
① Defina a matriz de estrutura de evento do descritor monitorado e adicione o descritor e as informações de identificação do evento a serem monitoradas a cada nó da matriz; ② Inicie uma chamada para iniciar o monitoramento e copie a matriz da estrutura de evento do descritor para o kernel para rotação. Consulta
transversal julgamento, se o tempo limite estiver pronto/espera, a chamada retornará e, em cada estrutura de evento pronto, o evento pronto atual será representado; ③
Realize a pesquisa e percorra a matriz para determinar qual evento o evento pronto está em cada nó da matriz. Determina se o descritor está pronto e o que fazer com ele.
(2) Operação de código:
-
int poll(struct pollfd* arry_fds,nfds_t nfds,int timeout) poll---监控采用时间结构体的形式; struct pollfd { int fd ;---要监控的描述符; short events; --- 要监控的事件POLLIN/POLLOUT; short revents; --- 调用返回是填充的就绪事件 arry_fds---事件结构体数组,填充要监控的描述符以及事件信息; nfds --- 数组中的有效节点数个数; timeout---监控事件超时等待事件; 返回值:返回值大于0表示就绪描述符事件的个数;返回值等于0就表示等待超时,返回值小于0表示监控出错。
#include <poll.h>
#include <unistd.h>
#include <stdio.h>
int main() {
struct pollfd poll_fd;//定义事件结构体
poll_fd.fd = 0;
poll_fd.events = POLLIN;//输入事件
for (;;) {
int ret = poll(&poll_fd, 1, 1000);
if (ret < 0) {
perror("poll");
continue;
}
if (ret == 0) {
printf("poll timeout\n");
continue;
}
if (poll_fd.revents == POLLIN) {
char buf[1024] = {
0};
read(0, buf, sizeof(buf) - 1);
printf("stdin:%s", buf);
}
}
}
(3) Análise de vantagens e desvantagens:
- Vantagens :
① O uso de estruturas de eventos para monitoramento simplifica o processo de operação dos três eventos na seleção.
② Não há limite máximo para o número de descritores monitorados.
③Não há necessidade de redefinir nós de eventos todas as vezes. - Desvantagens :
①Má portabilidade entre plataformas.
②Cada monitoramento ainda requer a cópia dos dados de monitoramento para o kernel.
③ O monitoramento no kernel ainda usa polling traversal e o desempenho diminuirá à medida que o número de descritores aumentar.
Modelo 3.eppol:
- O modelo de multiplexação mais útil e de melhor desempenho no Linux.
(1) Processo de operação:
① Iniciar uma chamada para criar a estrutura epollevent do identificador epoll no kernel (esta estrutura contém muitas informações, árvore vermelha e preta + lista vinculada bidirecional); ② Iniciar uma chamada para adicionar/excluir/modificar as informações de monitoramento do descritor
monitorado para a estrutura epollevent no kernel;
③Inicie o início do monitoramento, use operações de bloqueio assíncronas no kernel para implementar o monitoramento, aguarde o tempo limite/quando um descritor estiver pronto, ele retornará e retornará as informações da estrutura de eventos do descritor pronto do usuário ; ④O processo responde diretamente à descrição na
estrutura de eventos pronta. Basta usar os membros do símbolo para realizar operações.
(2) Informações da interface:
① Crie o identificador epoll eventpoll no kernel e retorne o descritor
-
int epoll _create(int size); :返回epoll操作句柄 -size : 在linux2.6.2以后被忽略,只要大于0即可。 返回值:文件描述符--epoll的操作句柄
②epoll função de registro de tempo:
-
int epoll_ctl(int epfd,int cmd, int fd, struct epoll_event* ev); -epfd : epoll_creat 返回的操作句柄; -cmd : 针对fd描述符的监控信息要进行的操作--添加/删除/修改 EPOLL_CTL_ADD/EPOLL_CTL_DEL/EPOLL_CTL_MOD; -fd : 要监控操作的描述符; -ev : fd描述符对应的事件结构体信息; struct epoll_event{ uint32_t events; //对fd描述符要监控的事件--EPOLLIN/EPOLLOUT; union{ int fd; // 监控操作的描述符; void* ptr;//要填充的描述符信息; } }
③Colete eventos que foram enviados no evento de monitoramento epoll:
-
int epoll_wait(int epfd,struct epoll_event *evs,int max_event,int timeout) -epfd : epoll操作句柄; -evs : struct epoll_event 结构体的首地址; -max_event : 本次监控想要获取的就绪事件的最大数量,不大于evens数组的最大节点数量,禁止越界访问。 -timeout : 等待超时时间--单位:毫秒。 返回值:返回值<0 表示监控出错 ,返回值 == 0 表示超时返回 , 返回值 > 0 表示就绪的时间的个数。
(3) princípio de monitoramento epoll: operação de bloqueio assíncrono:
- O monitoramento é concluído pelo sistema. O descritor de monitoramento e a estrutura de evento correspondente adicionada pelo usuário serão adicionados à árvore vermelha e preta na estrutura eventpoll do kernel. Depois que uma chamada é iniciada para iniciar o monitoramento, o sistema operacional faz um retorno de chamada para cada evento do descritor.Função, a função é adicionar a estrutura à lista duplamente vinculada quando o descritor estiver pronto para o evento de interesse.
- O próprio processo apenas determina se a lista duplamente vinculada é NULL todas as vezes e determina se está pronta.
① Crie um identificador
② Adicione as informações do descritor monitorado e as informações da estrutura do evento correspondente ao kernel;
③ Inicie o monitoramento de bloqueio assíncrono e o sistema adicione as informações da estrutura do evento correspondente ao descritor pronto à lista duplamente vinculada;
④ Julgando a lista duplamente vinculada A estrutura do evento é retornada ao processo.
⑤ O processo só precisa determinar a operação correspondente no descritor enviado na estrutura de tempo com base nas informações de tempo na estrutura de evento pronta.
(4) Vantagens e desvantagens:
- Vantagens :
① Não há limite máximo para o número de monitoramento sem descritor;
② As informações de monitoramento só precisam ser adicionadas ao kernel uma vez;
③ O monitoramento usa operações de bloqueio assíncronas e o desempenho não diminuirá com o aumento de operadores;
④ Diretamente retorna o tempo de prontidão para as informações do usuário, o processo opera diretamente no descritor e evento retornado, sem julgar se está pronto. - Desvantagens :
Fraca portabilidade entre plataformas.
(5) Método de disparo em epoll:
- Suponha que exista um exemplo :
adicionamos um soquete tcp ao descritor epoll.
Neste momento, 2 KB de dados são gravados na outra extremidade do soquete.
Chame epoll_wait e ele retornará. Isso significa que está pronto para a operação de leitura
e depois chama read, apenas 1 KB de dados foi lido
e continuou a chamar epoll_wait
①Método de disparo horizontal:
- O estado padrão do epoll é o modo de trabalho LT :
quando o epoll detecta que o evento no soquete está pronto, ele não precisa processá-lo imediatamente. Ou processa apenas parte dele;
como no exemplo acima, já que apenas 1K dados foram read, ainda há 1K de dados restantes no buffer. , quando epoll_wait for chamado pela segunda vez, epoll_wait ainda retornará imediatamente e notificará o soquete que o evento de leitura está pronto; epoll_wait não retornará imediatamente até que todos os dados no
buffer foi processado,
ou seja, suporta leitura e gravação com bloqueio e leitura sem bloqueio.
②Método de disparo de borda:
- Se usarmos o sinalizador EPOLLET ao adicionar o soquete ao descritor epoll na etapa 1, o epoll entra no modo de trabalho ET: quando
o epoll detecta que o evento no soquete está pronto, ele deve ser processado imediatamente;
como no exemplo acima, embora apenas 1K foi lido Há 1K de dados restantes no buffer. Quando epoll_wait for chamado pela segunda vez, epoll_wait não retornará; ou seja,
no modo ET, depois que o evento no descritor de arquivo estiver pronto, haverá apenas uma oportunidade de processamento;
ET O desempenho é superior ao do LT (epoll_wait retorna muito menos vezes). O Nginx usa o modo ET para usar o epoll por padrão. Ou seja:
ele suporta apenas leitura e gravação sem bloqueio. - A seleção e a votação realmente funcionam no modo LT. O epoll pode suportar tanto LT quanto ET.