目录
kthread_worker
驱动传输数据的方式
同步传输
异步传输
头文件
kthread_worker结构体
kthread_work结构体
kthread_flush_work结构体
init_kthread_worker()函数
为kthread_worker创建内核线程
init_kthread_work()函数
启动工作
刷新工作队列
停止内核线程
实验环节
dts_led.c文件
Makefile文件
执行过程
函数解析
init_kthread_worker()宏
kthread_worker_fn()函数
init_kthread_work()函数
kthread_queue_work()函数
kthread_flush_worker()函数
kthread_worker
可以通过kthread_worker结构体让内核创建一个线程,在线程里完成一件事情。
驱动传输数据的方式
同步传输
低速数据:驱动同步传输
优点:简单直接
缺点:传输效率低,同步传输会造成当前线程阻塞,影响用户空间应用程序执行效率
异步传输
高速数据:驱动交给内核来异步传输
优点:无阻塞
缺点:机制复杂、内核里的相关代码很多
头文件
#define <linux/kthread.h>
kthread_worker结构体
该结构体存放在内核/include/linux/kthread.h文件。
把内核线程抽象为流水线工人,按序处理其他线程 / 进程交付的批量工作。内核里的流水线工人可能有多个。
struct kthread_worker {
unsigned int flags;
spinlock_t lock; // 自旋锁
struct list_head work_list; // 不需要延时完成的工作串联,即串联kthread_work结构体
struct list_head delayed_work_list; // 需要延时完成的工作串联
struct task_struct *task; // 表示一个具体的进程或者线程
struct kthread_work *current_work; // 指向正在处理的某一个具体的工作
};
kthread_work结构体
该结构体存放在内核/include/linux/kthread.h文件。
表示等待内核线程处理的具体工作。
struct kthread_work {
struct list_head node; // 此链表节点将串联在kthread_worker->work_list
kthread_work_func_t func; // 函数指针,为每一个具体工作指定一个具体的函数,真正负责完成具体的工作,自定义
struct kthread_worker *worker; // 这个具体工作属于哪个工人,内核里面可能有多个工人,指向上面node成员指向的kthread_worker
/* Number of canceling calls that are running at the moment. */
int canceling;
};
typedef void (*kthread_work_func_t)(struct kthread_work *work);
kthread_flush_work结构体
该结构体存放在内核/kernel/kthread.c文件。
表示等待某个内核进程工人处理完所有工作。
struct kthread_flush_work {
struct kthread_work work; //具体内核线程工人
struct completion done; //完成量,等待所有工作处理完毕
};
init_kthread_worker()函数
先定义,再初始化。
struct kthread_worker hi_worker; // 定义一个工人
init_kthread_worker(&hi_worker); // 初始化一个工人
为kthread_worker创建内核线程
先定义,再初始化。
#define <linux/sched.h>
/*
* kthread_worker_fn:内核线程一直运行的函数,具体的工作就是在这个函数指针里完成。这是内核提供给我们的
* hi_worker:已初始化的kthread_worker结构体变量
* “nvme%d”:为内核线程设置名字
*/
struct task_struct *kworker_task;
kworker_task = kthread_run(kthread_worker_fn, &hi_worker, "nvme%d", 1); // kworker_task 要被hi_worker中的task成员所指向
init_kthread_work()函数
先定义,再初始化。
// xxx_work_fn:处理该工作的具体函数,自定义实现,负责完成具体的工作
struct kthread_work hi_work; // 定义一个具体的工作变量
init_kthread_work(&hi_work, xxx_work_fn); // 为此工作变量指定一个具体的工作函数
启动工作
交付工作给内核线程工人,注意工人手里可能有多个具体工作。
/*
* hi_worker:具体内核线程工人,struct kthread_worker
* hi_work:具体工作,struct kthread_work
*/
kthread_queue_work(&hi_worker, &hi_work);
刷新工作队列
刷新指定kthread_worker上所有work
// 等待此流水线工个人手里的工作完成
// hi_worker:具体内核线程工人
kthread_flush_worker(&hi_worker);
停止内核线程
停止创建的内核线程。
// struct task_struct 指针
kthread_stop(kworker_task);
实验环节
dts_led.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#define DEV_NAME "rgb_led"
#define DEV_CNT (1)
int rgb_led_red;
int rgb_led_green;
int rgb_led_blue;
static dev_t led_devno;
static struct cdev led_chrdev;
struct class *class_led;
struct device *device;
struct device_node *rgb_led_device_node;
struct kthread_worker hi_worker;
struct kthread_work hi_work;
struct task_struct *kworker_task;
unsigned int write_data = 0;
static int led_chrdev_open(struct inode *inode, struct file *filp)
{
printk("open form driver\n");
kworker_task = kthread_run(kthread_worker_fn, &hi_worker, "nvme%d", 1);
return 0;
}
void rgb_control(struct kthread_work *work)
{
/* 设置GPIO1_04输出点平 */
if(write_data & 0x04){
gpio_set_value(rgb_led_red, 0);
}else{
gpio_set_value(rgb_led_red, 1);
}
/* 设置GPIO4_20输出点平 */
if(write_data & 0x02){
gpio_set_value(rgb_led_green, 0);
}else{
gpio_set_value(rgb_led_green, 1);
}
/* 设置GPIO4_19输出点平 */
if(write_data & 0x01){
gpio_set_value(rgb_led_blue, 0);
}else{
gpio_set_value(rgb_led_blue, 1);
}
}
static ssize_t led_chrdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret, error;
unsigned char receive_data[10]; //用于保存接收到的数据
if(cnt > 10) cnt = 10;
error = copy_from_user(receive_data, buf, cnt);
if(error < 0) return -1;
ret = kstrtoint(receive_data, 16, &write_data);
if(ret) return -1;
kthread_init_work(&hi_work, rgb_control);
kthread_queue_work(&hi_worker, &hi_work);
return cnt;
}
static int led_chrdev_release(struct inode *inode, struct file *filp)
{
printk(KERN_ALERT "finished!!!\n");
return 0;
}
static struct file_operations led_chrdev_fops = {
.owner = THIS_MODULE,
.open = led_chrdev_open,
.write = led_chrdev_write,
.release = led_chrdev_release,
};
static int led_probe(struct platform_device *pdv)
{
int ret = -1; //保存错误状态码
printk(KERN_ALERT "match successed!\n");
/* 获取rgb_led的设备树节点 */
rgb_led_device_node = of_find_node_by_path("/rgb_led");
if(rgb_led_device_node == NULL){
printk(KERN_ERR "get rgb_led failed!\n");
return -1;
}
/* 获取red led GPIO 引脚号 */
rgb_led_red = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
if(rgb_led_red < 0){
printk(KERN_ERR "rgb_led_red failed!\n");
return -1;
}
/* 获取green led GPIO 引脚号 */
rgb_led_green = of_get_named_gpio(rgb_led_device_node, "rgb_led_green", 0);
if(rgb_led_green < 0){
printk(KERN_ERR "rgb_led_green failed!\n");
return -1;
}
/* 获取blue led GPIO 引脚号 */
rgb_led_blue = of_get_named_gpio(rgb_led_device_node, "rgb_led_blue", 0);
if(rgb_led_blue < 0){
printk(KERN_ERR "rgb_led_blue failed!\n");
return -1;
}
/* 设置GPIO为输出模式,并默认高电平 */
gpio_direction_output(rgb_led_red, 1);
gpio_direction_output(rgb_led_green, 1);
gpio_direction_output(rgb_led_blue, 1);
/* 第一步
* 采用动态分配的方式获取设备编号,次设备号为0
* 设备名称为rgb-leds,可通过命令cat /proc/devices查看
* DEV_CNT为1,当前只申请一个设备编号
*/
ret = alloc_chrdev_region(&led_devno, 0, DEV_CNT, DEV_NAME);
if(ret < 0){
printk("fail to alloc led_devno\n");
goto alloc_err;
}
/* 第二步
* 关联字符设备结构体cdev与文件操作结构体file_operations
*/
led_chrdev.owner = THIS_MODULE;
cdev_init(&led_chrdev, &led_chrdev_fops);
/* 第三步
* 添加设备到cdev_map哈希表中
*/
ret = cdev_add(&led_chrdev, led_devno, DEV_CNT);
if(ret < 0){
printk("fail to add cdev\n");
goto add_err;
}
/* 第四步:创建类 */
class_led = class_create(THIS_MODULE, DEV_NAME);
/* 第五步:创建设备 */
device = device_create(class_led, NULL, led_devno, NULL, DEV_NAME);
return 0;
alloc_err:
return -1;
add_err:
//添加设备失败时,需要注销设备号
unregister_chrdev_region(led_devno, DEV_CNT);
printk("error!\n");
}
static const struct of_device_id rgb_led[] = {
{.compatible = "fire,rgb_led"},
{/* sentinel */}
};
/* 定义平台设备结构体 */
struct platform_driver led_platform_driver = {
.probe = led_probe,
.driver = {
.name = "rgb-leds-platform",
.owner = THIS_MODULE,
.of_match_table = rgb_led,
}
};
static int __init led_platform_driver_init(void)
{
int DriverState;
kthread_init_worker(&hi_worker);
DriverState = platform_driver_register(&led_platform_driver);
printk(KERN_ALERT "DriverState is %d\n", DriverState);
return 0;
}
static void __exit led_platform_driver_exit(void){
kthread_flush_worker(&hi_worker);
kthread_stop(kworker_task);
/* 销毁设备 */
device_destroy(class_led, led_devno);
/* 删除设备号 */
cdev_del(&led_chrdev);
/* 取消注册字符设备 */
unregister_chrdev_region(led_devno, DEV_CNT);
/* 销毁类 */
class_destroy(class_led);
platform_driver_unregister(&led_platform_driver);
printk(KERN_ALERT "led_platform_driver exit\n");
}
module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("couvrir");
MODULE_DESCRIPTION("led module");
MODULE_ALIAS("led module");
Makefile文件
照旧
执行过程
虚拟机:
执行make和make copy。生成.ko文件。
开发板(在挂载目录下执行):
sudo insmod dts_led.ko
sudo sh -c "echo 1 > /dev/rgb_led"
sudo sh -c "echo 2 > /dev/rgb_led"
sudo sh -c "echo 4 > /dev/rgb_led"
sudo rmmod dts_led.ko
函数解析
init_kthread_worker()宏
初始化kthread_worker结构体。
#define init_kthread_worker(worker) \
do { \
static struct lock_class_key __key; \
__init_kthread_worker((worker), "("#worker")->lock", &__key); \
} while (0)
void __init_kthread_worker(struct kthread_worker *worker, const char *name, struct lock_class_key *key)
{
memset(worker, 0, sizeof(struct kthread_worker));
spin_lock_init(&worker->lock); // 初始化自旋锁
lockdep_set_class_and_name(&worker->lock, key, name);
INIT_LIST_HEAD(&worker->work_list); // 初始化链表结点
INIT_LIST_HEAD(&worker->delayed_work_list); // 初始化链表结点
}
kthread_worker_fn()函数
int kthread_worker_fn(void *worker_ptr)
{
// 指针类型转化
struct kthread_worker *worker = worker_ptr;
struct kthread_work *work;
...
// current是内核的一个全局变量,专门用来表示当前运行的进程或者线程
// 当某个线程运行这个函数时,线程会被保存到task指针
worker->task = current;
if (worker->flags & KTW_FREEZABLE)
set_freezable();
repeat:
set_current_state(TASK_INTERRUPTIBLE); // 设置当前线程的运行状态(当前的线程可以接收中断)
if (kthread_should_stop()) { // 判断当前线程是否应该停止运行
__set_current_state(TASK_RUNNING); // 设置当前进程线程为正在运行态
spin_lock_irq(&worker->lock);
worker->task = NULL; // task原来是指向当前线程,现在要停止运行了,指针设置为指向空
spin_unlock_irq(&worker->lock);
return 0;
}
// 若线程没有结束运行,则接着执行下面的代码
// struct kthread_work
work = NULL;
spin_lock_irq(&worker->lock);
// 遍历工人的work_list成员
if (!list_empty(&worker->work_list)) {
// 根据链表结点获取具体工作结构体 kthread_work
work = list_first_entry(&worker->work_list, struct kthread_work, node);
list_del_init(&work->node); // 将work中的链表结点从worker的链表里面删除
}
worker->current_work = work;
spin_unlock_irq(&worker->lock);
if (work) {
__set_current_state(TASK_RUNNING); // 设置当前线程为正在运行
work->func(work); // 自己实现的那个具体函数
} else if (!freezing(current))
schedule(); // 让出处理机资源
try_to_freeze();
cond_resched();
// 其实是个死循环,每一次循环都会有一个链表节点对应的node被提取出来
goto repeat;
}
bool kthread_should_stop(void)
{
// 判断当前线程的标志位
return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)->flags);
}
init_kthread_work()函数
初始化kthread_work,为工作变量结构体指定一个具体的函数fn。
#define kinit_thread_work(work, fn) \
do { \//初始化work成员变量
memset((work), 0, sizeof(struct kthread_work)); \
INIT_LIST_HEAD(&(work)->node); \//初始化work中的链表节点
(work)->func = (fn); \//fn就是我们自定义的
} while (0)
kthread_queue_work()函数
把具体的工作交付给worker(其实就是把work中的链表节点插入到worker对应的链表中)。
bool kthread_queue_work(struct kthread_worker *worker, struct kthread_work *work)
{
bool ret = false;
unsigned long flags;
spin_lock_irqsave(&worker->lock, flags); // 加锁,加锁前先关中断
if (!queuing_blocked(worker, work)) {
kthread_insert_work(worker, work, &worker->work_list);
ret = true;
}
spin_unlock_irqrestore(&worker->lock, flags); // 解锁,加锁后开中断
return ret;
}
kthread_flush_worker()函数
void kthread_flush_worker(struct kthread_worker *worker)
{
struct kthread_flush_work fwork = {
KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),
COMPLETION_INITIALIZER_ONSTACK(fwork.done),
};
// 两件事:work中的node加入,唤醒worker工作
// 其实相当于在worker的链表最末尾新加了一个链表节点,这个具体工作最后被处理(就是最后执行kthread_flush_work_fn)
kthread_queue_work(worker, &fwork.work);
// 参数类型为 struct completion 完成量
// 进程进入休眠状态
// 会在 kthread_flush_work_fn 中唤醒此完成量,其实就是执行到最后kthread_work对应的函数就是这个,那么前面的都执行完毕了
wait_for_completion(&fwork.done);
}