Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

57189
2265
156768


[리눅스커널] 커널동기화: 레이스 컨디션은 왜 발생할까? 9. 커널 동기화(spinlock/mutex)

이전 절에서 레이스 컨디션과 임계 영역의 개념을 소개했습니다. 이 내용을 읽으니 자연스럽게 다음과
같은 의문이 생깁니다. 레이스 컨디션은 왜 발생할까요? 이번 시간에는 그 이유를 살펴보겠습니다.

SMP(symmetric multiprocessing)

레이스 컨디션이 발생하는 첫 번째 이유는 리눅스 시스템에서 SMP(symmetric multiprocessing)를 적용하기 때문입니다. 여기서 SMP란 하나의 시스템에 다수의 CPU가 한 개의 메모리를 쓰는 컴퓨터 시스템 아키텍처를 말합니다.

이러한 SMP에 대한 정의를 읽어도 레이스 컨디션이 왜 발생하는지 잘 이해되지 않습니다. 그런데 소프트웨어 관점에서 SMP 시스템에서는 다수의 CPU 내에 있는 프로세스가 다음과 같이 실행됩니다. 다수의 CPU에서 병렬로 프로세스가 커널 코드를 실행한다.

만약 리눅스 시스템에 4개의 CPU를 탑재한 SMP 시스템에서는 4개의 CPU에서 병렬로 서로 다른 프로세스들이 실행될 수 있는 것입니다. 이 경우 서로 다른 CPU에서 실행 중인 프로세스가 같은 코드나 함수에 접근하는 상황을 초래할 수 있습니다. 이런 현상을 동시성(Concurrency)이라고도 부릅니다. 달리 보면 SMP 시스템은 동시성이 발생할 수 밖에 없는 조건입니다.

그런데 동시성과 레이스 컨디션은 비슷한 의미를 지닙니다. 그러면 두 용어의 차이점은 무엇일까요? 동시성은 SMP 시스템에서 2개의 코어에서 같은 코드를 실행하는 것을 의미합니다. 레이스 컨디션은 ‘동시성’이란 환경에서 2개의 코어에 있는 프로세스가 임계 영역을 실행했을 때 문제가 발생하는 조건이나 상황을 의미합니다.

현재 대부분의 리눅스 시스템은 SMP 환경에서 구동합니다. 여러분이 사용 중인 스마트폰을 포함해서 대부분 리눅스는 SMP 시스템에서 실행됩니다. 라즈베리 파이에도 쿼드코어 CPU(4개의 코어)가 탑재돼 있습니다.


선점 스케줄링 

레이스 컨디션이 발생하는 두 번째 요인은 리눅스 커널이 선점 스케줄링을 지원하기 때문입니다. 현재 대부분의 리눅스 커널은 선점형 스케줄링 환경에서 실행되는데, 이를 가리켜 “우리가 입력한 코드 블록이 실행되는 도중 선점 스케줄링될 수 있다”라고 해석할 수 있습니다.

이 내용을 읽고 나면 자연히 다음과 같은 의문이 생깁니다. 

   *선점 스케줄링으로 레이스 컨디션이 발생할까?

선점 스케줄링으로 다양한 종류의 레이스 컨디션이 발생할 수 있습니다. 이해를 돕기 위해 한 가지 예를 들어보겠습니다. 여러분이 작성한 코드가 rpi_set_synchronize() 함수에서 실행 중이라고 가정하겠습니다. A 프로세스가 rpi_set_synchronize() 함수를 실행하는 도중 선점 스케줄링이 된 후 B 프로세스 가 다시 rpi_set_synchronize() 함수를 호출하면 어떻게 될까요? 중요한 자료구조를 처리하는 코드 구간을 B 프로세스가 다시 실행한다면 레이스 컨디션이 발생할 수 있습니다.

선점 스케줄링이 발생해 다른 프로세스가 rpi_set_synchronize() 함수를 호출하지 않아도 다른 문제를 겪을 수 있습니다. 만약 rpi_set_synchronize() 함수 내에서 알고리즘 연산이나 정확한 딜레이를 주는 코드를 실행 중이라고 가정하겠습니다. 이때 선점 스케줄링으로 실행하는 코드를 멈추고 다른 프로세스로 스케줄링되면 정해진 딜레이를 주지 못해 오동작을 유발할 수 있습니다.

