Linux remove a API strncpy após 6 anos e mais de 360 patches
(phoronix.com/news)- No Linux 7.2, os usos internos da API
strncpydesapareceram do kernel, concluindo a remoção final dessa interface de cópia de strings que já estava marcada para descontinuação havia muito tempo strncpy()copia a quantidade especificada de bytes, mas o comportamento de terminação NUL não é intuitivo, o que por anos fez dela uma fonte de bugs no kernel- Sua característica de preencher o buffer de destino com zeros desnecessariamente também gerava problemas de desempenho, e foram necessários cerca de 6 anos e 362 commits para eliminar isso
- No merge de sexta-feira, além do corpo principal da API, também foi removida a última implementação específica por arquitetura para per-CPU
- O código do kernel agora precisa escolher funções alternativas conforme o uso, como
strscpy(),strscpy_pad(),strtomem_pad(),memcpy_and_pad(),memcpy()
strncpy desaparece no Linux 7.2
- O Linux 7.2 concluiu a remoção da API
strncpy, que no kernel já estava marcada para descontinuação havia muito tempo - Após 6 anos de trabalho de limpeza, não resta mais código interno do kernel usando a interface
strncpy - A mudança não foi apenas uma troca simples de função, mas quase uma eliminação de práticas antigas de cópia de strings em todo o kernel
O tamanho do trabalho até a remoção
- A remoção de
strncpyexigiu cerca de 362 commits - O trabalho avançou de forma gradual, eliminando aos poucos os usos de
strncpydentro do kernel - No Linux 7.2, esse processo de limpeza chegou ao fim
Por que strncpy era um problema no kernel
strncpyera vista há anos no kernel Linux como uma fonte recorrente de bugs- Dois comportamentos em especial eram problemáticos
- O significado e o comportamento da terminação NUL não são intuitivos, o que facilita erros de uso
- O preenchimento redundante com zeros no buffer de destino gerava custo de desempenho desnecessário
O merge que fez a remoção
- O merge feito na sexta-feira removeu a API
strncpy - No mesmo merge, também desapareceu a última implementação de
strncpyespecífica por arquitetura para per-CPU
APIs alternativas para usar no código do kernel
- Em vez de
strncpy, agora é preciso escolher a função adequada ao destino da cópia e à condição de terminaçãostrscpy(): para destinos com terminação NULstrscpy_pad(): para destinos com terminação NUL quando for necessário preenchimento com zerosstrtomem_pad(): para campos de largura fixa sem terminação NULmemcpy_and_pad(): para cópia limitada com padding explícitomemcpy(): para cópia de memória quando o comprimento é conhecido
1 comentários
Comentários do Hacker News
Antigamente eu costumava tirar sarro dos desenvolvedores do kernel Linux, supostamente os melhores programadores de C do mundo, por não saberem criar tipos como stringbuffer ou stringview, mas dá para entender até certo ponto, porque naquela época não existia o consenso atual sobre esse tema
Quem já tinha enxergado a direção certa era Dennis Ritchie, que em 1990 propôs um tipo de fat pointer para C. Teria sido uma adição perfeita se tivesse entrado no C99, e o mundo poderia ter sido bem diferente se o comitê tivesse incluído isso
Em 2007 houve uma segunda chance com o texto “C's greatest mistake”, de Walter Bright, que explicava de forma mais clara slices/stringview, essencialmente a mesma ideia do Ritchie, mas também não entrou no C11. Já chegamos ao C23 e ainda não temos isso; em vez disso, ganhamos _Generic e VLA, então parece aquela situação de “vamos comemorar muito”
Pesquisando, também encontrei um post no Reddit sobre o mesmo tema, e a discussão de bike-shedding foi engraçada: https://www.reddit.com/r/C_Programming/comments/90uq7c/cs_bi...
Fico curioso sobre por que o comportamento de arrays em C decaírem para ponteiros foi projetado assim. Há explicações de que a intenção era permitir compilar código B como C com mudanças mínimas, e em B a declaração de array na prática definia um ponteiro e um array, inicializando esse ponteiro para apontar para o primeiro elemento do array
O problema maior hoje é que a biblioteca padrão de C continua presa à era K&R, e nem mesmo recursos da linguagem adicionados no C99, como parâmetros e valores de retorno com struct, foram refletidos nas APIs da biblioteca padrão. Já melhoraria bastante se a biblioteca padrão tivesse structs de intervalo como pares ponteiro/tamanho, junto com novas funções de string ou versões atualizadas das existentes que usassem isso
Dizem que o strncpy dentro do kernel Linux foi por anos uma “fonte persistente de bugs”, por causa da semântica contraintuitiva, do tratamento de terminação NUL e do problema de desempenho de preencher o destino com zeros desnecessariamente
Sempre que me pediam para revisar código em C, eu procurava por strncpy e quase sempre encontrava algum bug ali
Há coisas que me incomodam há 40 anos. Strings terminadas em NUL, e agora também strings que não são UTF-8 em entrada e saída
O costume de tratar fim de linha como LF, CR ou CRLF também, assim como separar campos com pipe ou vírgula. Se tivessem usado caracteres ASCII não ambíguos como GS, FS e RS, a codificação/decodificação de fim de linha seria apenas um problema de E/S, e HT/VT/CR/LF/FF poderiam ter permanecido literalmente como códigos relacionados à saída
Ficou muito mais simples porque desapareceu toda a sujeira de tratamento de escape que aparece em dados separados por vírgula
O padrão Unicode diz que não só CR, LF, CRLF e esses caracteres, mas também tabulação vertical e form feed devem ser tratados como separadores de linha
Fins de linha como LF, CR e CRLF também são convenções de sistema operacional, e é melhor que linguagens de programação não tentem “adivinhar” o fim de linha correto. Isso cria mais problemas do que resolve e, de novo, em geral é um problema específico do Windows que a Microsoft deveria trazer para o século atual
Da última vez que tive que lidar com arquivo CSV em bash, converti internamente para RS e FS e processei assim
Em vez de strncpy, o código do kernel Linux recomenda usar strscpy() para destinos terminados em NUL, strscpy_pad() para destinos terminados em NUL que precisam de padding com zero, strtomem_pad() para campos de largura fixa sem terminação NUL, memcpy_and_pad() para cópia limitada com padding explícito e memcpy() para cópia de memória com comprimento conhecido
Isso parece um pesadelo, e não sei se precisa ser tão complicado assim
Acho melhor quando, ao ler o código, a intenção do desenvolvedor já fica clara só pela função escolhida
É nesse tipo de trabalho repetitivo e entediante que o verdadeiro trabalho da engenharia de sistemas acontece
Projetos grandes de infraestrutura como esse, que tornam o kernel Linux mais confiável enquanto o mantêm utilizável em produção durante todo o processo, se movem em escala de décadas, não de meses
Mas não sei se dá para produzir progresso significativo de longo prazo nesse ritmo. Não é exatamente uma reclamação, é mais um paradoxo da infraestrutura central
É um trabalho impressionante e que dá humildade. É surpreendente que tanta gente tenha contribuído
“Recursos novos e legais” tendem a receber reconhecimento com mais facilidade, mas em algo tão fundamental quanto o kernel, remover recursos ruins pode ser ainda mais importante
Quando, daqui a 50 anos, as pessoas esquecerem como ler código-fonte e os restos de Claude/Codex forem se acumulando silenciosamente enquanto queimam a maior parte da energia da Terra, parece que esse tipo de trabalho vai sobreviver como uma lenda da “era de fundação”
E também é a única pessoa que sabe o que é a Unix epoch
Acho que as strings terminadas em 0 são o maior erro da história da computação. Strings no estilo Pascal eram muito mais seguras
Ainda é um ponteiro para um array de caracteres terminado em 0, mas há um campo de comprimento logo antes do primeiro byte apontado pelo ponteiro. Partindo da premissa de que não há NUL embutido, também é compatível com strings em C, e funções do tipo BSTR podem aproveitar o valor do comprimento
Por um tempo, até 16 bits podem ter parecido exagerados, e hoje 32 bits podem parecer pequenos demais. O C, que é uma linguagem de “tipagem forte”, na prática é bastante frouxo justamente em pontos importantes
Não escrevo código relacionado a Pascal há mais de 30 anos, mas tenho uma lembrança vaga de que, mesmo na época, eu achava o sistema de strings complicado demais de usar
Há dor e retrabalho demais por não existir nem um único tipo de string
strncpytambém passasse a usar esse tipo e essas funçõesFico curioso sobre o que havia de tão difícil em reescrever os usos de
strncpya ponto de isso ter levado 6 anosGostaria de saber se era porque o uso estava realmente espalhado, se foi um trabalho de longo prazo feito só quando surgia a chance de mexer no mesmo arquivo, ou se houve alguma outra dificuldade
Já lidei com código em apps Win32 que usava strings com preenchimento por espaços. A string de destino era preenchida com espaços, mas o último byte ainda era um nulo
Para operações como comprimento e cópia, era preciso usar versões específicas das funções de string. Não sei por que faziam isso, mas a base de código era tão antiga que pode ter vindo do comportamento de structs em Pascal
charem bancos de dados SQL. Diferente devarchar, camposcharsão preenchidos com espaços