Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

1138
469
422311


[리눅스커널] 동기화 - 커널 프로세스 레이스 컨디션 9. 커널 동기화(spinlock/mutex)

이번에 커널 공간에서만 실행하는 커널 쓰레드에서 발생하는 Race를 확인합니다. 커널 쓰레드 중 많이 알려진 워커 쓰레드를 예를 듭시다.

이를 위해 다음 패치 코드를 적용할 필요가 있습니다.
1 diff --git a/kernel/workqueue.c b/kernel/workqueue.c
2 --- a/kernel/workqueue.c
3 +++ b/kernel/workqueue.c
4 @@ -2187,6 +2187,12 @@ static void process_scheduled_works(struct worker *worker)
5  *
6  * Return: 0
7  */
8  +
9  +static unsigned int global_func_exe_times = 0;
10 +
11 +extern void trace_function_dummy_call(void);
12 +
13 static int worker_thread(void *__worker)
14 {
15  struct worker *worker = __worker;
16 @@ -2195,6 +2201,15 @@ static int worker_thread(void *__worker)
17  /* tell the scheduler that this is a workqueue worker */
18  worker->task->flags |= PF_WQ_WORKER;
19 woke_up:
20 + trace_printk("[+] comm: %s, pid: %d, global_w_func_exe_times: %d from(%pS) \n", 
21 + current->comm, current->pid, global_func_exe_times, (void *)__builtin_return_address(0));
22 +
23 + global_func_exe_times++;
24 +
25 + trace_function_dummy_call();
26 +
27  spin_lock_irq(&pool->lock);
28 
29  /* am I supposed to die? */
30 @@ -2255,6 +2270,11 @@ static int worker_thread(void *__worker)
31  } while (keep_working(pool));
32 
33  worker_set_flags(worker, WORKER_PREP);
34 +
35 + trace_printk("[-] comm: %s, pid: %d, global_w_func_exe_times: %d from(%pS) \n", 
36 + current->comm, current->pid, global_func_exe_times, (void *)__builtin_return_address(0));
37 +
38 sleep:
39  /*
40   * pool->lock is held and there's no work to process and no need to

...
 
* 유튜브 강의 동영상도 있으니 같이 들으면 좋습니다.




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

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

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


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

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

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


Thanks,
Austin Kim


repository:
http://rousalome.egloos.com/10025739






핑백

덧글

  • 최재국 2020/07/23 18:14 # 삭제 답글

    뭔가 이해가 잘안되는것 같아서 질문드립니다.

    1 static int worker_thread(void *__worker)
    2 {
    3struct worker *worker = __worker;
    4struct worker_pool *pool = worker->pool;
    5
    6worker->task->flags |= PF_WQ_WORKER;
    7 woke_up:
    8spin_lock_irq(&pool->lock);
    9
    10/* am I supposed to die? */
    11if (unlikely(worker->flags & WORKER_DIE)) {
    12spin_unlock_irq(&pool->lock);
    13WARN_ON_ONCE(!list_empty(&worker->entry));
    14worker->task->flags &= ~PF_WQ_WORKER;
    15
    16set_task_comm(worker->task, "kworker/dying");
    17ida_simple_remove(&pool->worker_ida, worker->id);
    18worker_detach_from_pool(worker, pool);
    19kfree(worker);
    20return 0;
    21}
    22
    23worker_leave_idle(worker);

    A스레드가 먼저 위 코드를 실행하여 3번 라인을 실행하고 4번 라인이 실행되기전에 스케줄링되어 B 스레드가 3번라인을 실행하면

    woker 포인터 변수는 덮어 씌어지잖아요?? 그럼 결국 두 스레드가 실행될 함수는 같아지는데

    그걸 방지하기 위해서 11~23줄에서 woker포인터 변수로 워커 스레드를 종료한다는 말인가요??
  • AustinKim 2020/07/23 22:24 #

    A스레드가 먼저 위 코드를 실행하여 3번 라인을 실행하고 4번 라인이 실행되기전에 스케줄링이 된다는 것은 선점이 된다는 이야기인데요.

    3번 라인을 수행하면,

    3 struct worker *worker = __worker;

    __worker 포인터를 *worker 변수가 레지스터나 혹은 스택 공간에 저장을 할 것입니다.
    만약 3번 라인을 수행하고 4번 라인을 수행하기 직전에 선점이 되면 *worker 변수에 대응하는 레지스터 정보는 해당 프로세스의 thread_info에 저장되거나, 프로세스의 스택 공간에 푸시된 데이터는 그대로 유지되므로 데이터를 덮어 쓰는 일은 발생하지 않습니다.
  • 2020/07/23 22:24 # 답글 비공개

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