2014-10-07 3 views
2

두 개의 커널 스레드를 생성하고 각각 하나의 전역 변수를 증가시키는 샘플 Linux 장치 드라이버 코드를 작성했습니다. 나는 변수를 증가시키는 작업을 수행하기 위해 대기열을 사용했으며 타이머가 만료되고 각 스레드가 무작위로 깨어날 때까지 각 스레드는 대기열에서 대기합니다.대기 행렬 대기 시스템이있는 Linux 드라이버 코드

하지만이 모듈을 삽입하면 전체 시스템이 멈추고 컴퓨터를 다시 시작해야합니다. 이것은 모듈을 삽입 할 때마다 발생합니다. 실수로 dead-lock 상황에 들어가는 지 확인하기 위해 kthread 코드를 디버깅하려고 시도했지만 코드에 문제가 있는지 확인할 수 없습니다.

아무도 내가 전화 끊기 상황을 코드에서 잘못하고 있다고 말할 수 있습니까?

#include <linux/module.h> 
    #include <linux/kernel.h> 
    #include <linux/init.h> 
    #include <linux/errno.h> 
    #include <linux/semaphore.h> 
    #include <linux/wait.h> 
    #include <linux/timer.h> 
    #include <linux/sched.h> 
    #include <linux/kthread.h> 

    spinlock_t my_si_lock; 
    pid_t kthread_pid1; 
    pid_t kthread_pid2 ; 
    static DECLARE_WAIT_QUEUE_HEAD(wqueue); 
    static struct timer_list my_timer; 
    int kthread_num; 

    /* the timer callback */ 
    void my_timer_callback(unsigned long data){ 
    printk(KERN_INFO "my_timer_callback called (%ld).\n", jiffies); 
     if (waitqueue_active(&wqueue)) { 
       wake_up_interruptible(&wqueue); 
     } 
    } 

    /*Routine for the first thread */ 
    static int kthread_routine_1(void *kthread_num) 
    { 
     //int num=(int)(*(int*)kthread_num); 
     int *num=(int *)kthread_num; 
     char kthread_name[15]; 
     unsigned long flags; 
     DECLARE_WAITQUEUE(wait, current); 

     printk(KERN_INFO "Inside daemon_routine() %ld\n",current->pid); 

     allow_signal(SIGKILL); 
     allow_signal(SIGTERM); 

     do{ 
       set_current_state(TASK_INTERRUPTIBLE); 
       add_wait_queue(&wqueue, &wait); 

       spin_lock_irqsave(&my_si_lock, flags); 
       printk(KERN_INFO "kernel_daemon [%d] incrementing the shared data=%d\n",current->pid,(*num)++); 
       spin_unlock_irqrestore(&my_si_lock, flags); 

       remove_wait_queue(&wqueue, &wait); 

       if (kthread_should_stop()) { 
         break; 
       } 

     }while(!signal_pending(current)); 

     set_current_state(TASK_RUNNING); 
     return 0; 
    } 

    /*Routine for the second thread */ 
    static int kthread_routine_2(void *kthread_num) 
    { 
     //int num=(int)(*(int*)kthread_num); 
     int *num=(int *)kthread_num; 
     char kthread_name[15]; 
     unsigned long flags; 
     DECLARE_WAITQUEUE(wait, current); 

     printk(KERN_INFO "Inside daemon_routine() %ld\n",current->pid); 

     allow_signal(SIGKILL); 
     allow_signal(SIGTERM); 

     do{ 
       set_current_state(TASK_INTERRUPTIBLE); 
       add_wait_queue(&wqueue, &wait); 

       spin_lock_irqsave(&my_si_lock, flags); 
       printk(KERN_INFO "kernel_daemon [%d] incrementing the shared data=%d\n",current->pid,(*num)++); 
       spin_unlock_irqrestore(&my_si_lock, flags); 

       remove_wait_queue(&wqueue, &wait); 

       if (kthread_should_stop()) { 
         break; 
       } 

     }while(!signal_pending(current)); 

     set_current_state(TASK_RUNNING); 
     return 0; 
    } 

    static int __init signalexample_module_init(void) 
    { 
     int ret; 

     spin_lock_init(&my_si_lock); 
     init_waitqueue_head(&wqueue); 
     kthread_num=1; 

     printk(KERN_INFO "starting the first kernel thread with id "); 
     kthread_pid1 = kthread_run(kthread_routine_1,&kthread_num,"first_kthread"); 
     printk(KERN_INFO "%ld \n",(long)kthread_pid1); 
     if(kthread_pid1< 0){ 
       printk(KERN_ALERT "Kernel thread [1] creation failed\n"); 
       return -1; 
     } 

     printk(KERN_INFO "starting the second kernel thread with id"); 
     kthread_pid2 = kthread_run(kthread_routine_2,&kthread_num,"second_kthread"); 
     printk(KERN_INFO "%ld \n",(long)kthread_pid2); 
     if(kthread_pid2 < 0){ 
       printk(KERN_ALERT "Kernel thread [2] creation failed\n"); 
       return -1; 
     } 

     setup_timer(&my_timer, my_timer_callback, 0); 

     ret = mod_timer(&my_timer, jiffies + msecs_to_jiffies(2000)); 
     if (ret) { 
       printk("Error in mod_timer\n"); 
       return -EINVAL; 
     } 
     return 0; 
    } 

    static void __exit signalexample_module_exit(void) 
    { 
     del_timer(&my_timer); 
    } 

