Se você deseja irresistivelmente usar equipamentos do museu para o desenvolvimento moderno, este artigo é especialmente para você.
Bug épico
Agora, provavelmente, alguns leitores ficarão muito surpresos:
desde 2007, o kernel Linux vive no Bug sério, resultando em congelamento completo do sistema ao trabalhar sob carga de memória pesada.
No momento em que escrevo, é maio de 2025, então o inseto conseguiu comemorar a idade adulta e abrir a primeira garrafa de cerveja.
O relatório original tem esta aparência:

Claro, os desenvolvedores do kernel Atualizado problemas, mas por uma série de razões.. não contam Este bug é importante.
Sim, você leu certo:
“sistema completo pendurado sob carga” e “os desenvolvedores não acham importante consertar” – o que você acha dessas realidades do Linux?
Além disso, recentemente um ticket descrevendo esse bug em geral Fechado com a expressão épica “apenas se torne obsoleto”:

Com uma leve dica de que algumas pessoas deveriam parar de coletar seus computadores no lixo:
mas agora não me preocupo com menos de 32 Gb de RAM para um desktop.
Agora role para baixo na discussão do bug no rastreador e veja Última postagem Sobre o problema:

Claro, está tudo ótimo e o próprio autor deste artigo tem 64 GB em uma das máquinas em funcionamento há muito tempo, e alguns de meus colegas conseguiram enfiar 128 GB em um laptop – para que possamos finalmente ver o SUSE Linux, que não fica lento.
Mas, infelizmente, esse problema não se limita apenas aos amantes de antiguidades de computador – nos tempos de nuvem de hoje, o ambiente de desktop Linux típico é máquina virtual, com uma quantidade limitada de memória. Provavelmente, até mesmo seu site corporativo está sendo executado em uma máquina virtual com 4 GB de memória.
Então, na verdade, o problema diz respeito quase todos Usuários de Linux, não apenas ideológicos Mendigos entusiastas que colecionam equipamentos para museus.
Como isso aconteceu
Se você entende pelo menos um pouco sobre computadores, depois de ler o parágrafo acima e comparar a escala do problema e a atitude dos desenvolvedores Linux em relação a ele, acho que você já tirou algumas conclusões:
ou a equipe do kernel Linux é completamente incompetente, ou
O autor tem um contrato com reptilianosVárias nuances importantes foram omitidas na descrição acima.
É verdade que, como de costume, há muitos “especiais” entre os desenvolvedores Linux modernos, mas ainda assim perdi deliberadamente uma série de nuances.
Vou descrever em que ponto esse bug aparece:
É necessário aumentar a carga no uso da memória por um longo tempo e persistentemente, e em pequenas porções E necessariamente de vários processos diferentes – de modo que Assassino OOM não tinha tempo para trabalhar.
Na prática, você precisa treinar redes neurais ou executar continuamente aplicativos pesados em Java/Node (principalmente IDE) e criar constantemente grandes projetos.
E tudo isso em equipamentos de escritório despreparados com 4-6 GB de memória, que é de valor histórico, ou em uma máquina virtual.
Remendo l9ec
Já existe há muito tempo Patch não oficialque resolve o problema de travamento descrito aninhado quadrado de forma radical:
O kernel não fornece uma maneira de proteger o conjunto de trabalho sob pressão de memória. Uma certa quantidade de páginas de arquivo anônimas e limpas é exigida pelo espaço do usuário para operação normal. Em primeiro lugar, o espaço do usuário precisa de um cache de bibliotecas compartilhadas e binários executáveis. Se a quantidade de páginas de arquivo limpas cair abaixo de um determinado nível, então surra e até bloqueio ao vivo pode ocorrer.
Na verdade, este patch forma uma pequena quantidade de memória (o mesmo working set
), que é proibido sobrecarregar até mesmo os aplicativos mais astutos que mordem oamassar por kilobytes.
Uma pequena demonstração do kernel corrigido em operação:
Claro, o patch também foi notado por aqui há um arquivo da correspondência épica na lista de discussão do Linux Kernel Ano inteiro, onde o autor do patch tenta explicar aos outros que ele não é um camelo e o problema realmente existe.
Mas o patch nunca chegou ao mainstream, o que leva a certos pensamentos.

