2016-10-20 2 views
2

Linux 커널 모듈을 작성하는 동안 세마포를 잠금 해제 할 때까지 기다리지 못하는 동안 kthread에 문제가 발생했습니다. 이로 인해 모듈을 언로드 할 때 스레드가 멈추지 않고 rmmod이 멈 춥니 다.리눅스 커널 - 세마포어를 기다리는 kthread를 멈추는 방법?

참고 :이 모듈은 3.10 커널에서 실행되며 새로운 버전으로 업데이트 할 수 없습니다

다음은 모듈 소스 코드에서 흥미로운 부분입니다. 이것은 단순한 프로듀서 소비자 문제를 나타내며, 목록의 크기가 제한되지 않으므로 (제작자 세마포어가 필요하지 않음) 뮤텍스가 보호됩니다. 목록에서 뭔가를 가져 오는 기능은 생산자가 올리고 소비자가 중단 한 세마포에 의해 보호됩니다. 생성자 함수는이 코드 조각에 표시되지 않은 외부 이벤트 (사실 char 장치)에서 가능한 한 작게 유지해야합니다. 이 프로세스는 모듈 언로드를 제외하고는 완벽하게 작동합니다.

얼어 붙는 부분은 코드 스 니펫에 주석으로 표시됩니다. 내가 kthread를 막을 수있는 유일한 방법은 kthread_stop을 호출하는 것인데,이 경우 실패하는 것은 분명히 수면 스레드를 깨울 수 없기 때문입니다. 스레드가 종료 될 때까지 기다리기 때문에 호출이 반환되지 않고 모듈이 언로드되지 않습니다.

모듈을 성공적으로 언로드하기 위해 세마포를 기다리는 kthread를 깨우고 중지 할 수 있습니까?

목록 구현 :

#include <linux/mutex.h> 
#include <linux/list.h> 
#include <linux/semaphore.h> 

static LIST_HEAD(list); 
DEFINE_MUTEX(list_lock); 
DEFINE_SEMAPHORE(sem_list_consumer); 

void add_to_list(struct *some_struct) { 
    int rv = mutex_lock_interruptible(&list_lock); 
    if(rv != 0) { 
     return; 
    } 

    list_add(&some_struct->list, &list); 
    mutex_unlock(&list_lock); 
    up(&sem_list_consumer); 
} 

struct some_struct * take_from_list() { 
    int rv; 
    some_struct *entry; 

    /* this is where the kthread will freeze when module is unloaded */ 
    rv = down_interruptible(&sem_list_consumer); 
    if(rv != 0) { 
     return NULL; 
    } 

    rv = mutex_lock_interruptible(&list_lock); 
    if(rv != 0) { 
     up(&sem_list_consumer); 
     return NULL; 
    } 

    if (list_empty(&list)) { 
     mutex_unlock(&list_lock); 
     return NULL; 
    } else { 
     entry = list_last_entry(&list, struct some_struct, list); 
     if (entry) { 
      list_del(&entry->list); 
     } 
    } 

    mutex_unlock(&list_lock); 
    return entry; 
} 

소비자 kthread는 구현 :

#include <linux/kthread.h> 
#include <linux/sched.h> 

int consumer_kthread(void *data) { 
    struct some_struct *entry; 

    set_current_state(TASK_INTERRUPTIBLE); 
    while (!kthread_should_stop()) { 
     /* Here the function including the semaphore is called */ 
     entry = take_from_list(); 
     if(entry != NULL) { 
      /* Do something with 'entry' here */ 
     } else { 
      /* Some handling of returned NULL pointers */ 
     } 

     set_current_state(TASK_INTERRUPTIBLE); 
    } 
    set_current_state(TASK_RUNNING); 

    return 0; 
} 

모듈 구현 :

#include <linux/init.h> 
#include <linux/kthread.h> 
#include <linux/module.h> 
#include <linux/sched.h> 

static struct task_struct *consumer_task; 

static int __init initModule(void) { 
    consumer_task = kthread_run(consumer_kthread, NULL, "list-consumer"); 

    return 0; 
} 

static void __exit exitModule(void) { 
    /* this call will cause rmmod to freeze forever */ 
    kthread_stop(consumer_task); 
} 

module_init(initModule); 
module_exit(exitModule); 

MODULE_LICENSE("GPL v2"); 
MODULE_DESCRIPTION("My Module"); 
+0

'take_from_list' 함수에는'NULL '을 반환하는 곳이 세 곳 있습니다. 이 세 곳 중 하나에서'sem_list_consumer' 세마포어를 가지고있는 동안 리턴합니다. 나는이 불일치가 당신의 문제와 관련이 있다고 생각합니다. 그렇지 않으면 호출자가 세마포어를 해제해야하는지 여부를 어떻게 알 수 있습니까? –

+0

나중에 문제가 발생할 수 있지만 프로듀서 코드를 호출하지 않고도 모듈을로드 및 언로드하는 경우에도 문제가 발생합니다. 또한 문제는 뭔가가 kthread를 중단 시키거나 깨우는 경우에만 발생합니다. 그러나 이것이 내가 달성하지 못한 것입니다. – marandus

+0

