内核线程的概念:
内核线程类似于用户进程,通常用于并发处理些工作,它是一种在内核空间实现后台任务的方式,并且可以参与时间片轮转调度
内核线程可以进行繁忙的异步事件处理,也可以睡眠等待某事件的发生,内核线程可以访问内核函数和数据结构
很多设备驱动程序都是用了内核线程来完成辅助任务,例如:USB设备驱动程序中的khubd内核线程用来监控USB集线器,并在USB被热插拔的时候配置USB设备
内核线程的查看:
用户使用ps命令可以查看系统中正在运行的内核线程(也称内核进程)
内核线程的实现流程:
内核线程的创建及开启:
#include <linux/kthread.h> //内核线程接口函数头文件
struct task_struct *kernel_thread; //定义线程指针
创建内核线程(方法一),返回值为创建线程的指针
struct task_struct *kthread_create(int (*threadfn) \
(void *data),void *data,const char namefmt[], ...);
参数1:线程函数指针,线程开启后将运行此函数
参数2:函数参数data,传递给线程函数
参数3:线程名称,这个函数可以像printk一样传入某种格式的线程名
内核线程创建后不会马上运行,需要通过以下函数启动
int wake_up_process(struct task_struct *p);
创建内核线程函数并运行(方法二)
kthread_run(threadfn, data, namefmt, ...); //宏
内核线程的停止:
停止线程检测函数 (线程函数内使用)
int kthread_should_stop(void)
停止内核线程函数 (线程函数外使用)/*接收到停止信号,返回真.*/
int kthread_stop(struct task_struct *k);
1.该函数发送信号给内核线程,如果线程函数不检测信 号也不返回,那么此函数它将一直进行等待
2.在调用kthread_stop函数时,线程函数不能已经运行 结束,否则,kthread_stop函数会一直进行等待。
线程函数的运行:
线程函数,内核线程开启后会运行该函数
int threadfunc(void *data);
1.该函数由用户自己实现,函数格式如上所示
2.该函数必须能让出CPU,以便其他线程能够得到执 行,也必须能重新得到调度
实例代码:
#include <linux/module.h> /*module_init()*/
#include <linux/kernel.h> /* printk() */
#include <linux/init.h> /* __init __exit */
#include <linux/fs.h> /* file_operation */
#include <asm/uaccess.h> /* copy_to_user, copy_from_user */
#include <linux/device.h> /*class ,class_create ,device_create 等*/
#include <linux/errno.h> /* Error number */
#include <linux/delay.h> /* udelay */
#include <mach/regs-gpio.h>
#include <linux/types.h>
#include <asm/io.h>
#include <mach/gpio.h>
#include <linux/kthread.h>
#define DRIVER_NAME "s5pv210-key15"
#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
#define __debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#define GPH2CON (unsigned long)(S5PV210_GPH2_BASE+ 0x00)
#define GPH2DAT (unsigned long)(S5PV210_GPH2_BASE + 0x04)
#define GPH2UP (unsigned long)(S5PV210_GPH2_BASE + 0x08)
#define GPH2DRV (unsigned long)(S5PV210_GPH2_BASE+ 0x0C)
#define GPH3CON (unsigned long)(S5PV210_GPH3_BASE+ 0x00)
#define GPH3DAT (unsigned long)(S5PV210_GPH3_BASE + 0x04)
#define GPH3UP (unsigned long)(S5PV210_GPH3_BASE + 0x08)
#define GPH3DRV (unsigned long)(S5PV210_GPH3_BASE+ 0x0C)
static int major = 0; /* Driver Major Number */
static int minor = 0; /* Driver Minor Number */
struct class *key15_class;
static struct device *key15_device;
static wait_queue_head_t wait_queue;
static struct task_struct *key_thread; //线程指针
static unsigned char key;
/*思考:可否将上面全局变量封装为结构体,让代码
* 看起来更规范,请自行改进......
*/
static int key_conversion(int key_vlaue)
{
int key;
switch(key_vlaue){
case 0xf0: //1111 0000
key=2;
break;
case 0xe8: //1110 1000
key=3;
break;
case 0xd8: //1101 1000
key=9;
break;
case 0xb8:
key=10;
break;
case 0x78:
key=11;
break;
default:
break;
}
return key;
}
int keynum_scan(void)
{
int dat_val;
int key = -1;
dat_val = readl(GPH2DAT);
//if there has a key pressed
if((dat_val & 0xf8) != 0xf8){
msleep(100);
if((readl(GPH2DAT) & 0xf8)!=0xf8){
key = key_conversion(dat_val);
}
}
return key;
}
static int key_thread_func(void *data)
{
unsigned char *pkey = (unsigned char *)data;
int key;
for(;;){ //while循环实现线程任务
if(kthread_should_stop())
break;
key = keynum_scan();
if(key >= 0){
*pkey = key;
wake_up_interruptible(&wait_queue); //唤醒等待队列
}
schedule_timeout(1); //调度,没执行完就让出cpu一次
}
return 0;
}
static ssize_t key15_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int keynum;
int cpy_len;
int retval;
interruptible_sleep_on(&wait_queue);
keynum = key;
cpy_len = min(sizeof(keynum), count);
retval = copy_to_user(buf, &keynum, cpy_len);
return (cpy_len - retval);
}
/* Driver Operation structure */
static struct file_operations key15_fops = {
owner: THIS_MODULE,
read: key15_read,
};
static int io_port_init(void)
{
unsigned long reg_val;
//GPH3 0 1为输出功能,数据初始化为0
reg_val = readl(GPH3CON);
reg_val &= ~((0x0f<<0) | (0x0f<<4));
reg_val |= ((0x01<<0) | (0x01<<4));
writel(reg_val, GPH3CON);
reg_val = readl(GPH3DAT);
reg_val &= ~((0x01<<0) | (0x01<<1));
writel(reg_val, GPH3DAT);
//GPH2 3 4 5 6 7 为输入功能,上拉使能,驱动能力为2级
reg_val = readl(GPH2CON);
reg_val &= ~((0x0f<<12) | (0x0f<<16) | (0x0f<<20) | (0x0f<<24) | (0x0f<<28));
writel(reg_val, GPH2CON);
reg_val = readl(GPH2UP);
reg_val &= ~((0x03<<6) | (0x03<<8) | (0x03<<10) | (0x03<<12) | (0x03<<14));
reg_val |= ((0x02<<6) | (0x02<<8) | (0x02<<10) | (0x02<<12) | (0x02<<14));
writel(reg_val, GPH2UP);
reg_val = readl(GPH2DRV);
reg_val &= ~((0x03<<6) | (0x03<<8) | (0x03<<10) | (0x03<<12) | (0x03<<14));
reg_val |= ((0x02<<6) | (0x02<<8) | (0x02<<10) | (0x02<<12) | (0x02<<14));
writel(reg_val, GPH2DRV);
__debug("key15 open called!\n");
return 0;
}
/* Module Init & Exit function */
static int __init key15_module_init(void)
{
int retval = 0;
/* Module init code */
__debug("key15_init\n");
io_port_init();
init_waitqueue_head(&wait_queue); //初始化等待队列头
/*创建内核线程*/
key_thread = kthread_create(key_thread_func, &key, "key15_task");
if(IS_ERR(key_thread)){
err("create kernel thread failed\n");
retval = PTR_ERR(key_thread);
goto error;
}
/*运行内核线程*/
wake_up_process(key_thread);
/* Driver register */
major = register_chrdev(major, DRIVER_NAME, &key15_fops);
if(major < 0){
err("register char device fail!\n");
retval = major;
goto error;
}
key15_class=class_create(THIS_MODULE,DRIVER_NAME);
if(IS_ERR(key15_class)){
err("class create failed!");
retval = PTR_ERR(key15_class);
goto error_class;
}
key15_device=device_create(key15_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);
if(IS_ERR(key15_device)){
err("device create failed!");
retval = PTR_ERR(key15_device);
goto error_device;
}
__debug("register myDriver OK! Major = %d\n", major);
return 0;
error_device:
class_destroy(key15_class);
error_class:
unregister_chrdev(major, DRIVER_NAME);
error:
return retval;
}
static void __exit key15_module_exit(void)
{
/* Module exit code */
__debug("key15_exit\n");
kthread_stop(key_thread); //线程停止函数
/* Driver unregister */
unregister_chrdev(major, DRIVER_NAME);
device_destroy(key15_class,MKDEV(major, minor));
class_destroy(key15_class);
return;
}
module_init(key15_module_init);
module_exit(key15_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eric");