리눅스 커널에서 work라고 부르나 워크(struct work_struct)로 명시 하겠습니다.
워크란 무엇이고 어떤 동작을 할까요? 워크는 워크큐 실행 단위이며 워커 쓰레드에서 실행합니다. 커널 후반부 처리 기법으로 워크큐를 많이 쓴다고 했는데 워크가 바로 후반부 처리를 합니다. 워크가 어떻게 동작하는지 이번 장에서 코드를 분석하면서 상세히 알아볼 예정입니다.
struct work_struct 구조체
워크를 표현하는 자료구조는 무엇일까요? 정답은 struct work_struct 이며 다음 코드와 같습니다.
[https://elixir.bootlin.com/linux/v4.14.43/source/include/linux/workqueue.h]
1 struct work_struct {
2 atomic_long_t data;
3 struct list_head entry;
4 work_func_t func;
5 #ifdef CONFIG_LOCKDEP
6 struct lockdep_map lockdep_map;
7#endif
8 };
라즈비안에서는 CONFIG_LOCKDEP 이란 컨피그는 설정돼지 않았으니 func이 가장 마지막 멤버입니다.
struct work_struct 이란 구조체가 워크를 표현하니 각 멤버의 의미를 알아봅시다.
먼저 2번 줄 멤버부터 보겠습니다. data이란 멤버에는 워크 상태를 저장합니다.
워크를 초기화하면 data 멤버에 0xFFFFFFE0을 저장하고, 워크를 워크큐에 큐잉하면 WORK_STRUCT_PENDING_BIT(0x1)값을 저장합니다. data 멤버을 읽어 워크를 제어하는 코드가 많으니 워크큐를 디버깅할 때 주의 깊게 봐야 합니다.
다음 3번째 줄 코드 entry 멤버를 보겠습니다.
3 struct list_head entry;
entry는 링크드 리스트로 선언됐습니다.
워크는 워커풀 struct worker_pool 구조체 중 링크드 리스트 멤버인 worklist에 등록합니다. 여기서 struct worker_pool.worklist는 struct work_struct.entry 주소를 가르킵니다.
4번째 줄 코드의 func이란 멤버는 워크 핸들러 함수를 지정합니다.
4 work_func_t func;
워크큐 후반부 처리를 어디서 하는가라고 누군가가 질문을 하면 워크 핸들러라고 대답할 수 있습니다. 워크 핸들러 함수는 언제 실행할까요? 워크를 워크큐에 큐잉하고 나서 워커 쓰레드가 실행하면 워크 핸들러 함수를 호출합니다. 동적 타이머의 핸들러 함수와 유사하다고 볼 수 있습니다.
워크큐에서 워크란 자료구조를 언제 어떻게 처리하는지 잘 알아야 워크큐 커널 코드 이해가 빠릅니다. 워크란 struct work_struct 이란 구조체로 표현하고 워크큐 후반부 처리는 워크 핸들러가 실행할 때 완료된다는 점을 기억할 필요가 있습니다.
다음에서는 워크를 초기화하는 방법에 대해서 알아봅니다. 워크를 워크큐에 큐잉하기 전에 거쳐야 하는 단계입니다.
Reference(워크큐)
워크큐(Workqueue) Overview
최근 덧글