이미 생산자 코드가 문제가 아니므로 오류가 소비자 코드에 있습니다. 누락 된 코드 때문에 말할 수 없지만,'take_from_list' 함수에서'mutex_unlock (& ​​list_lock);과'return NULL; '줄 사이에'up (& sem_list_consumer);를 호출하면됩니다. 'take_from_list'가'NULL'을 반환 할 때에도'sem_list_consumer' 세마포어가 유지되기를 기대하지 않는다면,'take_from_list'를 다루어야 할 필요가 있습니다. 세마포어를 가지지 않고'NULL'을 반환하는 경우도 있습니다. –

답변

2

누락 된 코드는이 대답이 교양있는 추측 만 사용할 수 있음을 의미합니다. 여기

는 누락 된 코드에 대한 내 가정은 다음과 같습니다

  1. take_from_list가 유효한 항목을 반환하는 경우, consumer_kthread는 항목과 함께 무언가를하고 take_from_list에서 down_interruptible(&sem_list_consumer)에 대한 호출을 일치하도록 up(&sem_list_consumer)를 호출합니다.

  2. NULL 반환 take_from_list 경우, consumer_kthreadNULL 포인터의 일부 처리를 수행하고, sem_list_consumer 세마포어가 원래 상태에 있다고 가정합니다. 가끔 처음 up(&sem_list_consumer)를 호출하지 않고 NULL을 반환하기 때문에

그 가정을 감안할 때, take_from_list에 버그가있다. 즉, 후속 호출 인 take_from_list은 신호에 의해 중단 될 때까지 down_interruptible(&sem_list_consumer)에 대한 호출을 차단합니다.추가 consumer_kthread의 누락 된 코드에서 어떤 장소가있는 경우

struct some_struct * take_from_list() { 
    int rv; 
    some_struct *entry; 

    rv = down_interruptible(&sem_list_consumer); 
    if(rv != 0) { 
     return NULL; 
    } 

    rv = mutex_lock_interruptible(&list_lock); 
    if(rv != 0) { 
     up(&sem_list_consumer); 
     return NULL; 
    } 

    if (list_empty(&list)) { 
     mutex_unlock(&list_lock); 
     up(&sem_list_consumer); /* <-- this line was missing */ 
     return NULL; 
    } else { 
     entry = list_last_entry(&list, struct some_struct, list); 
     if (entry) { 
      list_del(&entry->list); 
     } 
    } 

    mutex_unlock(&list_lock); 
    return entry; 
} 

개정

: 그 버그를 해결하려면, 항상 NULL를 리턴 할 때마다 그것을 왼쪽 상태에서 세마포어를 떠나 take_from_list 변경 자체를 대기 대기열에 연결하고 잠자기 상태가되면 kthread_should_stop()으로의 호출이 깨우기 ​​상태에 포함되어야합니다. 웨이크 업 조건은 다른 조건 (||) kthread_should_stop()에 의해 충족되어야합니다.

exitModule 함수에서 kthread_stop(condition_thread)을 호출하면 소비자 스레드가 깨어납니다. 이벤트를 기다리는 중이라면 가장 먼저 할 일은 깨우기 상태를 확인하고 만족스럽지 않으면 다시 잠자기 상태로 되돌아가는 것입니다. 가능한 깨우기 조건 중 하나로 kthread_should_stop()을 포함 시키면 소비자 스레드가 즉시 다시 잠자기 상태가되지 않도록합니다.

+0

맞습니다. 누락 된 선이 문제를 일으킬 수 있습니다. 그러나 근본 원인이 아닙니다. 전체 설계는 목록이 비어있는 동안 kthread가 잠자기 상태가되고 목록에 무언가가있는 경우 생산자가 깨우침을 받았다는 아이디어에 기반합니다 (이것이 내가 생산자 - 소비자 문제를 명시 적으로 언급 한 이유입니다). 따라서 목록이 비어있는 경우 스레드는 누락 된 행을 추가 한 지점에 절대로 도달하지 않습니다. 커널 내부의 kthread에 신호를 보내는 방법을 보여 주시겠습니까? 내가 방해 신호를 놓치고 그것을 보내는 방법을 모르기 때문에 그것은 내 문제를 해결할 것 같아요. – marandus

+0

당신의'exitModule' 함수는 생성자 쓰레드가 깨어나는 것과 같은 방식으로 컨슈머 쓰레드를 깨울 수 있어야합니다. 소비자 스레드에 신호를 보내려면 exitModule이 필요하지 않지만 소비자 스레드를 깨우기 전에 플래그를 설정해야하며 소비자 스레드는 해당 플래그가 설정 될 때 적절한 조치를 취해야합니다. –

+0

별도의 플래그를 사용하는 대신 wait 이벤트 깨우기 조건의 일부로'kthread_should_stop()'에 대한 호출을 포함시킬 수 있습니다. 실제로 그렇게하는 것이 가장 쉬운 방법 일 것입니다. 그에 따라 내 대답을 수정합니다. 신호를 잊어 버리십시오 - 이것은 문제에 대한 무뚝뚝한 해머 방식입니다. –

1

당신은 대기 프로세스를 보낼 필요 신호. 그런 다음 프로세스는 TASK_INTERRUPTABLE에서 TASK_RUNNING으로 변경되고 스케줄 된 후 EINTR을 리턴하는 down_interruptable로 실행됩니다.

+0

답변 해 주셔서 감사합니다. kthread에 신호를 보내는 방법을 간단한 예를 들어 주시겠습니까? 어떻게 작동하는지 알기 위해 커널 소스를 파헤 치려고했지만 정확한 방법과 siginfo 구조체와 같은 것이 무엇인지, 그리고 무엇을 써야하는지 잘 모르겠습니다. – marandus