
멀티스레드 프로그래밍해본 분이라면 한 번쯤 락(lock) 때문에 골머리 앓아본 적 있을 거예요. 락을 잡으면 안전하긴 한데 느려지고, 락 없이 짜자니 race condition이 무서워서 손이 안 가고요. 그래서 보통은 atomic 명령어를 쓰거나 lock-free 자료구조를 직접 짜는 길로 가는데, 그것도 쉽지 않거든요. 그런데 사실 리눅스 커널에는 이걸 우아하게 풀어주는 기능이 숨어있어요. 바로 Restartable Sequences, 줄여서 rseq라는 녀석이에요.
Justine Tunney(cosmopolitan libc 만든 그분이요)가 자기 사이트에 정리한 글이 있는데, 이게 진짜 좋은 입문 자료라 같이 풀어볼게요.
rseq가 뭐예요?
핵심 아이디어를 한 줄로 말하면 "어떤 코드 영역이 실행되는 동안 다른 CPU로 옮겨가거나 다른 스레드한테 선점당하면, 그 영역의 시작점으로 다시 점프시켜준다"는 거예요. 리눅스 커널이 4.18 버전부터 정식 지원하기 시작했고, glibc 2.35부터 기본으로 활성화됐어요.
이게 왜 강력하냐면, per-CPU 자료구조에 접근할 때 락이나 atomic 없이도 안전하게 쓸 수 있게 해주거든요. per-CPU 자료구조라는 게 뭐냐면, 각 CPU 코어마다 자기 전용 데이터를 두는 거예요. 다른 코어랑 공유 안 하니까 충돌 자체가 안 일어나는 게 정상이죠.
비유로 설명해볼게요. 식당에서 종업원이 주문을 받아 적고 있어요. 적는 도중에 누가 "다른 테이블 가서 주문 받아"라고 부르면, 종업원은 적던 종이를 그냥 찢어버리고 새 테이블에 가서 처음부터 다시 적어요. 이게 rseq의 본질이에요. "내가 작업하다가 중간에 자리를 옮기게 되면, 그 작업은 없던 일로 하고 처음부터 다시 한다."
어떻게 동작해요?
rseq는 syscall로 커널에게 "이 메모리 영역에 critical section의 시작점, 끝점, 그리고 중단됐을 때 점프할 abort handler 주소를 적어둘게요"라고 알려줘요. 그러면 커널은 그 스레드를 다른 CPU로 옮기거나 시그널을 보낼 때, 만약 그 시점에 명령어 포인터가 critical section 안에 있다면 abort handler로 점프시켜요.
코드로 보면 보통 이런 구조예요. 먼저 "내가 지금 어느 CPU에 있나?"를 rseq의 빠른 경로로 읽어와요. 그 다음 그 CPU 전용 자료구조에 값을 쓰는데, 이때 atomic이나 락을 안 써요. 만약 쓰는 도중에 다른 CPU로 옮겨가면? 커널이 알아서 abort 시켜서 처음부터 다시 시작하게 해줘요. 결과적으로 "성공할 때까지 빠른 경로로 시도하다가, 실패하면 다시 시도"가 되는 거예요.
왜 빠른가요?
atomic 명령어, 예를 들어 x86의 lock cmpxchg 같은 건 CPU 캐시 라인을 다른 코어들로부터 격리시키는 비용이 들어요. 멀티코어 시스템에서 캐시 일관성 프로토콜이 작동해야 하니까 한 번 호출에 수십에서 수백 사이클이 들 수 있어요.
반면 rseq를 잘 쓴 코드는 일반 메모리 read/write만 써요. 다른 코어와 캐시 라인을 공유할 필요가 없으니까(per-CPU니까!) 거의 캐시 미스 없이 동작해요. Justine이 측정한 벤치마크를 보면 malloc 같은 메모리 할당 경로에서 의미 있는 성능 향상을 보여줘요. tcmalloc이나 jemalloc 같은 최신 메모리 할당기들이 이미 rseq를 활용하고 있고요.
비슷한 기술과 비교
비슷한 영역에 있는 기술로는 RCU(Read-Copy-Update), seqlock, hazard pointer 같은 게 있어요. 이들은 모두 "락 없이 동시성을 다루겠다"는 같은 목적이지만 접근 방식이 달라요. RCU는 읽기는 거의 무료지만 쓰기 쪽 복잡도가 높고, seqlock은 짧은 자료에 좋지만 라이터가 자주 변경하면 리더가 계속 재시도해야 하고, hazard pointer는 메모리 reclamation 문제를 풀어주지만 코드가 길어져요.
rseq는 "per-CPU 데이터에 한해서는 그냥 보통 메모리 접근처럼 쓰고, 중단되면 다시 시도해라" 라는 간단한 모델을 제공해서 특정 상황에서 깔끔하게 들어맞아요. 단점은 리눅스 전용이고, 직접 어셈블리 수준으로 다뤄야 하는 경우가 많아서 사용 난이도가 있다는 거예요.
한국 개발자에게 시사점
당장 rseq를 직접 코딩할 일은 많지 않을 거예요. 하지만 우리가 매일 쓰는 메모리 할당기, 가비지 컬렉터, 데이터베이스 엔진 내부에서는 이미 이런 기법이 활발히 쓰이고 있어요. 고성능 시스템을 다루는 일을 한다면, 한 번쯤 깊이 들여다볼 가치가 있어요.
특히 게임 서버, 거래 시스템, 메시지 큐처럼 마이크로초 단위 성능이 중요한 분야에서는 rseq를 활용하는 자료구조를 직접 짤 일이 생길 수도 있고요. 그리고 "커널이 사용자 공간 코드의 실행 흐름을 알고 개입한다"는 이 아이디어 자체가 흥미로워요. 운영체제와 응용프로그램이 더 긴밀하게 협력하는 미래의 한 단면을 보여주는 사례거든요.
정리
rseq는 "락 없이 빠르게, 그러나 안전하게"를 새로운 방식으로 풀어내는 도구예요. 코드를 자르지 않고 "중단되면 처음부터 다시" 라는 우아한 추상으로 해결해주거든요.
여러분은 lock-free 자료구조를 직접 만들어본 경험이 있으세요? 어떤 도구나 패턴이 가장 쓸 만했는지 궁금하네요.
🔗 출처: Hacker News
TTJ 코딩클래스 정규반
월급 외 수입,
코딩으로 만들 수 있습니다
17가지 수익 모델을 직접 실습하고, 1,300만원 상당의 자동화 도구와 소스코드를 받아가세요.
"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"
실제 수강생 후기- 비전공자도 6개월이면 첫 수익
- 20년 경력 개발자 직강
- 자동화 프로그램 + 소스코드 제공