Linux Kernel(4.14) Hacks

rousalome.egloos.com

포토로그 Kernel Crash




[라즈베리파이] 워크큐(Workqueue) 용어 알아보기 [라즈베리파이] 워크큐

이번에는 워크큐에서 쓰이는 여러 용어를 소개합니다.
많은 개발자들이 워크큐를 낯설어하는 이유는 자료구조와 그 개념을 익히기 어렵기 때문입니다. 워크와 워커쓰레드 그리고 풀워크가 자료구조가 어떤 형태로 연결되서 처리하는지 큰 그림을 그리기 어렵습니다. 각각 의미와 자료 구조를 살펴보겠습니다. 

1. 워커 쓰레드
우선 워커 쓰레드부터 알아보겠습니다. 
워커 쓰레드는 워크를 실행하는 프로세스입니다. 리눅스 시스템에서 항상 백그라운드로 실행하는 친숙한 프로세스입니다. 프로세스 이름은 "kworker/"로 시작하며 워크큐 종류에 따라 "kworker/" 이후 번호가 붙습니다.

다음은 라즈베리파이에서 확인한 워커 쓰레드입니다.
root@raspberrypi:/# ps -ely | grep kworker
S   UID   PID  PPID  C PRI  NI   RSS    SZ WCHAN  TTY          TIME CMD
I     0     4     2  0  60 -20     0     0 worker ?        00:00:00 kworker/0:0H
I     0    16     2  0  60 -20     0     0 worker ?        00:00:00 kworker/1:0H
I     0    21     2  0  60 -20     0     0 worker ?        00:00:00 kworker/2:0H
I     0    26     2  0  60 -20     0     0 worker ?        00:00:00 kworker/3:0H
I     0    29     2  0  80   0     0     0 worker ?        00:00:00 kworker/0:1
I     0    30     2  0  80   0     0     0 worker ?        00:00:00 kworker/1:1

워커 쓰레드 이름은 “kworker/” 으로 시작하는데 뒷 쪽에 숫자가 붙습니다. 예를 들어 위에서 소개한 가장 마지막 워커 쓰레드 이름은 “kworker/1:1”입니다. 뒤에 1:1 이란 숫자 보입니다.

워커 쓰레드 이름을 결정하는 코드는 create_worker() 함수에 있으며 어떤 기준으로 워커 쓰레드 이름을 짓는지는 코드 분석과 함께 알아볼 예정입니다. 

다음 그림으로 워커풀과 워커 및 워크를 소개합니다. 개념만으로는 각 워크큐 구성 요소에 대해 이해하기 어려우니 자료구조가 어떤 흐름으로 구성돼 있는지 알아볼 필요가 있습니다.


2. 풀워크큐
먼저 위 그림에서 system_wq 멤버 중에 cpu_pwqs를 보겠습니다. 이 멤버 변수는 per-cpu 타입 변수이며 이를 풀워크큐라고 부릅니다. 이 cpu_pwqs 멤버 주소 기준으로 실행 중인 CPU 번호 기준으로 오프셋을 per_cpu_ptr()이란 함수로 적용하면 CPU 개수 별로 있는 struct pool_workqueue 구조체 메모리 공간에 접근할 수 있습니다. per_cpu_ptr() 이란 함수를 호출하면 __per_cpu_offset[] 이란 배열에 접근해서 실행 중인 CPU 번호에 해당하는 오프셋 주소를 더합니다.

라즈베리파이 CPU개수가 4개이니 4개 메모리 공간에 struct pool_workqueue이란 자료구조가 있습니다. 풀 워크큐는 멀티 CPU 별로 처리하며 워크와 워커 개수를 관리합니다.

3. 워커풀
다음으로 워커풀을 알아보겠습니다. 워커풀은 struct worker_pool 자료구조이며 워커 쓰레드와 워크를 관리합니다.  per-cpu 타입 변수인 풀 워크큐 멤버 중 하나이니 CPU 개수만큼 워커 풀이 있습니다. 워커풀은 워크큐의 핵심 자료 구조 중 하나입니다. 워커풀은 per-cpu 변수인데 워크를 큐잉할때 실행하는 CPU 번호 기준으로 워크를 처리합니다. 만약 CPU1에서 워크를 큐잉하면 CPU1에 해당하는 per-cpu 워크 풀이 이 워크를 관리합니다. 또한 디바이스 드라이버에서 워크를 큐잉하면 struct worker_pool 멤버 중 worklist이란 링크드 리스트에 등록합니다. 

