딜레이 워크를 실행하기 위해서 먼저 딜레이 워크를 초기화해야 합니다. 이를 위해 INIT_DELAYED_WORK() 매크로 함수를 호출해야 합니다.
먼저 딜레이 워크를 초기화하는 디바이스 드라이버 코드를 열어 봅시다.
[https://elixir.bootlin.com/linux/v4.14.43/source/drivers/thermal/da9062-thermal.c#L248]
1 static int da9062_thermal_probe(struct platform_device *pdev)
2 {
3 struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
4 struct da9062_thermal *thermal;
...
5 INIT_DELAYED_WORK(&thermal->work, da9062_thermal_poll_on);
5번째 줄 코드를 보면 첫 번째 인자인 &thermal->work 로 struct delayed_work 구조체 주소를 전달하고, 두 번째 인자로 워크 핸들러인 da9062_thermal_poll_on() 함수를 지정합니다.
딜레이 워크 초기화 과정은 워크 초기화 코드와 비교해서 어떤 차이가 있을까요? 딜레이 워크는 초기화 과정에서 핸들러 함수를 바로 지정합니다.
이제 INIT_DELAYED_WORK() 함수 구현부 코드를 보면서 어떤 동작을 수행하는지 조금 더 확인합시다.
[https://elixir.bootlin.com/linux/v4.14.43/source/include/linux/workqueue.h#L259]
1 #define INIT_DELAYED_WORK(_work, _func) \
2 __INIT_DELAYED_WORK(_work, _func, 0)
3
4 #define __INIT_DELAYED_WORK(_work, _func, _tflags) \
5 do { \
6 INIT_WORK(&(_work)->work, (_func)); \
7 __setup_timer(&(_work)->timer, delayed_work_timer_fn, \
8 (unsigned long)(_work), \
9 (_tflags) | TIMER_IRQSAFE); \
10 } while (0)
2번 줄 코드를 보면 INIT_DELAYED_WORK() 매크로 함수는 __INIT_DELAYED_WORK() 함수로 치환됩니다. 이때 __INIT_DELAYED_WORK() 매크로 함수에 전달된 인자는 그대로 __INIT_DELAYED_WORK() 함수에 전달합니다.
6번째 줄 코드를 보겠습니다.
워크를 초기화할 때 썼던 INIT_WORK() 매크로 함수를 써서 워크를 초기화합니다. 여기서 __INIT_DELAYED_WORK() 함수에 전달된 첫 번째 인자는 _work인데 구조체는 struct delayed_work입니다.
INIT_WORK() 매크로 코드는 다음과 같습니다. 링크드 리스트인 entry 멤버를 초기화하고 data에 WORK_DATA_INIT() 값인 0xFFFFFFE0을 지정합니다. func 멤버에는 워크 핸들러 함수 주소를 저장합니다.
[https://elixir.bootlin.com/linux/v4.14.43/source/include/linux/workqueue.h#L236]
1 #define INIT_WORK(_work, _func) \
2 __INIT_WORK((_work), (_func), 0)
3
4 #define __INIT_WORK(_work, _func, _onstack) \
5 do { \
6 __init_work((_work), _onstack); \
7 (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
8 INIT_LIST_HEAD(&(_work)->entry); \
9 (_work)->func = (_func);
위에서 살펴봤듯이 struct delayed_work의 첫 번째 멤버는 work이고 타입은 struct work_struct 이니 struct delayed_work->work이란 인자로 워크를 초기화하는 겁니다.
다음 7~9번째 줄 코드를 보겠습니다.
__setup_timer() 함수를 호출해서 동적 타이머를 초기화합니다. 동적 타이머 구조체는 &(_work)->timer이고 동적 타이머 핸들러는 delayed_work_timer_fn() 함수입니다. __setup_timer() 함수에 전달하는 세 번째 인자는 _work인데 구조체는 struct delayed_work입니다.
__setup_timer() 함수는 동적 타이머를 초기화하는 동작입니다.
딜레이 워크는 지정한 시각 후에 실행하는 워크입니다. 이를 위해 동적 타이머를 쓰는 겁니다. 딜레이 워크 함수로 지연 시각을 지정하면 동적 타이머 핸들러인 delayed_work_timer_fn() 함수가 지연 시각 후 실행합니다.
이번에 딜레이 워크를 초기화하는 코드 리뷰 분석을 했습니다. 코드를 열어보니 워크와 비슷한 코드가 많습니다. 딜레이 워크를 워크를 HZ 단위 시각으로 지연해서 실행하는 워크이기 때문입니다.
다음에 딜레이 워크를 실행하는 동작을 알아보겠습니다.
"이 포스팅이 유익하다고 생각되시면 댓글로 응원해주시면 감사하겠습니다.
그리고 혹시 궁금점이 있으면 댓글로 질문 남겨주세요. 상세한 답글 올려드리겠습니다!"
Reference(프로세스 관리)
4.9 프로세스 컨택스트 정보는 어떻게 저장할까?
4.9.1 컨택스트 소개
4.9.2 인터럽트 컨택스트 정보 확인하기
4.9.3 Soft IRQ 컨택스트 정보 확인하기
4.9.4 선점 스케줄링 여부 정보 저장
4.10 프로세스 디스크립터 접근 매크로 함수
4.10.1 current_thread_info()
4.10.2 current 매크로란
4.11 프로세스 디버깅
4.11.1 glibc fork 함수 gdb 디버깅
4.11.2 유저 프로그램 실행 추적
최근 덧글