mmdev 发表于 2013-2-1 12:03:13

linux内核线程的创建及在QEMU上的测试方法

作者:华清远见讲师 刘洪涛
本文主要介绍一个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;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='\0';printk(kthread_dev_obj->data);//打印出用户空间数据printk("in kthread\n");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 %d\n", 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 %d\n", 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.22kthread_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 uninstalled\n");}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的方法

使用新立得获取安装包
http://hi.csdn.net/attachment/201011/22/0_12904168555C60.gif



3、制作内核镜像

(1)配置好交叉开发工具
(2)配置内核
#cp arch/arm/configs/realview_defconfig .config
#make menuconfig
配置支持initial ramdisk
http://hi.csdn.net/attachment/201011/22/0_1290417070183W.gif
配置支持Ramdisk块设备:
Device Drivers ->Block devices->RAM disk support
其中:Default RAM disk size (kbytes)必须要改成和你的RAMDISK镜像一样的大小。
http://hi.csdn.net/attachment/201011/22/0_1290417322m1Nm.gif

配置内核添加调试选项:
Kernel hacking ->Compile the kernel with debug info
http://hi.csdn.net/attachment/201011/22/0_1290417436Jkzs.gif


设置内核支持ext2文件系统

http://hi.csdn.net/attachment/201011/22/0_1290417565350z.gif

保存退出。

(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,出现如下图的显示.
http://hi.csdn.net/attachment/201011/22/0_1290417678L2g2.gif
在view菜单中打开汇编窗口,然后在汇编窗口中设置断点(比在c中准确)。
http://hi.csdn.net/attachment/201011/22/0_1290417762PWpP.gif
Gdb命令行输入c
(gdb) c
启动目标系统
系统会在断点处停止,接下就可以用ddd提供图形调试工具调试代码了。
系统正常启动后,测试结果如下:
# echo 123456 > /dev/kthread
123456
in kthread
# ps w
PID USER VSZ STAT COMMAND
1 0 0 SW
2 0 0 SW<
3 0 0 SWN
4 0 0 SW<
5 0 0 SW<
6 0 0 SW<
40 0 0 SW<
41 0 0 SW<
53 0 0 SW
54 0 0 SW
55 0 0 SW<
56 0 0 SW<
131 0 0 SW<
188 0 0 SW<
196 0 0 SW<
202 0 1996 S init
206 0 1492 S < /bin/udevd --daemon
208 0 2000 S -/bin/sh
209 0 2000 R ps w

页: [1]
查看完整版本: linux内核线程的创建及在QEMU上的测试方法