워커는 워커 쓰레드를 관리하는 자료구조이며 워커풀의 workers란 링크드 리스트로 관리됩니다. 자료구조는 struct worker입니다. 워크큐에서 워커와 워커쓰레드란 용어를 무작위로 쓰는데 워커란 용어를 보면 워커 쓰레드를 떠 올릴 필요가 있습니다. 예를 들어 워커가 생성됐다면 워커 쓰레드가 생성됐다고 봐야 합니다. 반대로 워커를 해제했다면 워커에 대응하는 워커 쓰레드를 해제한 겁니다.

4. 워크
워크는 워크큐를 실행하는 단위입니다. 워크는 워커 쓰레드에서 실행하는데 워커 쓰레드는 워크를 실행하기 위해 생성됩니다. 리눅스 커널에서는 work라고 하는데 앞으로 이 용어를 그대로 씁니다. 커널 후반부 처리는 워크의 워크 핸들러에서 실행합니다. 

인터럽트 후반부 처리를 코드를 2단계로 나눌 경우 빠르게 실행해야 할 코드는 인터럽트 핸들러에 위치하고 나머지 커널 쓰레드 레벨에서 실행해도 되는 코드(후반부 처리)는 워크 핸들러에서 처리하도록 설계할 수 있는 겁니다. 결국 워크를 실행하는 이유는 워크 핸들러를 호출하기 위해서입니다. 

워크 핸들러를 지정하면 호출되는 과정은 동적 타이머와 비슷합니다. 동적 타이머는 만료 시각을 설정하면 동적 타이머 핸들러가 호출합니다. 마찬가지로 워크를 워크큐에 큐잉해서 워커 쓰레드에서 실행할 때 해당 워크 핸들러를 호출합니다.

동적 타이머를 소개한 김에 동적 타이머 핸들러와 워크 핸들러의 차이점에 대해서 살펴보면서 워크에 대해 조금 더 알아봅시다. 후반부 처리를 위해 워크 대신 동적 타이머를 생성해도 되지 않을까란 질문을 던질 수도 있습니다. 동적 타이머는 동적 타이머 핸들러와 실행 시각을 지정하면 동적 타이머가 만료할 시각에 동적 타이머 핸들러가 실행됩니다.

동적 타이머 핸들러와 워크 핸들러의 가장 큰 차이점은 뭘까요? 각각 핸들러 실행 시점이 다릅니다. 동적 타이머는 타이머 인터럽트가 발생한 후 Soft IRQ 서비스 콜백 함수로 호출됩니다. 따라서 동적 타이머 핸들러 함수는 Soft IRQ Context에서 실행하니 빠른 시간 내에 처리해야 합니다.

대신 워크는 워커 쓰레드가 실행하고 워크 핸들러 실행 도중 스케줄링 될 수 있으므로 워크 핸들러 실행 시간이 오래 걸려도 됩니다. 또한 커널 스케줄링 관련 함수를 모두 호출할 수 있습니다.




핑백

덧글

  • Daniel 2018/12/13 16:05 # 삭제 답글

    안녕하세요.
    Posting이 Linux Kernel을 배워가는데 정말 큰 도움이 되고있습니다.
    중간에 이 책에서 사용하는 용어를 그대로 사용하겠다고 하셨는데 참고하시는 책이 어떤 책인지 알 수 있을까요?
    감사합니다.
  • Guillermo 2018/12/14 10:13 #

    제글을 주의깊게 읽어주셔서 감사합니다.

    지금 제 블로그에 올리는 글은 제가 집필 중인 '리눅스 커널 분석(4.14)'이란 책 초본 내용입니다.
    그래서 이런 표현을 쓴 것입니다.

    올린 글을 다음과 같이 고쳐야 겠군요.

    리눅스 커널에서는 work라고 하는데 이 책에서는(x) 이 용어를 그대로 씁니다.
    -> 리눅스 커널에서는 work라고 하는데 앞으로(o) 이 용어를 그대로 씁니다.
  • Daniel 2018/12/14 11:02 # 삭제 답글

    답변 감사합니다.
    어서 책으로 출판됐으면 좋겠네요.
    정말 많은 도움을 받고 있습니다.
  • Guillermo 2018/12/14 11:07 #

    도움이 되고 있다니 저도 기쁩니다.

    혹시 비밀댓글로 이메일 주소 알려주시면 지금 댓글 달아주신 워크큐 전체 챕터 원고 보내드릴 수도 있습니다.
    더 많은 도움이 됐으면 좋겠습니다.
  • 2018/12/14 11:23 # 삭제 답글 비공개

    비공개 덧글입니다.
  • 2018/12/14 11:29 # 답글 비공개

    비공개 덧글입니다.
  • 2018/12/15 10:23 # 비공개

    비공개 답글입니다.
댓글 입력 영역