Linux內核多線程(四)


自己創建的內核線程,當把模塊加載到內核之后,可以通過:ps –ef 命令來查看線程運行的情況。通過該命令可以看到該線程的pid和ppid等。也可以通過使用kill –s 9 pid 來殺死對應pid的線程。如果要支持kill命令自己創建的線程里面需要能接受kill信號。這里我們就來舉一個例,支持kill命令,同時rmmod的時候也能殺死線程。

 

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/param.h>

#include <linux/jiffies.h>

#include <asm/system.h>

#include <asm/processor.h>

#include <asm/signal.h>

#include <linux/completion.h>       // for DECLARE_COMPLETION()

#include <linux/sched.h>            

#include <linux/delay.h>            // mdelay()

#include <linux/kthread.h> 

 

MODULE_LICENSE("GPL");

 
static DECLARE_COMPLETION(my_completion);

 
static struct task_struct *task;

 
int flag = 0;

 
int my_fuction(void *arg)
{

    printk(" in %s()\n", __FUNCTION__);

    allow_signal(SIGKILL); //使得線程可以接收SIGKILL信號

    mdelay(2000);

    printk(" my_function complete()\n");

    printk("should stop: %d\n",kthread_should_stop());

    while (!signal_pending(current) && !kthread_should_stop()) {//使得線程可以可以被殺死,也可以再rmmod的時候結束

        printk(" jiffies is %lu\n", jiffies);

        set_current_state(TASK_INTERRUPTIBLE);

        schedule_timeout(HZ * 5);   

         printk("should stop: %d\n",kthread_should_stop());

    }

    printk("Leaving my_function\n");

    flag = 1; //flag很關鍵!

    return 0;

}

 

static int __init init(void)
{

    task = kthread_run(my_fuction,NULL,"my_function");

    printk("<1> init wait_for_completion()\n");

    return 0;

}

 

static void __exit finish(void)
{        

        int ret;

        if(!flag)
        {

                 if (!IS_ERR(task))
                 {  

                      int ret = kthread_stop(task);  

                      printk(KERN_INFO "First thread function has stopped ,return %d\n", ret);  

                 }                  

       }

    

    printk("task_struct: 0x%x",task);

    printk(" Goodbye\n");

}

 

module_init(init);

module_exit(finish);

運行結果(執行kill之后):

運行結果(rmmod之后):

說明:程序運行后線程循環執行每隔5個內核 ticks 就答應一次當前的jiffies值。可以通過kthread_stop()來結束,也可以通過kill命令來結束。

程序中使用了flag 變量來控制是否使用 kthread_stop()函數有兩個原因:首先,當線程創建成功之后IS_ERR()不能檢測出線程是否還在運行,因為此時task是一個正常的地址而不是錯誤碼(后面會說明IS_ERR的原理);其次,線程不能被殺次兩次,如果使用kill命令之后線程已經被殺死,但是在此使用kthread_stop()函數就會出嚴重問題,因為此時線程已經被殺死,task指向的地址已經無效,struct kthread 也已經不存在,操作此時使用kthread_stop()設置should_stop是沒有意義的。同樣可以得出結論,當線程結束之后使用kthread_should_stop()來查看線程運行狀態也會造成內核異常。

 

IS_ERR()函數的原理:

#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)

static inline long IS_ERR(const void *ptr)
{
  return IS_ERR_VALUE((unsigned long)ptr);
}

內核中的函數常常返回指針,問題是如果出錯,也希望能夠通過返回的指針體現出來。
所幸的是,內核返回的指針一般是指向頁面的邊界(4K邊界),即

ptr & 0xfff == 0

這樣ptr的值不可能落在(0xfffff000,0xffffffff)之間,而一般內核的出錯代碼也是一個小負數,在-1000到0之間,轉變成unsigned long,正好在(0xfffff000,0xffffffff)之間。因此可以用

(unsigned long)ptr > (unsigned long)-1000L

也就等效於(x) >= (unsigned long)-MAX_ERRNO
其中MAX_ERRNO 為4095

來判斷內核函數的返回值是一個有效的指針,還是一個出錯代碼。

涉及到的任何一個指針,必然有三種情況,一種是有效指針,一種是NULL,空指針,一種是錯誤指針,或者說無效指針.而所謂的錯誤指針就是指其已經到達了 最后一個page.比如對於32bit的系統來說,內核空間最高地址0xffffffff,那么最后一個page就是指的 0xfffff000~0xffffffff(假設4k一個page).這段地址是被保留的,如果超過這個地址,則肯定是錯誤的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM