Comecei a pensar sobre esse problema no contexto de etiqueta na lista de discussão do Kernel do Linux. Como o projeto de software livre mais conhecido e sem dúvida o mais bem-sucedido e importante do mundo, o kernel Linux recebe bastante destaque. E o fundador e líder do projeto, Linus Torvalds, claramente não precisa de introdução aqui.
Linus ocasionalmente atrai polêmica com suas chamas no LKML. Essas chamas são frequentemente, por sua própria admissão, relacionadas à quebra do espaço do usuário. O que me leva à minha pergunta.
Posso ter uma perspectiva histórica de por que quebrar o espaço do usuário é uma coisa tão ruim? Pelo que entendi, a quebra do espaço do usuário exigiria correções no nível do aplicativo, mas isso é uma coisa tão ruim se melhorar o código do kernel?
Pelo que entendi, a política declarada de Linus é que não quebrar o espaço do usuário supera todo o resto, incluindo a qualidade do código. Por que isso é tão importante e quais são os prós e os contras dessa política?
(Há claramente alguns contras a essa política, aplicada de forma consistente, uma vez que Linus ocasionalmente tem "discordâncias" com seus principais tenentes no LKML sobre exatamente esse tópico. Até onde eu sei, ele sempre consegue entender o assunto.)
fonte
Respostas:
O motivo não é histórico, mas prático. Existem muitos programas que rodam no kernel do Linux; se uma interface do kernel interromper esses programas, todos precisariam atualizar esses programas.
Agora é verdade que a maioria dos programas de fato não depende diretamente das interfaces do kernel (as chamadas do sistema ), mas apenas das interfaces da biblioteca padrão C ( wrappers C nas chamadas do sistema). Ah, mas qual biblioteca padrão? Glibc? uClibC? Dietlibc? Biônico? Musl? etc.
Mas também existem muitos programas que implementam serviços específicos do SO e dependem de interfaces do kernel que não são expostas pela biblioteca padrão. (No Linux, muitos deles são oferecidos por meio de
/proc
e/sys
.)E existem binários estaticamente compilados. Se uma atualização do kernel quebrar uma delas, a única solução seria recompilá-las. Se você possui a fonte: o Linux também suporta software proprietário.
Mesmo quando a fonte está disponível, reunir tudo isso pode ser uma dor. Especialmente quando você está atualizando seu kernel para corrigir um erro no seu hardware. As pessoas geralmente atualizam seu kernel independentemente do resto do sistema porque precisam do suporte de hardware. Nas palavras de Linus Torvalds :
Ele também explica que uma razão para tornar isso uma regra forte é evitar o inferno das dependências, onde você não apenas precisa atualizar outro programa para que algum kernel mais recente funcione, mas também precisa atualizar outro programa e outro e outro , porque tudo depende de uma determinada versão de tudo.
No espaço do usuário, essas dependências mútuas geralmente são resolvidas mantendo diferentes versões da biblioteca; mas você só pode rodar um kernel, então ele precisa suportar tudo o que as pessoas possam querer fazer com ele.
Oficialmente ,
Na prática, porém,
O que muda com mais freqüência são as interfaces que devem ser usadas apenas por programas relacionados a hardware
/sys
. (/proc
por outro lado, que desde a introdução de/sys
foi reservado para serviços não relacionados a hardware, praticamente nunca quebra de maneiras incompatíveis.)Em suma,
e isso é ruim porque existe apenas um kernel, que as pessoas desejam atualizar independentemente do restante do sistema, mas existem muitos aplicativos por aí com interdependências complexas. É mais fácil manter o kernel estável do que manter milhares de aplicativos atualizados em milhões de configurações diferentes.
fonte
Em qualquer sistema interdependente, existem basicamente duas opções. Abstração e integração. (De propósito, não estou usando termos técnicos). Com o Abstraction, você está dizendo que quando você faz uma chamada para uma API que, embora o código por trás da API possa mudar, o resultado será sempre o mesmo. Por exemplo, quando ligamos para
fs.open()
nós, não nos importamos se é uma unidade de rede, um SSD ou um disco rígido, sempre teremos um descritor de arquivo aberto com o qual podemos fazer coisas. Com a "integração", o objetivo é fornecer a "melhor" maneira de fazer uma coisa, mesmo que a maneira mude. Por exemplo, abrir um arquivo pode ser diferente para um compartilhamento de rede e para um arquivo em disco. Ambas as formas são usadas amplamente na área de trabalho moderna do Linux.Do ponto de vista dos desenvolvedores, é uma questão de "funciona com qualquer versão" ou "funciona com uma versão específica". Um ótimo exemplo disso é o OpenGL. A maioria dos jogos está configurada para funcionar com uma versão específica do OpenGL. Não importa se você está compilando a partir do código-fonte. Se o jogo foi escrito para usar o OpenGL 1.1 e você está tentando executá-lo no 3.x, não vai se divertir. Do outro lado do espectro, espera-se que algumas ligações funcionem, não importa o quê. Por exemplo, quero ligar
fs.open()
Não quero me importar com a versão do kernel em que estou. Eu só quero um descritor de arquivo.Existem benefícios para cada caminho. A integração fornece recursos "mais recentes" ao custo da compatibilidade com versões anteriores. Enquanto a abstração fornece estabilidade em relação às chamadas "mais recentes". Embora seja importante notar que é uma questão de prioridade, não de possibilidade.
Do ponto de vista comunitário, sem uma razão realmente boa, a abstração é sempre melhor em um sistema complexo. Por exemplo, imagine se
fs.open()
funcionasse de maneira diferente, dependendo da versão do kernel. Então, uma biblioteca de interação simples do sistema de arquivos precisaria manter várias centenas de métodos diferentes de "arquivo aberto" (ou blocos provavelmente). Quando uma nova versão do kernel foi lançada, você não seria capaz de "atualizar", teria que testar cada software usado. O kernel 6.2.2 (falso) pode apenas quebrar o seu editor de texto.Para alguns exemplos do mundo real, o OSX tende a não se importar com a quebra do espaço do usuário. Eles visam "integração" sobre "abstração" com mais frequência. E a cada atualização importante do sistema operacional, as coisas acontecem. Isso não quer dizer que uma maneira seja melhor que a outra. É uma decisão de escolha e design.
Mais importante ainda, o ecossistema Linux é repleto de projetos incríveis de código aberto, onde pessoas ou grupos trabalham no projeto em seu tempo livre ou porque a ferramenta é útil. Com isso em mente, no momento em que deixa de ser divertido e passa a ser uma PIA, esses desenvolvedores irão para outro lugar.
Por exemplo, enviei um patch para
BuildNotify.py
. Não porque sou altruísta, mas porque uso a ferramenta e queria um recurso. Foi fácil, então aqui, tem um patch. Se fosse complicado ou complicado, eu não usariaBuildNotify.py
e encontraria outra coisa. Se toda vez que uma atualização do kernel fosse lançada, meu editor de texto falhasse, eu usaria apenas um sistema operacional diferente. Minhas contribuições para a comunidade (por menores que sejam) não continuariam ou existiriam, e assim por diante.Portanto, a decisão de design foi tomada para abstrair as chamadas do sistema, para que, quando eu fizer
fs.open()
, funcione. Isso significa manterfs.open
muito tempo depois defs.open2()
ganhar popularidade.Historicamente, esse é o objetivo dos sistemas POSIX em geral. "Aqui estão um conjunto de chamadas e valores de retorno esperados, você descobre o meio". Novamente por motivos de portabilidade. Por que Linus escolhe usar essa metodologia é interno ao cérebro dele, e você teria que pedir que ele soubesse exatamente o porquê. No entanto, se fosse eu, escolheria a abstração ao invés da integração em um sistema complexo.
fonte
É uma decisão e escolha de design. A Linus deseja garantir aos desenvolvedores do espaço do usuário que, exceto em circunstâncias extremamente raras e excepcionais (por exemplo, relacionadas à segurança), as mudanças no kernel não quebrarão seus aplicativos.
Os profissionais são que os desenvolvedores do userspace não encontrarão seu código subitamente quebrado em novos kernels por motivos arbitrários e caprichosos.
Os contras são que o kernel precisa manter o código antigo e os syscalls antigos por toda a eternidade (ou, pelo menos, muito tempo depois das datas de validade).
fonte