歡迎您光臨本站 註冊首頁

linux內核線程的創建及在QEMU上的測試方法

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

作者:劉洪濤,華清遠見嵌入式學院講師.

本文主要介紹一個linux內核線程的實例,以及在QEMU平台上測試的過程.

一、內核線程的創建

編寫一個字元設備驅動,在驅動註冊時,開啟一個內核線程.在用戶向設備寫入數據時,字元設備的wirte方法能夠激活此內核線程,並在線程中實現列印用戶輸入的數據.

驅動代碼如下(在2.6.22內核上測試通過),關鍵部分加上了註釋:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h> /* printk(), min() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <linux/spinlock.h>
static int kthread_major = 0;
module_param(kthread_major, int, 0);
MODULE_AUTHOR("farsight");
MODULE_LICENSE("Dual BSD/GPL");
struct kthread_dev {
struct task_struct *thread;
struct cdev cdev;
char* name;
int data_size;
char data[100];
spinlock_t queue_lock;

};
int kthread_open(struct inode *inode,struct file *filp)


{
return 0;
}
ssize_t kthread_read(struct file *file, char __user *buff, size_t count, loff_t *offp)

{

return 0;

}
ssize_t kthread_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)
{
int ret;
ret=sizeof(kthread_dev_obj->data);
if(count>(ret-1))
count=ret-1;
if(copy_from_user(kthread_dev_obj->data,buff,count)<0)//獲取用戶數據
{
goto out1;
}
spin_lock(&kthread_dev_obj->queue_lock);
kthread_dev_obj->data_size=count;
spin_unlock(&kthread_dev_obj->queue_lock);
wake_up_process(kthread_dev_obj->thread);//喚醒內核線程
return count;
out1:
return 0;
}

static int kthread_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
return 0;
}

static int kthread_release(struct inode *node, struct file *file)
{
return 0;
}

/*
* Set up the cdev structure for a device.
*/
static void kthread_setup_cdev(struct cdev *dev, int minor,struct file_operations *fops)
{
int err, devno = MKDEV(kthread_major, minor);

cdev_init(dev, fops);
dev->owner = THIS_MODULE;
err = cdev_add (dev, devno, 1);
/* Fail gracefully if need be */


if (err)
printk (KERN_NOTICE "Error %d adding kthread%d", err, minor);
}

static struct file_operations kthread_remap_ops = {
.owner = THIS_MODULE,
.open = kthread_open,
.release = kthread_release,
.read = kthread_read,
.write = kthread_write,
.ioctl = kthread_ioctl,
};

static int kthread_fun(void * arg) //內核線程運行函數
{
while (!kthread_should_stop()) {
spin_lock(&kthread_dev_obj->queue_lock);
if(kthread_dev_obj->data_size){
spin_unlock(&kthread_dev_obj->queue_lock);
kthread_dev_obj->data[kthread_dev_obj->data_size]='';
printk(kthread_dev_obj->data);//列印出用戶空間數據
printk("in kthreadn");
kthread_dev_obj->data_size=0;
}
else{
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock(&kthread_dev_obj->queue_lock);
schedule();
}
}
return 0;
}

static int kthread_init(void)
{
int result;
dev_t dev = MKDEV(kthread_major, 0);

/* Figure out our device number. */
if (kthread_major)
result = register_chrdev_region(dev, 1, "kthread");


else {
result = alloc_chrdev_region(&dev, 0, 1, "kthread");
kthread_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "kthread: unable to get major %dn", kthread_major);
return result;
}
if (kthread_major == 0)
kthread_major = result;

kthread_dev_obj= kmalloc(sizeof(struct kthread_dev), GFP_KERNEL);
kthread_setup_cdev(&kthread_dev_obj->cdev, 0,&kthread_remap_ops);
printk("kthread device installed, with major %dn", kthread_major);
my_class= class_create(THIS_MODULE, "kthread");
// device_create(my_class, NULL, MKDEV(kthread_major, 0),NULL, "kthread");
device_create(my_class, NULL, MKDEV(kthread_major, 0), "kthread");//for
// 2.6.22
kthread_dev_obj->name="kthreadtest";//內核線程的名稱
spin_lock_init(&kthread_dev_obj->queue_lock);
kthread_dev_obj->thread=kthread_run(kthread_fun,kthread_dev_obj,"%sd",kthread_dev_obj->name);//創建並運行內核線程
return 0;
}
static void kthread_cleanup(void)
{
kthread_stop(kthread_dev_obj->thread);//停止內核線程
cdev_del(&kthread_dev_obj->cdev);
unregister_chrdev_region(MKDEV(kthread_major, 0), 1);
device_destroy(my_class,MKDEV(kthread_major,0));
class_destroy(my_class);
kfree(kthread_dev_obj);


printk("kthread device uninstalledn");
}
module_init(kthread_init);
module_exit(kthread_cleanup);

二、在QEMU平台上的測試方法

QEMU可以模擬很多硬體平台,使用QEMU適用於你手邊沒用硬體平台,或沒用很好的內核調試工具的情況.

這裡主要介紹使用QEMU模擬ARM開發環境,並運行linux系統的過程.

