이번 주 LWN.net 위클리 에디션(9월 10일자) 주요 내용이다. 2년 넘게 끌어온 Rust never 타입(!) 안정화가 3,300개 크레이트를 흔드는 호환성 문제를 넘어 1.99에 들어가게 됐고, CERN은 컴파일러 플래그 하나 때문에 가속기 제어 컴퓨터 2,200대를 CentOS에서 Debian으로 옮기기로 했다. 커널 쪽에서는 메모리 티어링 작업이 “이 기능을 정말 원하는 고객이 있느냐”는 근본적인 반대에 부딪혔고, 6.19에서 깨진 TCMalloc을 되살리려는 RSEQ 연산 제안이 “결국 또 하나의 BPF”라는 지적을 받고 있다. 여기에 멀티스레드 파이썬을 결정적으로 테스트하는 blanket, 가변 폰트와 MathML을 지원하게 된 Typst 0.15까지 다룬다.
- Stabilizing Rust's never type — 2년 작업 끝에 안정화된
!타입과 크레이트 3,300개를 건드린 호환성 작업 - CERN's migration path from CentOS Linux to Debian — x86-64-v2 플래그 하나가 불러온 가속기 제어 시스템 이전
- Recent work in memory tiering — pghot 핫 페이지 승격과 티어 인지 memcg, 그리고 설계 자체에 대한 반대
- Deterministic testing for multithreaded Python — 스레드 실행 순서를 통제하는 테스트 라이브러리 blanket
- Fixing the TCMalloc regression with RSEQ operations — 유저 공간 복귀 때마다 실행되는 연산 목록을 등록하는 새 API
- Typst makes big strides — 가변 폰트, MathML, 번들 출력, 다중 참고문헌을 담은 0.15
- 짧은 소식: 7.3-rc2가 “풀팻” rc가 된 사연, Asahi Linux의 M3 지원, Rustls 10년, Audacity 4.0의 Qt 재작성, Jellyfin 12
Rust never 타입 안정화
함수의 반환 타입은 그 함수가 만들어내는 데이터의 종류를 나타낸다. Rust의 never 타입은 느낌표(!) 하나로 표기하며, 값이 절대 나올 수 없는 자리를 표시한다. 오랫동안 컴파일러 내부에서만 쓰이던 불안정 기능이었는데, 8월 24일 컴파일러 기여자 “waffle”이 2년 넘는 작업 끝에 안정화에 성공했다. 오래 걸린 이유는 기존 에디션에 작은 호환성 파괴를 동반했기 때문이다.
never 타입이 필요한 이유는 두 가지다. 실용적인 쪽은 제네릭 코드의 최적화다. 표준 라이브러리의 FromStr 트레이트는 변환 결과 또는 커스텀 에러 타입을 돌려주는데, 변환이 절대 실패하지 않는 타입이라면 Err를 never로 지정할 수 있다. 그러면 컴파일러가 에러 분기가 존재하지 않음을 알고 관련 코드를 통째로 제거한다.
impl FromStr for ByteString {
type Err = !;
fn from_str(s: &str) -> Result<Self, !> { ... }
// 제네릭 인터페이스는 그대로 유지하면서
// 실제로는 아래와 동등한 코드가 생성된다:
// fn from_str(s: &str) -> Self { ... }
}철학적인 쪽은 타입 추론이다. Rust에서는 if나 while도 표현식이라 무한 루프에도 타입을 붙여야 한다. never 타입은 다른 어떤 타입으로도 자동 변환되는데, “값이 나올 수 없는 계산의 결과”이므로 그 코드에 도달할 수 없다는 뜻이고, 따라서 무시해도 안전하다. 타입 시스템이 수행하는 데드 코드 제거인 셈이다.
문제는 never fallback이라 불리는 구석이었다. 타입 추론이 끝나도 결정되지 않은 타입이 남으면 컴파일러는 지정된 폴백 타입을 가정하는데, 2024 에디션 이전에는 유닛 타입 ()였고 2024 에디션부터는 ! 자신이다. 여기에 표준 라이브러리의 Infallible 타입을 !의 별칭으로 바꾸는 작업이 얽혔다. 둘 다 개별로는 호환성 파괴지만, 동시에 적용하면 서로 거의 상쇄된다는 것이 해법이었다.
“거의”를 확인하는 도구가 crater다. crates.io의 공개 라이브러리를 전부 내려받아 컴파일해 보는 이 도구를 waffle이 4월에 돌린 결과, 영향을 받은 크레이트는 3,300개였지만 실제로 완전히 깨진 것은 7개뿐이었다. 나머지는 이미 수정된 라이브러리의 옛 버전에 의존하는 경우였다. 2024년부터 이 상황에 경고를 띄워 온 덕분이다. 가장 흔한 잔존 오류는 제네릭 함수를 타입 정보 없이 호출하는 코드였다.
fn foo<T: Default>() -> Result<T, Error> { ... }
// 타입을 지정하지 않은 호출
foo()?;
// 이전: T가 ()로 추론됨 → Default를 구현하므로 컴파일 성공
// 변경 후: T가 !로 추론됨 → Default 미구현 → 컴파일 에러
// 해결: 반환 타입을 명시한다
foo::<()>()?;
// 또는
() = foo()?;메인테이너들은 3,300개를 그냥 깨뜨릴 생각이 없었다. waffle은 주요 라이브러리 메인테이너들과 함께 위와 같은 간단한 수정을 패치 버전으로 백포트하는 작업을 진행했고, 이것으로 실패 크레이트 1,553개가 해결됐다. 수명이 끝난 버전이라며 백포트를 거절한 메인테이너도 있었는데, 그쪽 주장은 옛 Rust를 쓰거나 관리되는 버전으로 올리라는 것이었다.
결론적으로 Rust 1.99부터 never 타입이 안정화되고 Infallible은 !의 타입 별칭이 된다. 코드가 깨진 사용자의 선택지는 1.98에 머무르거나, 의존성을 수정이 포함된 버전으로 올리거나, 영향받는 호출의 반환 타입을 명시하는 것 세 가지다. 하위 호환성 약속을 어긴 사례로 볼 수도 있지만, 몇 년간 경고를 띄우고 커뮤니티와 함께 백포트까지 진행한 과정을 보면 오히려 그 약속을 지키려 한 사례로 읽을 수도 있다.
CERN, 가속기 제어 시스템을 Debian으로
CERN 직원 Federico Vaga와 Nikos Tsipinakis가 8월 30일 MiniDebConf Winterthur 2026에서 “Controlling CERN's Accelerators with Debian”을 발표했다. 데이터 센터 이야기가 아니라, 실제로 입자를 충돌시키는 가속기 체인을 제어하는 컴퓨터 이야기다.
규모부터 특이하다. 가속기 복합 단지는 43제곱킬로미터가 넘는 영역에 분포하고 가장 깊은 곳은 지하 100미터다. 빔 파이프만 36킬로미터, 장치 17,000개, 이를 원격 제어하기 위한 케이블 70,000개, 컴퓨터 2,200대가 얽혀 있다. 시중에 맞는 부품이 없어 PCI 카드까지 대부분 CERN에서 직접 설계한다. 그래서 커스텀 커널 드라이버가 많고, 드라이버 버그가 곧 시스템 안정성 문제로 이어진다.
CERN은 2008년 실시간 패치를 얹은 Linux로 시작해 Fermilab과 함께 만든 Scientific Linux를 거쳐 CentOS Linux로 옮겨갔다. 직접 리빌드하느니 업스트림에 기여하는 편이 낫다는 판단이었다. 그런데 CentOS Linux 8이 조기 종료되면서 계획이 무너졌다. CentOS Stream 9으로 시작해 10으로 가되, 인프라를 다른 배포판으로도 옮길 수 있게 만들고 Debian을 플랜 B로 두는 것이 당시 결정이었다.
Debian이 플랜 A가 된 결정타는 Vaga의 표현으로 “단순한 컴파일러 플래그 하나”였다. Red Hat이 RHEL 9을 x86-64-v2, RHEL 10을 x86-64-v3 마이크로아키텍처에 맞춰 최적화하기로 한 것이다.
| 항목 | 수치 |
|---|---|
| v2 최적화 바이너리로 부팅 불가한 시스템 | 전체의 47% (Intel Core Duo 계열 등 구형 CPU) |
| v3까지 적용 시 추가로 부팅 불가 | 17% (누적 약 65%) |
| 장비 교체 추정 비용 | 약 540만 스위스프랑 (약 667만 달러) |
| 이전 대상 컴퓨터 | 지하 프론트엔드 2,200대 |
돈만의 문제도 아니다. 2010년 이전 PC는 PCI 슬롯이 넉넉했지만 요즘 장비는 그렇지 않아, 시스템 하나를 교체하는 데 컴퓨터 세 대가 필요해지고 랙 공간이 늘어난다. 랙이 모자라면 지하를 더 파야 한다. 제어 시스템의 주력인 VMEbus 단일 보드 컴퓨터도 CentOS 때문에 쓸모없어지는데, 새 세대를 설계해 뒀어도 생산은 전쟁이나 AI 수요 같은 이유로 지연된다. 43제곱킬로미터에 흩어진 지하 시설로 장비를 싣고 내려가 재배선하다 케이블 두 개를 바꿔 꽂으면 그 사실은 한참 뒤 시운전에서야 드러난다.
그래서 결론은 “가능한 한 많은 하드웨어에서 동작하려 하는 범용 운영체제” Debian이다. 커널은 배포판 것 대신 kernel.org 소스로 필요한 드라이버를 모두 내장해 직접 빌드한다.
이전 대상은 CERN 인프라 3계층 중 세 번째뿐이다. 물리학자와 운영자가 쓰는 콘솔(AlmaLinux 10 + Xfce)과 명령을 로깅·인증·검증하는 데이터 센터 계층(RHEL 9 → AlmaLinux 10)은 그대로 두고, 지하 100미터에서 명령을 실제 하드웨어 신호로 바꾸는 “프론트엔드” 컴퓨터만 Debian으로 간다. 전자 엔지니어 관점에서는 자석을 꽂는 컴퓨터가 프론트엔드라서 붙은 이름이다.
일정 설계가 흥미롭다. 가속기는 24시간 7일 돌아가고 그동안은 업데이트를 넣을 수 없다. 연중 몇 차례 있는 짧은 “기술 정지”로는 배포판 메이저 업그레이드를 감당할 수 없다. Tsipinakis의 표현대로 “여러분은 5년 계획을 세우겠지만, CERN은 최소 15년 계획이라 2041년까지 기계 상태를 알고 있다”. 2027년 초에 Debian을 배포해 2033년 롱 셧다운까지 유지하는 것이 계획이고, 정규 3년 + LTS 2년 + Freexian의 ELTS 5년을 합치면 trixie로 2035년까지 버틸 수 있다는 점이 백업 플랜이 됐다.
인프라도 함께 손봤다. 기존에는 2005년에 설계된 베어메탈 서버 한 대가 FTP로 GRUB과 커널 설정을, NFS로 루트 디렉터리를 공급했다. “숨도 쉬면 안 되는” 이 서버가 죽으면 임베디드 시스템 2,000대가 동시에 루트 드라이브를 잃는다. 이제는 Kubernetes 기반 다중 서버 부트 시스템과 GitLab 패키지 레지스트리로 바꿨다. RHEL 위에서 Debian 소프트웨어를 컴파일하는 문제는 ELBE(Embedded Linux Build Environment)로 풀었고, 기존 Koji 빌드 인프라에 Debian 패키지를 빌드하는 플러그인을 붙였다.
남은 과제는 도구다. 패키지 빌드·배포를 자동화하는 표준 도구가 없다는 것이 첫 번째 불만이었다. dak나 buildd는 Debian 전용이라 외부에서 굴리기 어렵고, CI 플랫폼 Debusine은 유망해 보이지만 CERN 인프라에서 지원할 수 없는 블로커가 있었다. 한 저장소에 같은 패키지의 여러 버전을 두는 기능도 대부분의 도구가 지원하지 않아, 이를 지원하는 reprepro 브랜치를 experimental에서 가져다 쓸 계획이다.
청중 질문 중에는 v2/v3 플래그 없이 CentOS Stream을 리빌드하는 방안이 있었다. AlmaLinux 팀과 논의했지만 “아무도 책임지려 하지 않았다”는 것이 답이었다. 플래그를 빼는 것으로 끝나는 게 아니라 검증 과정을 거쳐야 하는데, CERN의 본업은 입자 물리학이다. 프론트엔드 시스템은 공용 인터넷은 물론 CERN 네트워크에도 붙어 있지 않아 보안 업데이트는 기술 정지 때 연 1회 수준으로 적용한다. 그럼에도 업데이트가 필요한 이유는 기술 때문이다. 몇 달 전까지 CentOS 7을 쓰던 탓에 GCC 4에 묶여 있었고, 일부 인원은 자체 컴파일러를 유지하고 있었다.
발표 며칠 뒤인 9월 1일 가속기가 셧다운에 들어갔고, 2,200대 전부를 Debian trixie로 옮기는 작업이 시작됐다. Tsipinakis가 정리한 교훈은 하나다. 한 배포판의 특정 버전에서 돌아간다고 다음 메이저 버전에서도 돌아갈 것이라 가정해선 안 되며, 특정 배포판에 묶이지 않는 능력이 중요하다.
메모리 티어링 최근 작업과 설계 논쟁
티어드 메모리 시스템은 성능 특성이 다른 여러 종류의 메모리로 구성된다. 일반 DRAM에 더해 고대역폭 메모리나 느린 CXL 메모리가 함께 붙는 식이다. 어떤 메모리에 할당하느냐가 성능을 좌우하므로 커널 쪽 작업이 수년째 이어지고 있지만, 최근에는 속도가 다소 느려졌고 설계 자체에 대한 의문까지 나오는 중이다.
빠른 티어에서 느린 티어로 내리는 강등(demotion)은 스왑 아웃과 비슷한 판단이라 비교적 쉽다. 어려운 쪽은 느린 메모리에 있는 뜨거운 페이지를 찾아 올리는 승격(promotion)이다. 잘못하면 뜨거운 페이지를 느린 곳에 방치하거나, 뜨겁지 않은 페이지를 올려 성능을 깎아먹는다.
Bharata B Rao의 pghot 패치 시리즈는 승격 판단에 쓸 데이터의 출처가 여럿일 수 있다는 전제에서 출발한다. 페이지를 접근 불가로 표시하고 폴트를 추적하는 방식, CXL 시스템이 제공하는 액세스 추적, 그리고 이번 버전에서 추가된 AMD IBS(Instruction-Based Sampling) 프로파일 데이터를 통합하고, kmigrated라는 새 커널 스레드가 가장 뜨거운 페이지를 옮긴다.
| 모드 | 페이지당 추적 오버헤드 | 추가로 얻는 것 |
|---|---|---|
| 기본 | 1바이트 | 하위 티어 페이지의 접근 빈도 |
| precision 모드 | 4바이트 | 어느 NUMA 노드가 접근하는지까지 추적 → 사용 중인 노드로 승격, NUMA 밸런싱 작업의 상당 부분을 대체 가능 |
논의는 순탄하지 않았다. Andrew Morton이 이미 커널에 있는 DAMON으로 같은 일을 할 수 없느냐고 묻자 Rao는 LLM이 생성한 두 메커니즘 비교를 답변으로 내놨고, DAMON 작성자 SJ Park은 이에 별로 감흥을 느끼지 못한 채 “더 진행하기 전에 DAMON을 실제로 공부해 달라”고 요청했다. Park은 pghot 고유 기능으로 꼽힌 여러 소스 통합 같은 항목이 현재 DAMON에도 구현되고 있다고 덧붙였다.
Matthew Wilcox는 패치 세트 전체와 그 발상에 반대했다.
이걸 해야 한다고 생각하지 않는다. 고객이 실제로 원하는 사용 사례라고 보지 않는다. CPU 벤더들이 자기 고객이 원할 것이라 생각하는 것에 가깝다. 이 패치가 개발되는 몇 달 동안 반복해서 말해 왔다. 나는 반대다. — Matthew Wilcox
Meta의 Gregory Price는 자사가 “이 작업에 적극적으로 관심을 갖고 설계를 함께 이끌고 있다”고 바로 반박했고, Yongting Lin도 다소 미지근하게나마 지지를 표했다. 다만 David Hildenbrand는 여덟 번째 리비전인데도 어느 패치에도 리뷰 태그가 붙지 않았다는 점을 지적하며 “머지되기에는 한참 멀었다”고 평가했다. 우선순위가 중간 정도로도 인식되지 않아 메모리 관리 메인테이너들이 아직 제대로 들여다보지 않았다는 것이다.
한편 티어드 시스템에는 다른 문제도 있다. 먼저 실행된 워크로드가 빠른 메모리를 다 채워버리면 나중에 오는 워크로드는 느린 메모리만 쓰게 된다. pghot은 이를 사후에 교정하지만, 애초에 발생하지 않게 막자는 접근이 메모리 컨트롤 그룹(memcg)에 티어 인지 기능을 넣는 두 개의 패치 세트다.
| 패치 세트 | 한도 설정 방식 | 구현 특징 |
|---|---|---|
| Joshua Hahn | 시스템의 메모리 구성 비율을 그대로 적용 (DRAM 25% + CXL 75%라면 cgroup도 한도의 25%만 DRAM 사용) | 할당 시점에 여유 있는 티어를 고르고, 한도를 넘기면 타깃 reclaim 수행. 승격도 한도에 닿으면 느려진다. hard/soft 한도 모두 비례 적용 |
| Qiqi Liu | 동일하게 비례 설정하되 관리자가 조정 가능 | low limit(보장 최소치) 미구현, 한도 강제를 주로 reclaim에 의존 |
둘 다 문제를 풀 수는 있지만 메인테이너가 둘 다 머지할 리는 없다. Michal Hocko는 티어 단위 제어라는 목표 자체에 의구심을 보이면서 차이를 물었고, Hahn도 같은 질문을 던졌다. Liu가 두 접근을 자세히 비교한 뒤 Hahn의 방식을 밀고 가는 편이 낫겠다고 결론지었으니, 방향에는 합의가 선 셈이다. 다만 커널이 정말 티어 단위 제어를 가져야 하는지는 여전히 답이 나오지 않았다.
멀티스레드 파이썬을 결정적으로 테스트하기
free-threaded 파이썬 덕분에 멀티스레드 지원은 좋아졌지만, 테스트는 여전히 어렵다. 스레드 실행 순서를 호스트 시스템이 정하기 때문이다. Larry Hastings는 PyCon US에서 이 비결정성을 없애는 자신의 blanket 프로젝트를 발표했다. 이름은 “스레드를 사용하는 커버리지(coverage using threads)”에서 왔다.
출발점은 커버리지 보고서의 99%다. 드물고 이상한 조건에서만 실행되는 줄이 있으면 100%를 채울 수 없고, 그 조건은 개발자가 통제할 수 없다. threading 모듈의 동기화 프리미티브 일곱 개는 전부 비결정적이라, 세 스레드가 같은 락을 원할 때 획득 순서는 OS 스케줄러에 달려 있다.
blanket은 같은 일곱 개의 결정적 대체품을 Scenario 객체에서 제공한다. 아래는 문서의 퀵스타트 예제로, 세 스레드가 락을 잡았다 놓고 배리어에서 만나는 코드다. 순수 threading으로 쓰면 락 획득 순서 6가지와 배리어 통과 순서 6가지를 곱해 36가지 결과가 나온다.
import blanket
scenario = blanket.Scenario()
lock = scenario.Lock()
barrier = scenario.Barrier(3)
def worker(name):
with lock:
print(f"worker {name} got the lock")
barrier.wait()
print(f"worker {name} is past the barrier")
# 스레드 생성과 셔플은 기존 코드 그대로 둔다
lock_api = scenario.api(lock)
barrier_api = scenario.api(barrier)
with scenario:
for t in threads:
t.start()
list(lock_api.relay(B, A, C)) # 락은 B → A → C 순으로
lock_api.unblock(lock.release, C)
with barrier_api.cycle(C, A, B): # 배리어는 C → A → B 순으로
pass
for t in threads:
t.join()핵심은 blanket이 프리미티브를 재구현하지 않고 실제 threading 객체를 감싸기만 한다는 점이다. Hastings 본인이 개발하며 가장 놀랐던 부분이기도 하다. 필요한 것은 정지점 두 개뿐이었다.
| 상태 | 시점 | 역할 |
|---|---|---|
BLOCKED | 실제 메서드 호출 직전 | 어느 스레드가 어떤 순서로 프리미티브를 호출할지 통제 |
COMMIT | 메서드 호출됨 | 기존 상태 |
PAUSED | 반환 직전 | 배리어 등에서 어느 스레드가 먼저 재개할지 통제 |
RETURNED | 반환 완료 | 기존 상태 |
앞의 두 개가 시나리오 밖에서도 존재하던 상태이고, BLOCKED와 PAUSED가 시나리오 안(“regulated” 상태)에서 추가되는 “파킹 상태”다. 테스트가 돌아가는 동안 메인 스레드가 워커 스레드들의 스케줄러 역할을 하며 순서를 지시하고, 시나리오를 빠져나온 뒤 단언문을 검사한다. Hastings의 표현으로는 “락의 내부 상태를 통제할 필요가 없다. 누가 어떤 순서로 락을 호출하는지만 통제하면 된다”.
API는 저수준·중간·고수준 세 단계로 제공된다. 대부분은 고수준만 쓰지만 “모든 추상화는 언젠가 새기 때문에” 아래 단계도 이해해야 한다는 것이 그의 조언이다. 고수준 API는 프리미티브 종류에 맞춰져 있어 Lock·RLock에는 relay(), Condition·Event·Barrier에는 cycle(), 세마포어에는 allocate()를 쓴다. 저수준의 scenario.wait()는 Win32의 WaitForMultipleObjects()를 본떠, 넘긴 시그널 객체 중 하나가 참이 될 때까지 잠들었다가 신호를 보낸 객체들의 집합을 돌려준다.
그 밖에 타임아웃을 강제로 발생시키거나(expire()) 무시하게 하는 기능, threading을 직접 쓰는 모듈에 blanket을 몽키패치하는 scenario.inject(), 직접 만든 동기화 프리미티브의 바이트코드에 호출을 삽입하는 blanket.injector()가 있다. 성능은 목표가 아니다. blanket의 신조 자체가 “테스트는 사실상 싱글스레드여야 한다. 그렇지 않다면 충분히 blanket하지 않은 것이다. 더 느리게 만들어라”이다.
비슷한 기술로는 “stateless model checking”(SMC) 연구 분야가 있다. CHESS, Shuttle, Loom 같은 도구들이 스케줄러를 직접 쓰는 대신 가능한 순서 조합을 체계적으로 또는 확률적으로 탐색하는데, 조합이 금방 폭발하기 때문에 탐색 공간을 줄이는 것이 연구의 초점이다. blanket에 가장 가까운 것은 Coyote라고 한다. 덧붙여 Hastings는 이 프로젝트를 LLM과 페어 프로그래밍으로 진행했고, 그 덕에 개발 과정에서 blanket을 다섯 번 다시 썼다고 밝혔다.
RSEQ 연산으로 TCMalloc 회귀 고치기
restartable sequences(rseq)는 커널 유저 공간 인터페이스 중에서도 유별난 축에 속한다. 유저 공간이 간단한 락프리 연산을 수행하다가 선점되면 그 사실을 통보받아 재시작할 수 있게 해 준다. 6.19에 머지된 rseq 성능 개선 작업이 TCMalloc 할당자를 깨뜨렸는데, 원인은 TCMalloc이 문서화되지 않은 커널 동작에 기대고 있었기 때문이다.
struct rseq의 cpu_id_start 필드는 커널이 관리하며 유저 공간에서는 읽기 전용이라고 문서화돼 있다. TCMalloc은 여기에 센티널 값을 써넣고, 커널이 유효한 CPU 번호로 되돌려 놓는 것을 자료구조 배치 트릭과 함께 이용해 스레드가 중간에 중단됐음을 감지했다. 6.19의 최적화가 이 필드를 무조건 설정하는 동작을 없애면서 트릭이 무너졌다. 지금은 구 API 사용자를 감지해 옛 동작을 유지하는 방식으로 우회하고 있는데, 코드 복잡도와 성능 양쪽에서 비용이 크고 glibc가 rseq를 쓰는 방식과도 충돌한다.
Olivier Dion이 제안한 해법은 “RSEQ 연산”이다. 커널이 유저 공간으로 복귀할 때마다 실행할 연산 집합을 미리 등록해 두는 방식으로, TCMalloc을 위한 CPU 번호 무조건 설정을 되살리되 이 기능을 쓰지 않는 스레드의 성능은 건드리지 않는다. 가장 단순한 RSEQ_OP_RESET은 유저 공간 메모리의 워드 하나를 특정 값으로 되돌린다.
struct rseq_op_reset {
struct rseq_op_node node; /* type에 연산 종류를 넣고 나머지는 0으로 */
__u64 src; /* 저장할 값의 포인터 (0이면 0을 저장) */
__u64 dst; /* 저장될 위치 */
__u32 len; /* 4 또는 8바이트 */
};
struct rseq_op_node {
__u64 next;
__u64 prev;
struct {
__u8 type;
__u8 reserved[7];
};
};나머지 두 연산 RSEQ_OP_RESET_WITH_STRIDE_CPUID와 RSEQ_OP_RESET_WITH_STRIDE_MMCID는 각각 현재 CPU ID와 가상 CPU ID로 색인되는 배열에 값을 넣는다. 구조체를 준비한 뒤 prctl()의 새 PR_RSEQ_OP 연산으로 등록하며, 총 2,048개까지 등록할 수 있다.
prctl(PR_RSEQ_OP, PR_RSEQ_OP_REGISTER, &op, 0, 0);
prctl(PR_RSEQ_OP, PR_RSEQ_OP_UNREGISTER, &op, 0, 0);
특이한 점은 등록 구조체를 커널로 복사하지 않는다는 것이다. 구조체는 유저 공간 메모리에 그대로 남고, rseq_op_node의 next·prev 포인터로 커널이 관리하는 리스트에 엮인다. 유저 공간으로 복귀할 때마다 커널이 유저 메모리에 있는 이 리스트를 순회한다. rseq 구조체와 달리 커널 주소 공간에 매핑되지도 않아 매번 일반적인 유저 메모리 접근 방식을 쓴다. 당연히 느려지고, 유저 공간이 포인터를 포함한 내용을 언제든 바꿀 수 있어 코드 곳곳에서 리스트를 검증하느라 비용이 더 붙는다. 그럼에도 이렇게 한 이유는 저장할 값이나 위치를 유저 공간이 바꿀 수 있다는 점 자체가 원하는 기능이기 때문으로 보인다.
결국 커널에 (아주 단순하지만) 또 하나의 가상 머신을 추가하는 셈이라, 차라리 유저 공간 복귀 시 BPF 프로그램을 실행하게 하면 되지 않느냐는 의문이 남는다. 패치는 이 질문에 답하지 않지만, BPF 프로그램 로드에는 권한이 필요한 반면 rseq는 그렇지 않다는 것이 그럴듯한 답이다. RSEQ 연산이 필요한 모든 프로그램에 CAP_BPF를 요구하면 그 자체로 문제가 된다.
아직 RFC 단계라 의견이 많지는 않다. Dmitry Vyukov는 연산 구조체를 커널에 두는 편이 낫지 않겠냐고 물었고, Florian Weimer는 이 기능이 “시간이 지나면 또 하나의 바이트코드 인터프리터가 되어 사실상 BPF를 재구현하게 될 것”이라고 우려했다. 기사 작성 시점까지 Dion의 답은 없었다. TCMalloc이 현행 API를 쓰게 만들고 호환 코드를 걷어내자는 방향에는 관심이 분명하지만, 이 제안이 얼마나 바뀌어야 머지될지는 지켜봐야 한다.
Typst 0.15
Typst는 PDF·SVG·PNG로, 그리고 아직 진행 중인 HTML로 문서를 조판하는 시스템이다. 기술 문서에 강해 LaTeX의 대체재로 거론되며 Rust로 작성된 Apache-2.0 소프트웨어다. LWN이 1년 전 0.13을 살펴본 뒤 6월에 나온 0.15는 가변 폰트, MathML, 다중 참고문헌 등을 담았다.
가변 폰트(variable fonts)는 굵기·기울임 같은 변형을 파일 하나에 담는 최근 조판 기술이다. 각 변형은 “축(axis)”의 값으로 고르는데, 켜고 끄는 이산 값일 수도 연속 값일 수도 있다. 아래는 Roboto Flex의 YTDE 축(디센더 길이)과 Zycon의 M1 축을 바꿔 가며 출력하는 예제다. 축 이름은 모두 네 글자라 뒤의 공백 두 개까지 이름의 일부다.
#set text(font:"Roboto Flex")
#for n in (-305, -200, -98) {
set text(variations:("YTDE": n))
[A penguin jumped quietly.
]
}
#set text(font:"Zycon")
#for n in array.range(0, 10, inclusive:true) {
set text(variations:("M1 ": n/10))
str.from-unicode(127773)
}디센더 길이를 조절하는 용도는 예컨대 표를 조판할 때 디센더가 셀 경계와 부딪히지 않게 하는 것이다. HTML 출력은 여전히 실험 기능이지만 이번 릴리스에서 수식을 MathML로 변환하게 된 것이 큰 진전이다. 같은 Typst 코드가 PDF와 사실상 동일한 결과를 브라우저에서 내주며, MathJax 같은 자바스크립트 라이브러리나 이미지 없이 TeX 품질의 수식을 웹에 올릴 수 있다.
새 “번들(bundle)” 기능은 소스 파일 하나에서 여러 출력 파일을 정의하고 그들끼리 데이터와 링크를 공유하게 한다. 상호 링크된 HTML 파일들로 이뤄진 웹사이트를 만들거나, 학회 발표 슬라이드와 프리프린트를 한 소스에서 뽑는 용도에 맞는다. 아직 작업 중이라 HTML 출력과 함께 --features 플래그가 필요하다.
typst c --features html,bundle --format bundle bundle.typ
문서 하나에 참고문헌 목록을 여러 개 두는 기능도 들어왔다. 챕터마다 참고문헌 절을 따로 두는 책이 대표적인 용도로, bibliography()의 group 인자가 번호 매김을 제어한다. none이면 절마다 1번부터 다시 시작하고, 전체 연속이나 임의 그룹으로도 지정할 수 있다.
PDF 표준을 여러 개 동시에 타깃으로 지정하는 기능도 생겼다. 보존성 표준과 접근성 표준을 함께 만족하는 PDF를 만들려는 경우다. 조합이 맞지 않으면 컴파일러가 경고나 오류로 알려준다.
$ typst c --pdf-standard 1.7,a-1b book.typ
error: PDF 1.7 is not compatible with PDF/A-1b
hint: PDF/A-1b requires version PDF 1.4
$ typst c --pdf-standard 1.7,a-2b,ua-1 book.typ
error: PDF/UA-1 error: missing document title
= hint: set the title with `set document(title: [...])`
보통 Typst 컴파일러는 문법만 맞으면 더 요구하지 않지만, 접근성 표준을 지정하면 문서가 그 표준을 지켜야 한다. UA-1은 문서 제목을 요구한다. 사소해 보여도 미국을 비롯한 여러 나라에서 접근성 PDF 생산은 공공기관의 법적 요구사항이라 실무적으로 중요한 기능이다.
기여자는 460명, 커뮤니티 패키지는 1,500개를 넘었다. 다만 프로젝트가 회사이기도 해서 방향과 기여 수용 여부에 회사의 필요가 반영된다는 점, 그리고 LLM이 생성한 코드나 설명은 거부한다는 기여 정책은 알아둘 만하다. 한계도 분명하다. 저널이 소스 제출을 요구하는 경우 여전히 LaTeX나 Word만 받는 곳이 대부분이고, 1.0 이전 상태에서 문서를 깨뜨리는 변경이 남아 있는 한 이 상황은 쉽게 바뀌지 않을 전망이다.
짧은 소식
커널 — 7.3-rc2가 9월 7일 나왔다. 토발즈는 “특별히 바쁜 rc2처럼 느껴지지 않았는데 분명 바빴다”며 머지 윈도우 직후 숨을 고르는 조용한 시기여야 할 rc2가 “풀팻(full fat)” 릴리스가 됐다고 적었다. 이상한 점은 없다면서 덧붙인 말은 이렇다.
그냥 무작위일 수도 있지만, 우리는 당연히 전부 AI 탓으로 돌릴 것이다. 실제 원인이든 아니든 탓하기 쉬우니까 😉 — 리누스 토발즈
7.3은 현재까지 개발자 2,546명이 보낸 논-머지 체인지셋 15,882개를 받았고, 그중 561명이 첫 커널 기여자다. 안정 커널은 9월 7일 7.2.4, 6.18.50, 6.12.109가 나왔고 7.2.5와 6.18.51이 리뷰 중이다.
- Asahi Linux, M3 시리즈 지원 — 애플 M3 계열 칩 지원이 설치 프로그램에 들어갔다. 웹캠, 내장 마이크, USB(하드웨어 한계인 10Gb/s까지), AV1 포함 하드웨어 비디오 디코딩, WiFi, 블루투스가 M1·M2에서 되던 수준으로 동작한다. 남은 큰 예외는 완전한 DCP 지원과 GPU로, 3D 가속은 아직 성능이나 전력 효율을 기대하지 말라고 못박았다.
- Rustls 10년 — Joe Birr-Pixton이 Rustls TLS 라이브러리 10년을 돌아보는 글을 올렸다. 2016년 5월 2일 첫 커밋 후 한 달 만에 웹 대부분의 사이트와 상호운용됐고, 4개월도 안 돼 0.1.0이 나왔다. 2024년 2월의 0.23 라인은 이후 호환성을 깨지 않는 릴리스만 43번 내면서 FIPS 인증 암호화 옵션, 인증서 압축, Encrypted ClientHello, 양자내성 암호를 추가했다. 다음은 0.24, 그 다음이 1.0이다.
- Audacity 4.0 — 인터페이스를 Qt로 다시 썼고, UI 배치를 “워크스페이스”로 저장할 수 있게 됐으며 새
.aup4프로젝트 형식을 쓴다. 3.x와 기능 호환이 완전하지 않으니 빠진 기능 목록을 확인하는 편이 좋다. - Jellyfin 12 — 데이터베이스 성능 개선, 트랜스코딩용 FFmpeg 8.1 업그레이드, 책과 만화 미디어 처리 개선이 들어갔다.
- Buildroot 2026.08 — 기여자 100명이 보낸 약 1,000건의 변경. Linux 7.1.x, Binutils 2.46.1, GCC 16.2.0, glibc 2.44 지원과 함께 M68K, IBM Power 10/11 아키텍처가 추가됐다.
- Grml 2026.09 — 시스템 관리자용 Debian 기반 라이브 배포판의 새 버전으로, 다가오는 Debian 14(“forky”) 패키지를 기반으로 한다. 커널 7.1.8, exfat USB 부팅 지원, GNU Screen 5.0.1이 포함됐다.
- LibreOffice Base 설문 — 455명이 참여했고 그중 330명 이상이 Linux에서 Base를 쓴다. CD·DVD 목록 관리부터 ERP와 재무까지 용도가 다양했다. 가장 많은 요구는 덜 산만하고 매력적인 UI, 그 다음이 버그 수정과 안정성·성능이었으며 Java 의존성 제거 요청이 많이 언급됐다.
9월 하순 행사가 몰려 있다. 22~24일 예테보리에서 Reproducible Builds Summit, 같은 기간 파리에서 Kernel Recipes, 25~27일 아헨에서 PostmarketOS/Alpine 콘퍼런스, 28~30일 토론토에서 X.Org Developers Conference가 열린다. CFP 마감은 OpenFest 2026과 Ubuntu Summit 26.10이 10월 1일이다.
이번 주 핵심 요약
- Rust never 타입 안정화는 “호환성 파괴를 어떻게 다루는가”의 사례 연구에 가깝다. crater로 영향 범위를 3,300개까지 특정하고, 실제 파손 7개를 확인하고, 메인테이너들과 함께 1,553개분의 백포트를 진행한 뒤에야 1.99에 넣었다. 나만의 코드가 깨졌다면
foo::<()>()?처럼 타입을 명시하는 것이 대개 해결책이다. - CERN 사례에서 가져갈 것은 Debian이 RHEL보다 낫다는 결론이 아니라, 배포판의 마이크로아키텍처 기준선 변경이 하드웨어 교체 비용으로 직결된다는 점이다. x86-64-v2 하나에 시스템의 47%가 부팅 불가가 됐고 교체 견적이 540만 스위스프랑이었다. 구형 하드웨어를 오래 쓰는 환경이라면 배포판 선택 기준에 이 항목을 넣어야 한다.
- 메모리 티어링은 기술적 완성도보다 “누가 이걸 원하는가”에서 막혀 있다. pghot은 여덟 번째 리비전에 리뷰 태그가 0개이고, Wilcox는 CPU 벤더가 원하는 기능이라고 못박았다. 반면 티어 인지 memcg 쪽은 두 제안 중 Hahn의 방식으로 방향이 정리됐다. CXL 도입을 검토 중이라면 커널 지원이 아직 이 단계라는 점을 감안할 것.
- TCMalloc 회귀 대응은 문서화되지 않은 커널 동작에 의존하면 어떤 청구서가 오는지 보여준다. 읽기 전용이라 문서화된 필드에 값을 써넣은 트릭 하나가 지금 커널에 새
prctl()API와 유저 메모리 순회 루프를 추가하는 논의로 이어졌다. - 파이썬 쪽에서 blanket은 free-threaded 전환기에 실질적으로 쓸모 있는 도구다. 기존
threading프리미티브를 감싸기만 하므로 테스트 대상 코드를 고칠 필요가 없고,scenario.inject()로 서드파티 모듈에 주입할 수도 있다. 경쟁 조건 재현이 안 돼 커버리지를 못 채우고 있다면 시도해 볼 만하다.