• 2023. 9. 6.

    by. 도 현

    반응형

    리눅스 커널은 기본적으로 7개의 워크큐를 지원하며, 부팅 과정에서 생성한다. 워크큐를 생성하는 함수를 분석하면서 워크큐의 종류를 살펴보자.

     

    1. alloc_workqueue() 함수 분석

    워크큐를 생성하려면 alloc_workqueue() 함수를 호출해야 한다. 먼저 alloc_workqueue() 함수의 구현부를 보자. 코드를 보면 __alloc_workqueue_key() 함수로 치환된다는 사실을 알 수 있다. 워크큐를 표현하는 자료구조는 workqueue_struct 구조체이다. 이 구조체를 기준으로 메모리를 할당한 후 각 필드를 초기화하는 것이 alloc_workqueue() 함수의 역할이다. 먼저 alloc_workqueue() 함수에 전달하는 인자들의 속성을 알아보자.

    - fmt

    워크큐의 이름을 지정하며, workqueue_struct 구조체의 name 필드에 저장된다. 여기에는 "events", " events_highpri", "events_unbound", "events_freezable", " events_power_efficient", "events_freezable_power_efficient"라는 이름이 등록된다.

    - flags

    워크큐의 속성 정보를 저장한다. 이 매개변수는 workqueue_struct 구조체의 flags 필드에 저장된다. flags에 저장하는 열거형 타입의 멤버가 있다. enum 플래그는 워크큐의 종류에 따라 다르게 설정된다.

    - max_active

    workqueue_struct 구조체의 saved_max_active에 저장한다.

    alloc_workqueue() 함수는 workqueue_init_early() 함수에서 호출한다. workqueue_init_early() 함수에서 alloc_workqueue() 함수를 호출해 워크큐를 생성하는 것이다.

     

    2. 7가지 워크큐

    workqueue_init_early() 함수는 리눅스 커널에서 지원하는 7가지 워크큐를 생성한다. 이번에는 workqueue_init_early() 함수를 분석하면서 워크큐의 종류를 알아보자. workqueue_init_early() 함수의 핵심 동작은 다음과 같다,

    - 7가지 워크큐 생성

    - 워크큐가 제대로 생성됐는지 점

    "event"라는 이름으로 워크큐를 생성해서 system_wq라는 전역변수에 저장한다. 여기서 system_wq는 보통 시스템 워크큐라고도 부른다. system_highpri_wq라는 워크큐를 생성하는 코드이다. system_highpri_wq 워크큐는 시스템 워크큐에서 쓰는 워커 스레드보다 우선순위가 높은 워커 스레드가 처리한다. workqueue_struct 구조체의 flags 필드는 WQ_HIGHPRI로 저장한다. system_long_wq라는 워크큐를 생성하여 system_wq와 유사한 환경에서 조금 더 오래 걸리는 작업에 사용한다.  각각 워크큐 전역변수가 NULL이 아닌지 OR 연산으로 확인한 후 하나의 워크큐라도 NULL이면 커널 크래시를 유발한다. 커널 내부에서 기본적으로 7가지 워크큐를 지원하며, 커널 내부의 서브시스템에서도 워크큐를 사용해 커널을 관리한다. 한 가지 워크큐라도 제대로 생성하지 못하면 커널의 기본 동작을 장담할 수 없기 때문에 제대로 생성됐는지 체크하는 것이다. 리눅스 커널에서는 워크큐 개수가 7개로 정해져 있지 않다 디바이스 드라이버에서 워크큐를 생성해서 드라이버 흐름을 제어할 수 있다. 앞에서 설정한 7개의 워크큐는 기본적으로 리눅스 커널에서 생성하는 것이다.

     

    3. 워크란?

    워크큐를 구성하는 주요 개념 중 핵심은 워크이다. 그 이유는 워크큐를 실행하는 기본 단위가 워크이기 때문이다. 누군가 " 보통 워크큐에 워크를 큐잉 한다"라고 말하면 '후반부 처리를 워크가 실행하도록 워크를 워크큐에 실행 요청한다'라는 의미로 해석할 수 있다. 

     

    1. work_struct 구조체

    - atomic_long_t data

    data 필드는 워크 실행 상태를 나타내며 다음과 같은 값을 저장한다.

    - 워크 초기화 : 0xFFFFFFE0

    - 워크를 워크큐에 큐잉 : WORK_STRUCT_PENDING_BIT(0x1)

    커널 내부의 워크큐 함수에서는 워크의 실행 정보가 기록된 data 필드를 읽어 워크를 제어하는 코드가 많다.

    - struct list_head entry

    entry 필드의 타입은 연결 리스트이다. 워크를 워크큐에 큐잉하면 워커 풀 worker_pool 구조체 중 연결 리스트인 worklist에 등록된다. 여기서 worker_pool 구조체의 worklist는 워크의 entry 주소를 저장한다.

    - work_func_t func

    func 필드는 워크 핸들러 함수의 주소를 저장한다.

     

    워크를 워크큐에 큐잉하면 커널 스레드인 워커 스레드에서 워크 핸들러를 호출한다. 워크큐 후반부 처리는 워크 핸들러에서 후반부를 처리한다. 워크 핸들러 함수는 워크를 워크큐에 큐잉하고 나서 워커 스레드가 실행되면서 워크 핸들러 함수를 호출한다. 워크란 work_struct라는 구조체로 표현할 수 있고 후반부의 코드는 워크 핸들러가 실행될 때 처리된다. 

    반응형