module_init(signalexample_module_init); 
module_exit(signalexample_module_exit); 
MODULE_LICENSE("GPL"); 
MODULE_DESCRIPTION("Demonstrates use of kthread"); 
+0

사람들이 커널 코드를 연구하도록하려면 최소한 커널 코딩 스타일을 사용하여 가독성을 높이십시오. – sawdust

+0

두 번째 터미널 창을 열고 모듈을로드하는 동안 해당 창에서 "tail -f "을 사용해보십시오. 시스템은 계속 잠기지 만, printk 호출이 시스템 로그에 추가되기 전에 볼 수 있습니다.또한,이게 당신의 문제는 아니지만, 귀하의 kthread_pid 변수를'struct task_struct *'데이터 유형으로 변경하십시오. kthread_run을 호출하는 것은 userspace의 fork()와 같지 않으며 [다른 데이터 유형을 반환합니다] (http://lxr.free-electrons.com/source/include/linux/kthread.h#L22). – skrrgwasme

답변

3

당신은 스레드 기능을 모두 schedule()를 호출해야합니다

/* In kernel thread function... */ 

set_current_state(TASK_INTERRUPTIBLE); 
add_wait_queue(&wqueue, &wait); 

schedule(); /* Add this call here */ 

spin_lock_irqsave(&my_si_lock, flags); 
/* etc... */ 

는 스케줄러의 해제 과정을 이동할 수 있습니다 작업 구조, '현재 프로세스에 set_current_state(TASK_INTERRUPTIBLE) 세트 상태를 호출 실행 대기열은 일단 대기 상태가된다. 그런데 스케줄러에게 "알았어, 나는 새로운 상태를 만들었다. 지금 내 스케줄을 조정해라."라고 말해야한다. 이 두 번째 단계가 누락되었으므로 변경된 플래그는 다음 번에 스케줄러가 스레드를 일시 중단하기로 결정할 때까지 적용되지 않습니다. 그러면 스레드가 얼마나 빨리 발생하는지 또는 실행될 코드의 행을 알 수있는 방법이 없습니다 그것은 일어난다 (잠긴 코드를 제외하고 - 중단되면 안된다).

시스템의 상태가 예측할 수 없기 때문에 전체 시스템을 잠그는 이유가 확실하지 않습니다. 커널 스레드는 잠금 및 루핑을 잡기 전에 타이머가 만료되기를 기다리지 않았으므로 스케줄러가 새로운 작업 구조체 상태에 실제로 조치를 취할 것으로 예상 할 수 있는지 잘 모릅니다. 그 동안에. 스레드가 반복적으로 add_wait_queue(&wqueue, &wait);remove_wait_queue(&wqueue, &wait);을 호출하므로 타이머 콜백이 발생할 때까지 대기 대기열의 상태를 알 수 있습니다.

if (waitqueue_active(&wqueue)) { 
    wake_up_interruptible(&wqueue); 
} 

그것은 당신이 if 문이 실행되는 waitqueue 활성 작업이있을 가능성은 단지 그들이 시간에 의해 비워 가지고 : 사실, 스레드가 회전하고있는 커널 때문에이 코드는 경쟁 조건을 가지고 wake_up_interruptible(&wqueue);이 호출됩니다.

그건 그렇고, 전 세계 변수를 늘리려는 현재 목표가 waitqueues 및 절전 상태를 배우는 연습 일 뿐이라고 가정합니다. 실제로 공유 카운터를 구현하려는 경우 대신 atomic operations을보고 스핀 록을 덤프 할 수 있습니다.

스핀 록을 유지하기로 결정한 경우 DEFINE_SPINLOCK() 매크로 대신 사용하도록 전환해야합니다.

또한 내 의견에서 언급했듯이 두 개의 kthread_pid 변수를 task_struct * 유형으로 변경해야합니다. 시작하는 각 스레드에 대해 __exit 루틴에서 kthread_stop(kthread_pid);을 호출해야합니다. kthread_should_stop()은 멈추라는 말을하지 않으면 절대로 true를 반환하지 않습니다.