1、系統環境

操作系統平台:Ubuntu 10.10
交叉工具:arm-softfloat-linux-gnu
測試內核:Linux2.6.22
測試平台:RealView-EB (QEMU 模擬)

2、安裝QEMU的方法

使用新立得獲取安裝包

安裝完畢后,測試一下可以支持的ARM平台

$ qemu-system-arm -M ?
Supported machines are:
syborg Syborg (Symbian Virtual Platform)
musicpal Marvell 88w8618 / MusicPal (ARM926EJ-S)
mainstone Mainstone II (PXA27x)
n800 Nokia N800 tablet aka. RX-34 (OMAP2420)
n810 Nokia N810 tablet aka. RX-44 (OMAP2420)
cheetah Palm Tungsten|E aka. Cheetah PDA (OMAP310)
sx1 Siemens SX1 (OMAP310) V2
sx1-v1 Siemens SX1 (OMAP310) V1
tosa Tosa PDA (PXA255)
akita Akita PDA (PXA270)
spitz Spitz PDA (PXA270)
borzoi Borzoi PDA (PXA270)
terrier Terrier PDA (PXA270)
connex Gumstix Connex (PXA255)
verdex Gumstix Verdex (PXA270)


lm3s811evb Stellaris LM3S811EVB
lm3s6965evb Stellaris LM3S6965EVB
realview-eb ARM RealView Emulation Baseboard (ARM926EJ-S)
realview-eb-mpcore ARM RealView Emulation Baseboard (ARM11MPCore)
realview-pb-a8 ARM RealView Platform Baseboard for Cortex-A8
realview-pbx-a9 ARM RealView Platform Baseboard Explore for Cortex-A9
versatilepb ARM Versatile/PB (ARM926EJ-S)
versatileab ARM Versatile/AB (ARM926EJ-S)
integratorcp ARM Integrator/CP (ARM926EJ-S) (default)

3、製作內核鏡像

(1)配置好交叉開發工具

(2)配置內核
#cp arch/arm/configs/realview_defconfig .config
#make menuconfig
配置支持initial ramdisk

配置支持Ramdisk塊設備:
Device Drivers ->Block devices->RAM disk support
其中:Default RAM disk size (kbytes)必須要改成和你的RAMDISK鏡像一樣的大小.

配置內核添加調試選項:
Kernel hacking ->Compile the kernel with debug info

設置內核支持ext2文件系統

保存退出.

(3) 將上述的內核線程驅動加入到內核中,然後編譯內核.
#make zImage

4、製作根文件系統

製作一個8M的ramdisk根文件系統.這個步驟沒有什麼特別的,可以參考其它資料.

5、安裝gdb

(1)下載GDB源碼:

http://ftp.gnu.org/gnu/gdb/gdb-7.2.tar.bz2

(2)交叉編譯GDB

#./configure --target=arm-softfloat-linux-gnu –prefix=/home/lht/QEMU/arm-gdb
#make && make install

6、調試內核

#qemu-system-arm -M realview-eb -kernel ./zImage -initrd ./initrd.img -nographic -append "console=ttyAMA0" -m 64 -s -S

系統會暫停,等待遠端gdb連接調試.在另一終端下運行:

#ddd -debugger /home/lht/QEMU/arm-gdb/bin/arm-softfloat-linux-gnu-gdb ~/disk2/s3c2410/linux-2.6.22.6-qemu/vmlinux

此時會出現ddd運行界面,然後運行遠程連接命令:

(gdb)target remote localhost:1234

此時就可以運行gdb命令調試內核了.

如:在kthread_fun函數中設置一個斷點的方法
連接后,搜索欄中輸入kthead_fun,出現如下圖的顯示:

在view菜單中打開彙編窗口,然後在彙編窗口中設置斷點(比在c中準確).

Gdb命令行輸入c
(gdb) c
啟動目標系統
系統會在斷點處停止,接下就可以用ddd提供圖形調試工具調試代碼了.
系統正常啟動后,測試結果如下:
[root@farsight /]# echo 123456 > /dev/kthread
123456
in kthread
[root@farsight /]# ps w
PID USER VSZ STAT COMMAND
1 0 0 SW [swapper]
2 0 0 SW< [kthreadd]
3 0 0 SWN [ksoftirqd/0]
4 0 0 SW< [watchdog/0]
5 0 0 SW< [events/0]
6 0 0 SW< [khelper]


40 0 0 SW< [kblockd/0]
41 0 0 SW< [kseriod]
53 0 0 SW [pdflush]
54 0 0 SW [pdflush]
55 0 0 SW< [kswapd0]
56 0 0 SW< [aio/0]
131 0 0 SW< [kthreadtestd]
188 0 0 SW< [mtdblockd]
196 0 0 SW< [kpsmoused]
202 0 1996 S init
206 0 1492 S < /bin/udevd --daemon
208 0 2000 S -/bin/sh
209 0 2000 R ps w


[火星人 ] linux內核線程的創建及在QEMU上的測試方法已經有648次圍觀

http://coctec.com/docs/linux/show-post-49819.html