História com Xanmod
Além da versão principal do kernel, a chamada “baunilha”, cujas fontes são postadas no conhecido kernel.org, existem “compilações Vasyan” – conjuntos de patches de kernel montados por entusiastas para uma tarefa específica.
Uma dessas assembleias é chamada Xanmod e é dedicado à operação de um kernel moderno em um sistema de desktop com atrasos visuais mínimos:
XanMod é um Linux distribuição do kernel com configurações personalizadas e novos recursos. Construído para fornecer uma experiência de sistema estável, suave e sólida.
Então, no momento do aparecimento do patch l9ec, era Incluído na compilação Xanmod:

Mas nas últimas versões 6.x do Xanmod, ele não está mais lá, para o qual há uma razão formal – o aparecimento de aqui deste patch, Aparentemente que finalmente resolve o problema de suspensão:
MGLRU é Uma inovação do kernel que estávamos ansiosos para ver mesclada em 2022 e parece que isso pode acontecer para o próximo ciclo, v5.19, para melhorando o desempenho do sistema Linux, especialmente em casos de aproximação da pressão de memória.
No momento, a MGLRU na linha principal e provavelmente funciona agora e em seu sistema, a menos, é claro, que você tenha o Linux moderno e o MGLRU não seja desativado manualmente.
Infelizmente, como funciona o MGLRU outro (veja o comentário acima sobre 32 GB de memória na área de trabalho) e sua funcionalidade também foi testada em outro lugar:
No Android, nossa simulação mais avançada que gera pressão de memória a partir do comportamento realista do usuário mostra 18% menos mortes de memória baixa, o que, por sua vez, reduz as inicializações a frio em 16%.
Como você pode imaginar, o “comportamento realista do usuário” no Android móvel é um pouco diferente da sobrecarga total de ferramentas de desenvolvimento pesadas em um desktop morto ou em uma máquina virtual ainda mais fraca.
Portanto, os “usuários avançados do Linux” terão mais uma vez que cuidar de si mesmos e de seus problemas por conta própria.

Portabilidade para o Core 6.x
Infelizmente, o autor Remendo L9 aparentemente, cansado de bater de frente com, ele não transferiu seu maravilhoso patch para o branch do kernel 6.x, decidindo que, como os caras mais espertos do Google lançaram o MGLRU, sua decisão não teria mais utilidade.
Por mais estranho que pareça, mas isso Na verdade não E o patch L9 é muito mais previsível e confiável como um golpe de pé de cabra, em contraste com o circo com muito 14 patches MGLRU:
Esses patches LRU multigeracionais iniciais totalizam 14 patches no momento e, em um kernel corrigido, podem ser ativados por meio do LRU_GEN Kconfig switch
Na verdade, este artigo veio à tona depois que o autor novamente congelou sob carga enquanto trabalhava em um grande projeto, e é por isso que ele decidiu Desenterre a metralhadora do seu avô portar um patch conhecido para o kernel 6.x.
O último patch para o branch 5.x foi tomado como base, excluindo MGLRU: le9ec-5.15.patch e sua lógica foi adicionada à versão Xanmod do kernel 6.14.5.
Abaixo, explico passo a passo como portar a lógica do patch para que o procedimento possa ser repetido em kernels mais recentes e em versões “vanilla”.
Baixar arquivo com o kernel Xanmod e l9-patch nos links acima e descompacte.
Vale a pena avisar certo longe que o tamanho da versão atual do kernel Linux em forma descompactada ~1,8 GB, e você precisará de mais para montar ~ 28 GB.
Esses são os núcleos hoje.
Claro, use um pronto diff
Ele não funcionará automaticamente para o branch 6.x, então vamos transferir a lógica do patch passo a passo.
Ao todo Como parte do patch, as alterações são feitas em cinco arquivos:

Como não é muito importante corrigirmos a documentação, o primeiro arquivo pode ser ignorado.
Assim, a primeira correção relevante está no include/linux/mm.h
, onde você pode adicionar variáveis globais responsáveis pelos limites configurados:

Tudo o que você precisa fazer é colar as linhas no include/linux/mm.h
:
extern unsigned long sysctl_anon_min_kbytes;
extern unsigned long sysctl_clean_low_kbytes;
extern unsigned long sysctl_clean_min_kbytes;
O próximo passo é um pouco mais volumoso:

Você precisa encontrar a matriz static struct ctl_table vm_table()
No kernel/sysctl.c
e adicione três blocos responsáveis pela configuração interna.
{
.procname = "anon_min_kbytes",
.data = &sysctl_anon_min_kbytes,
.maxlen = sizeof(unsigned long),
.mode = 0644,
.proc_handler = proc_doulongvec_minmax,
},
{
.procname = "clean_low_kbytes",
.data = &sysctl_clean_low_kbytes,
.maxlen = sizeof(unsigned long),
.mode = 0644,
.proc_handler = proc_doulongvec_minmax,
},
{
.procname = "clean_min_kbytes",
.data = &sysctl_clean_min_kbytes,
.maxlen = sizeof(unsigned long),
.mode = 0644,
.proc_handler = proc_doulongvec_minmax,
},
Próxima edição no arquivo mm/Kconfig
, que adiciona controle dos novos parâmetros configuráveis do kernel:

Na verdade, você precisa adicionar edições ao mm/Kconfig
Três blocos: ANON_MIN_KBYTES
, CLEAN_LOW_KBYTES
e CLEAN_MIN_KBYTES
junto com todo o conteúdo.
Tudo acima foi responsável apenas pela configuração, a lógica principal do patch l9 recai sobre o mm/vmscan.c
em que as edições restantes ocorrerão.
Em primeiro lugar, adicione variáveis locais:

Em seguida, adicionamos a lógica para atribuir valores dos parâmetros do kernel:

Concentre-se na macro #define prefetchw_prev_lru_folio
, as linhas são adicionadas depois dele:
unsigned long sysctl_anon_min_kbytes __read_mostly = CONFIG_ANON_MIN_KBYTES;
unsigned long sysctl_clean_low_kbytes __read_mostly = CONFIG_CLEAN_LOW_KBYTES;
unsigned long sysctl_clean_min_kbytes __read_mostly = CONFIG_CLEAN_MIN_KBYTES;
A seguinte edição é adicionada ao static void get_scan_count
Quem conseguiu mudar a assinatura:

Eu adicionei imediatamente após o bloco de variáveis:
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
unsigned long anon_cost, file_cost, total_cost;
int swappiness = sc_swappiness(sc, memcg);
u64 fraction(ANON_AND_FILE);
u64 denominator = 0; /* gcc */
enum scan_balance scan_balance;
unsigned long ap, fp;
enum lru_list lru;
/*
* Force-scan anon if clean file pages is under vm.clean_low_kbytes
* or vm.clean_min_kbytes.
*/
if (sc->clean_below_low || sc->clean_below_min) {
scan_balance = SCAN_ANON;
goto out;
}
A próxima edição no mesmo arquivo deve ser colada no mesmo método get_scan_count
, mas mais baixo no código — concentre-se na linha nr(lru) = scan;
Felizmente, ela é a única:

Inseri a lógica de validação logo acima dela:
/*
* Hard protection of the working set.
*/
if (file) {
/*
* Don't reclaim file pages when the amount of
* clean file pages is below vm.clean_min_kbytes.
*/
if (sc->clean_below_min)
scan = 0;
} else {
/*
* Don't reclaim anonymous pages when their
* amount is below vm.anon_min_kbytes.
*/
if (sc->anon_below_min)
scan = 0;
}
nr(lru) = scan;
A próxima edição adiciona uma nova função prepare_workingset_protection
que deve ser chamado de um shrink_node_memcgs
:

Então você precisa encontrar o shrink_node_memcgs
(é o único) e insira uma nova função prepare_workingset_protection
acima dele:
static void prepare_workingset_protection(pg_data_t *pgdat,
struct scan_control *sc)
{
/*
* Check the number of anonymous pages to protect them from
* reclaiming if their amount is below the specified.
*/
if (sysctl_anon_min_kbytes) {
unsigned long reclaimable_anon;
reclaimable_anon =
node_page_state(pgdat, NR_ACTIVE_ANON) +
node_page_state(pgdat, NR_INACTIVE_ANON) +
node_page_state(pgdat, NR_ISOLATED_ANON);
reclaimable_anon <<= (PAGE_SHIFT - 10);
sc->anon_below_min = reclaimable_anon < sysctl_anon_min_kbytes;
} else
sc->anon_below_min = 0;
/*
* Check the number of clean file pages to protect them from
* reclaiming if their amount is below the specified.
*/
if (sysctl_clean_low_kbytes || sysctl_clean_min_kbytes) {
unsigned long reclaimable_file, dirty, clean;
reclaimable_file =
node_page_state(pgdat, NR_ACTIVE_FILE) +
node_page_state(pgdat, NR_INACTIVE_FILE) +
node_page_state(pgdat, NR_ISOLATED_FILE);
dirty = node_page_state(pgdat, NR_FILE_DIRTY);
/*
* node_page_state() sum can go out of sync since
* all the values are not read at once.
*/
if (likely(reclaimable_file > dirty))
clean = (reclaimable_file - dirty) << (PAGE_SHIFT - 10);
else
clean = 0;
sc->clean_below_low = clean < sysctl_clean_low_kbytes;
sc->clean_below_min = clean < sysctl_clean_min_kbytes;
} else {
sc->clean_below_low = 0;
sc->clean_below_min = 0;
}
}
Na verdade, a última edição está chamando uma nova função de uma existente shrink_node_memcgs
:

Depois de fazer todas essas correções, execute uma das opções de configuração do kernel:
make xconfig
e observe os novos campos de configuração:

A cadeia de compilação e instalação do kernel é bastante padrão:
make && make modules && make modules_install && make install
Infelizmente, isso não é tudo, e antes que o patch funcione, será necessário desabilitar o MGLRU, que, como já descrevi, foi introduzido no branch principal do kernel:
cat /sys/kernel/mm/lru_gen/enabled
Deve mostrar 0x0007
Se o MGLRU estiver ativado, você poderá desativá-lo com o comando:
echo 0 | sudo tee /sys/kernel/mm/lru_gen/enabled
Aqui aqui O autor do patch tem scripts prontos para automatizar todo esse circo.
Acabei de adicionar uma linha com uma desconexão no /etc/rc.local
.
Provas
Para os testes do patch portado, um dos meus laptops de combate foi levado Lenovo Z580 2012 ano de lançamento, com 8GB de memória:

Todos os tipos de jogos estão constantemente acontecendo nele – existem cinco sistemas operacionais diferentes e um monte de projetos e ferramentas de desenvolvimento para cada um.
Portanto, sem muita dificuldade, foram lançados simultaneamente:
-
PostgreSQL com uma base real
-
MySQL também com uma base real
-
Ideia Intellij
-
VSCode
-
Construindo um projeto no Node.js com Webpack e hot reload
-
Construindo um projeto Java bastante grande (~ 3000 arquivos de origem)
-
Chromium com 20 guias
Lembro que tudo isso está acontecendo 8 GB de memória real e em um laptop.
E desta vez o sistema operacional foi o Ubuntu usual:

É assim que fica em ação:

Uma semana após a publicação, decidi ir ainda mais longe e colocar o kernel corrigido com l9 no meu laptop 2007 com 3 GB de memória. E repeti os testes de carga.
Vídeo aqui.
Tudo funciona mais do que bem e o núcleo remendado funciona perfeitamente na sua soldagem.
Epílogo
Você pode brincar o quanto quiser com os desejos “finalmente compre um computador normal”, direi que Advisedly e uso hardware antigo há muito tempo – principalmente para avaliar o desempenho do software que crio.
E essa é uma das razões pelas quais obtemos maravilhas técnicas como Teletransportadores.
Se você ainda não alcançou um veado tão profundoe de iluminação em desenvolvimento, ainda vale a pena saber que pegamos congelamentos semelhantes em máquinas virtuais Linux, por exemplo, em um servidor de CI ao criar vários projetos ao mesmo tempo.
Portanto, a relevância do descrito ainda é Alto E não sei como aconteceu que um patch tão simples e óbvio, que garante resolver o problema, ainda não seja usado ativamente.
E, claro, Ao autor do patch raios de respeito, já que esta é a melhor escola de engenharia russa representativa.
Original, no qual o autor do artigo não se conteve e em cores contou tudo o que pensa sobre os desenvolvedores do kernel Linux, como de costume pode ser encontrado em nosso blog.