Eu acho que isso responde à sua pergunta:
De Richard Stevens ([email protected]):
A diferença básica é que o fd_set de select () é uma máscara de bit e, portanto, possui um tamanho fixo. Seria possível para o kernel não limitar esse tamanho quando o kernel é compilado, permitindo que o aplicativo defina FD_SETSIZE para o que ele quiser (como os comentários no cabeçalho do sistema implicam hoje), mas é preciso mais trabalho. O kernel do 4.4BSD e a função de biblioteca Solaris têm esse limite. Mas vejo que o BSD / OS 2.1 agora foi codificado para evitar esse limite, portanto é factível, apenas uma pequena questão de programação. :-) Alguém deve registrar um relatório de bug do Solaris sobre isso e ver se ele é corrigido.
Com poll (), no entanto, o usuário deve alocar uma matriz de estruturas pollfd e passar o número de entradas nessa matriz, para que não haja limite fundamental. Como observa Casper, menos sistemas têm poll () do que select, então o último é mais portátil. Além disso, com as implementações originais (SVR3), você não pode definir o descritor como -1 para dizer ao kernel para ignorar uma entrada na estrutura pollfd, o que dificultava a remoção de entradas da matriz; O SVR4 contorna isso. Pessoalmente, eu sempre uso select () e raramente poll (), porque também porto meu código para ambientes BSD. Alguém poderia escrever uma implementação de poll () que usa select (), para esses ambientes, mas eu nunca vi uma. Ambos select () e poll () estão sendo padronizados pelo POSIX 1003.1g.
O email mencionado acima é pelo menos tão antigo quanto 2001; poll()
agora, o comando (2017) é suportado em todos os sistemas operacionais modernos - incluindo BSD. De fato, algumas pessoas acreditam que isso select()
deve ser preterido . Opiniões à parte, as questões de portabilidade poll()
não são mais uma preocupação nos sistemas modernos. Além disso, epoll()
já foi desenvolvido (você pode ler a página do manual ) e continua a aumentar em popularidade.
Para o desenvolvimento moderno, você provavelmente não deseja usar select()
, embora não haja nada explicitamente errado. poll()
, e é uma evolução mais moderna epoll()
, fornece os mesmos recursos (e mais) que select()
sem sofrer com as limitações nele contidas.
select
oupoll
:(Na
select()
chamada, você cria três máscaras de bits para marcar quais soquetes e descritores de arquivo você deseja observar quanto a leitura, gravação e erros e, em seguida, o sistema operacional marca quais de fato tiveram algum tipo de atividade;poll()
você criou uma lista de IDs do descritor e o sistema operacional marca cada um deles com o tipo de evento que ocorreu.O
select()
método é bastante desajeitado e ineficiente.Normalmente, existem mais de mil descritores de arquivo em potencial disponíveis para um processo. Se um processo de longa execução tiver apenas alguns descritores abertos, mas pelo menos um deles tiver sido atribuído a um número alto, a máscara
select()
de bit transmitida deverá ser grande o suficiente para acomodar o descritor mais alto - de modo que intervalos inteiros de centenas de bits serão esteja desabilitado para que o sistema operacional tenha que percorrer todas asselect()
chamadas apenas para descobrir que está desabilitado.Uma vez
select()
retornado, o chamador precisa fazer um loop nas três máscaras de bits para determinar quais eventos ocorreram. Em muitas aplicações típicas, apenas um ou dois descritores de arquivo receberão novo tráfego a qualquer momento, mas as três máscaras de bits devem ser lidas até o fim para descobrir quais são esses descritores.Como o sistema operacional sinaliza sobre a atividade reescrevendo as máscaras de bits, elas são arruinadas e não são mais marcadas com a lista de descritores de arquivos que você deseja ouvir. Você precisa recriar a máscara de bits inteira a partir de outra lista que mantém na memória ou manter uma cópia duplicada de cada máscara de máscara e
memcpy()
o bloco de dados sobre as máscaras arruinadas após cadaselect()
chamada.Portanto, a
poll()
abordagem funciona muito melhor porque você pode continuar reutilizando a mesma estrutura de dados.De fato,
poll()
inspirou mais um mecanismo nos kernels modernos do Linux: oepoll()
que aprimora ainda mais o mecanismo para permitir mais um salto na escalabilidade, pois os servidores de hoje geralmente desejam lidar com dezenas de milhares de conexões ao mesmo tempo. Esta é uma boa introdução ao esforço:http://scotdoyle.com/python-epoll-howto.html
Embora este link tenha alguns gráficos interessantes mostrando os benefícios de
epoll()
(você notará queselect()
, neste ponto, é considerado tão ineficiente e antiquado que nem sequer obtém uma linha nesses gráficos!):http://lse.sourceforge.net/epoll/index.html
Atualização: Aqui está outra pergunta do Stack Overflow, cuja resposta fornece ainda mais detalhes sobre as diferenças:
Advertências de reatores select / poll vs. epoll em Twisted
fonte
Ambos são lentos e quase todos iguais , mas diferentes em tamanho e algum tipo de recurso!
Ao escrever um iterador, você precisa copiar o conjunto de
select
todas as vezes! Enquantopoll
corrigiu esse tipo de problema para ter um código bonito. Outra diferença é quepoll
pode lidar com mais de 1024 descritores de arquivo (FDs) por padrão.poll
pode manipular eventos diferentes para tornar o programa mais legível, em vez de ter muitas variáveis para lidar com esse tipo de trabalho. As operações nopoll
eselect
são lineares e lentas por causa de muitas verificações.fonte