18.kthread_worker:内核线程异步传输

目录

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 

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 

/*
 * 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 
#include 
#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
 
#include 
#include 
#include 
 
#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;
}

18.kthread_worker:内核线程异步传输_第1张图片

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);
}

18.kthread_worker:内核线程异步传输_第2张图片

 

你可能感兴趣的:(#,野火i.mx,6ull内核驱动进阶,linux)