Fundo:
A sobrecarga de chamadas do sistema é muito maior que a sobrecarga de chamadas de função (as estimativas variam de 20 a 100x) principalmente devido à alternância de contexto do espaço do usuário para o espaço do kernel e vice-versa. É comum que as funções embutidas reduzam as despesas gerais das chamadas de função e as chamadas de função são muito mais baratas que os syscalls. É lógico que os desenvolvedores desejam evitar parte da sobrecarga de chamadas do sistema cuidando da maior operação possível no kernel em um syscall.
Problema:
Isso criou um grande número de chamadas (supérfluos?) Do sistema, como sendmmsg () , recvmmsg () , bem como o chdir, aberto, lseek e / ou combinações link simbólico como: openat
, mkdirat
, mknodat
, fchownat
, futimesat
, newfstatat
, unlinkat
, fchdir
, ftruncate
, fchmod
, renameat
, linkat
, symlinkat
, readlinkat
, fchmodat
, faccessat
, lsetxattr
, fsetxattr
, execveat
, lgetxattr
, llistxattr
, lremovexattr
, fremovexattr
, flistxattr
, fgetxattr
,pread
, pwrite
etc ...
Agora, o Linux adicionou o copy_file_range()
que aparentemente combina lseek de leitura e syscalls de gravação. É apenas uma questão de tempo até que isso se torne fcopy_file_range (), lcopy_file_range (), copy_file_rangeat (), fcopy_file_rangeat () e lcopy_file_rangeat () ... mas como existem 2 arquivos envolvidos em vez de mais X chamadas, ele pode se tornar X ^ 2 Mais. OK, Linus e os vários desenvolvedores do BSD não deixaram isso ir tão longe, mas o que quero dizer é que, se houvesse um syscall em lote, todos (a maioria?) Deles poderiam ser implementados no espaço do usuário e reduzir a complexidade do kernel sem adicionar muito se houver alguma sobrecarga no lado da libc.
Muitas soluções complexas foram propostas que incluem algum thread syscall especial de formulário para syscalls sem bloqueio para syscalls de processos em lote; no entanto, esses métodos adicionam complexidade significativa ao espaço do kernel e do usuário da mesma maneira que libxcb vs. libX11 (as chamadas assíncronas exigem muito mais configuração)
Solução?:
Um syscall de lote genérico. Isso aliviaria o maior custo (comutadores de modo múltiplo) sem as complexidades associadas à existência de encadeamento especializado do kernel (embora essa funcionalidade possa ser adicionada posteriormente).
Basicamente, já existe uma boa base para um protótipo no syscall socketcall (). Apenas estenda a partir de uma matriz de argumentos para obter uma matriz de retornos, ponteiro para matrizes de argumentos (que inclui o número syscall), o número de syscalls e um argumento flags ... algo como:
batch(void *returns, void *args, long ncalls, long flags);
Uma diferença importante seria que os argumentos provavelmente todos necessidade de ser ponteiros para simplicidade para que os resultados de syscalls anteriores poderia ser usado por syscalls subsequentes (por exemplo, o descritor de arquivo de open()
para uso em read()
/ write()
)
Algumas vantagens possíveis:
- menos espaço do usuário -> espaço do kernel -> comutação do espaço do usuário
- possível alternador do compilador -fcombine-syscalls para tentar agrupar automaticamente
- sinalizador opcional para operação assíncrona (retorne fd para assistir imediatamente)
- capacidade de implementar futuras funções syscall combinadas no espaço do usuário
Questão:
É possível implementar uma syscall em lote?
- Estou perdendo algumas dicas óbvias?
- Estou superestimando os benefícios?
Vale a pena me preocupar em implementar uma syscall em lotes (não trabalho na Intel, Google ou Redhat)?
- Eu já corrigi meu próprio kernel antes, mas tenho medo de lidar com o LKML.
- A história mostrou que, mesmo que algo seja amplamente útil para usuários "normais" (usuários finais não corporativos sem acesso de gravação git), ele nunca poderá ser aceito a montante (unionfs, aufs, cryptodev, tuxonice, etc ...)
Referências:
fonte
batch
syscalls embatch
syscalls, você pode criar uma árvore chamada arbitrariamente profunda de syscalls arbitrárias. Basicamente, você pode colocar todo o aplicativo em um único syscall.Respostas:
Eu tentei isso em x86_64
Correção contra 94836ecf1e7378b64d37624fbb81fe48fbd4c772: (também aqui https://github.com/pskocik/linux/tree/supersyscall )
E parece funcionar - eu posso escrever olá para fd 1 e world para fd 2 com apenas um syscall:
Basicamente, estou usando:
como um protótipo universal de syscall, que parece ser como as coisas funcionam no x86_64, então meu "super" syscall é:
Ele retorna o número de syscalls tentados (
==Nargs
se oSUPERSYSCALL__continue_on_failure
sinalizador for passado, caso contrário>0 && <=Nargs
) e as falhas na cópia entre o espaço dos kernels e o espaço do usuário são sinalizadas por segfaults em vez do usual-EFAULT
.O que eu não sei é como isso seria portado para outras arquiteturas, mas seria bom ter algo assim no kernel.
Se isso fosse possível para todos os arcos, imagino que poderia haver um wrapper de espaço de usuário que proporcionasse segurança de tipo através de algumas uniões e macros (ele poderia selecionar um membro da união com base no nome do syscall e todas as uniões seriam convertidas para os 6 longos) ou qualquer que seja o equivalente da arquitetura de jour aos 6 longos).
fonte
open
inwrite
eclose
. Isso aumentaria um pouco a complexidade devido a get / put_user, mas provavelmente vale a pena. Quanto à portabilidade IIRC, algumas arquiteturas podem atrapalhar os registros syscall para os argumentos 5 e 6 se um syscall de 5 ou 6 argumentos for agrupado em lotes ... adicionar 2 argumentos adicionais para uso futuro resolveria isso e poderia ser usado no futuro para parâmetros de chamada assíncrona. bandeira um SUPERSYSCALL__async está definidoDuas dicas principais que vêm à mente imediatamente são:
Tratamento de erros: cada syscall individual pode terminar com um erro que precisa ser verificado e tratado pelo seu código de espaço do usuário. Uma chamada em lote, portanto, teria que executar o código de espaço do usuário após cada chamada individual de qualquer maneira, para que os benefícios das chamadas em lote do espaço do kernel fossem negados. Além disso, a API teria que ser muito complexa (se possível projetar) - por exemplo, como você expressaria lógica como "se a terceira chamada falhar, faça alguma coisa e pule a quarta chamada, mas continue com a quinta")?
Muitas chamadas "combinadas" que realmente são implementadas oferecem benefícios adicionais, além de não ter que se deslocar entre o usuário e o espaço do kernel. Por exemplo, eles geralmente evitam copiar a memória e usar os buffers completamente (por exemplo, transferir dados diretamente de um local no buffer da página para outro em vez de copiá-los através de um buffer intermediário). Obviamente, isso só faz sentido para combinações específicas de chamadas (por exemplo, leitura e gravação), não para combinações arbitrárias de chamadas em lote.
fonte