当前位置: 首页 > news >正文

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[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的方法

使用新立得获取安装包

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、制作根文件系统

制作一个8Mramdisk根文件系统。这个步骤没有什么特别的,可以参考其它资料。

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运行界面,然后运行远程连接命令:

gdbtarget 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

相关文章:

  • unity 查看prefab层次
  • anmpp限制php文件函数可以操作的目录
  • SMTP 550错误
  • 【监控】WebServer入库与缓存更新代码优化小计
  • Ext JS 4前瞻:快速、易用和稳定
  • C# 调用C++ CLR dll类库时,实现从 string 到 sbyte* 的转换
  • BaseTDI.sys 瑞星卡巴冲突,导致机器蓝屏
  • Freemodbus介绍及测试
  • struts tech
  • html代码中显示系统时间
  • 提高工作效率的一些方法
  • ASP.NET通过Global.asax和Timer定时器 定时调用WebService 运行后台代码(转)
  • POJ3216 最小路径覆盖
  • 一个字让你从新手成为合格的程序员
  • 学习ASP.NET缓存机制
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 【技术性】Search知识
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • android图片蒙层
  • oschina
  • Ruby 2.x 源代码分析:扩展 概述
  • Vue 动态创建 component
  • 阿里云前端周刊 - 第 26 期
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 精彩代码 vue.js
  • 聚类分析——Kmeans
  • 开源SQL-on-Hadoop系统一览
  • 数据结构java版之冒泡排序及优化
  • 详解NodeJs流之一
  • 《码出高效》学习笔记与书中错误记录
  • 交换综合实验一
  • ​第20课 在Android Native开发中加入新的C++类
  • #if和#ifdef区别
  • (多级缓存)多级缓存
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (新)网络工程师考点串讲与真题详解
  • (转)一些感悟
  • (转载)hibernate缓存
  • .bat批处理(五):遍历指定目录下资源文件并更新
  • .NET 线程 Thread 进程 Process、线程池 pool、Invoke、begininvoke、异步回调
  • .NET 中各种混淆(Obfuscation)的含义、原理、实际效果和不同级别的差异(使用 SmartAssembly)
  • .net经典笔试题
  • .NET企业级应用架构设计系列之技术选型
  • .NET企业级应用架构设计系列之结尾篇
  • .考试倒计时43天!来提分啦!
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [Android Studio] 开发Java 程序
  • [android] 手机卫士黑名单功能(ListView优化)
  • [Android]通过PhoneLookup读取所有电话号码
  • [BZOJ2850]巧克力王国
  • [C++]高精度 bign (重载运算符版本)
  • [C语言]一维数组二维数组的大小
  • [Docker]十.Docker Swarm讲解