이처럼 레이스 컨디션이 발생하는 원인 중 하나가 선점 스케줄링입니다.


인터럽트 발생 

어떤 CPU에서도 인터럽트는 언제든 발생할 수 있습니다. 여러분이 작성한 코드가 실행되는 도중에 인터럽트가 발생하면 실행을 멈추고 인터럽트 핸들러를 호출할 수 있습니다. 만약 rpi_set_synchronize() 함수를 실행하는 도중 인터럽트가 발생해 다시 rpi_set_synchronize() 함수에 진입하면 어떻게 될까요? 이 상황에서도 레이스 컨디션이 발생할 수 있습니다.


유저 프로세스에서 스레드 핸들링 

유저 어플리케이션에서 스레드를 생성해 한 개의 파일을 다수의 스레드가 접근할 수 있습니다. 이 과정에서 동시성 혹은 레이스 컨디션이 발생할 수 있습니다.

동시성 발생 사례 소개

임베디드 리눅스를 처음 접하는 분들은 동시성 문제가 왜 발생하는지 감이 잘 안 올 것입니다. 이해를
돕기 위해 리눅스 커널 커뮤니티에서 발췌한, SMP 환경에서 동시성 문제가 발생하는 사례를 소개하겠
습니다.

다음 사례를 소개합니다.
https://lore.kernel.org/patchwork/patch/880120/
01 CPU0                                    CPU1
02 mmap syscall                           ioctl syscall
03 -> mmap_sem (acquired)                 -> ashmem_ioctl
04 -> ashmem_mmap                            -> ashmem_mutex (acquired)
05    -> ashmem_mutex (try to acquire)       -> copy_from_user
06                                              -> mmap_sem (try to acquire)

03번째 줄에서 CPU0이 mmap_sem() 함수에 접근해 락을 획득했습니다. 그런데 맨 오른쪽 부분을 보면 거의 동시에 CPU1에서 06번째 줄과 같이 mmap_sem() 함수에 접근하고 있습니다.

위 사례를 보면 “2개의 CPU에서 실행 중인 프로세스가 동시에 같은 코드 블록을 실행한다”라는 사실을 알 수 있습니다.

여기까지 2개의 CPU에서 실행 중인 프로세스가 같은 코드 블록에 동시에 접근하는 사례를 살펴봤습니다. 이어서 다음 절에서는 리눅스 커뮤니티에서 논의된 레이스 컨디션과 관련된 패치를 분석하면서 레이스 컨디션에 대해 더 알아보겠습니다.



"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)

#Reference(커널 동기화)
커널 동기화 기본 개념 소개
레이스 발생 동작 확인
커널 동기화 기법 소개
스핀락
뮤텍스란
커널 동기화 디버깅


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2


Thanks,
Austin Kim



핑백

덧글

  • Daniel 2020/02/06 09:49 # 답글

    Deadlock 사례를 두 개 들어주셨는데 두 개 모드 lock을 한 곳에서 획득하고 다른 곳에서 또 획득하며 Deadlock에 빠진다고 하신 부분이 이해가 잘가지 않습니다.
    Lock이라는 게 한 곳에서 획득을 하게 되면 다른 곳에서 획득을 하지 못하고 기다리게 함으로써 Critical section을 보호하는 것 아닌가요?
    Lock이 한 곳에서 획득되고 그 lock을 다른 곳에서 획득하려고 하고 있는데 timeout 시간 내에 lock을 풀어주지 않아서 Deadlock이 발생한 사례일까요?
  • AustinKim 2020/02/06 09:58 #

    예리한 질문을 해주셨군요. 감사합니다.

    첫 번째 사례는 잘못된 예시(임계영역)이며, 이미 다른 초고수 커널 개발자님께서 지적해주셨습니다.
    데드락 보다는 '두 개의 CPU에서 실행 중인 프로세스가 같은 코드 구간을 실행한다'와 관련된 예시로 이 포스팅을 보시면 좋을 것 같습니다.

    참고로, 이 포스팅의 글은 오늘 내로 업데이트 될 예정입니다.
    제가 좀 바뻐서 블로그를 많이 신경 못 쓰고 있어요. ):

    Thanks,
    Austin Kim
댓글 입력 영역