linux内核线程的创建及在QEMU上的测试方法
来源:互联网 发布:java的开发效率 编辑:程序博客网 时间:2024/05/18 03:51
作者:刘洪涛,华清远见嵌入式学院讲师。
本文主要介绍一个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]='/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.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 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的方法
使用新立得获取安装包
安装完毕后,测试一下可以支持的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
如果您喜欢这篇文章,可以加华清远见老师为好友,单击以下链接即可:
(作者:华清远见嵌入式培训中心 www.embedu.org www.farsight.com.cn )
- linux内核线程的创建及在QEMU上的测试方法
- linux内核线程的创建及在QEMU上的测试方法
- linux内核线程的创建及在QEMU上的测试方法
- qemu在Linux系统上的使用
- linux内核线程的创建
- 测试qemu上的beagleboard
- 在Java中,关于线程的创建,方法及生命周期
- Linux 内核线程 的 创建 和 终止
- linux内核线程的创建与销毁
- linux内核线程的创建与销毁
- linux内核线程的创建与销毁
- 在linux上获得线程id的方法
- 在linux上获得线程id的方法
- 在linux上获得线程id的方法
- Linux最小根文件系统的建立,内核模块的编译,Qemu模拟测试最小系统
- Gentoo在qemu上的安装笔记
- Microwindows 在 MINI2440 QEMU上的移植
- 在qemu的beagleboard上运行android
- 怎样成为一名快速的学习者
- 【C语言】——float *g() 与 float (*h)()的区别
- 老鸟对菜鸟的一些建议
- 同一行的文本框和文字不能轴对称解决办法
- 矿山电工
- linux内核线程的创建及在QEMU上的测试方法
- 20101208
- 进入大本营
- 哈哈
- 计算机管理打不开,出现提示“该文件没有与之关联的程序来执行操作”
- 全球自由职业技能单日排行榜:PHP居首位
- 继续上班?还是去培训?
- 关于mysql_affected_rows()
- CTreeCtrl控件的技巧