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

这篇具有很好参考价值的文章主要介绍了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 <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;
}

18.kthread_worker:内核线程异步传输,# 野火i.mx 6ull内核驱动进阶,linux

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:内核线程异步传输,# 野火i.mx 6ull内核驱动进阶,linux

 文章来源地址https://www.toymoban.com/news/detail-688676.html

到了这里,关于18.kthread_worker:内核线程异步传输的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 内核线程与用户线程的区别

    内核线程和用户线程是操作系统中的两种不同类型的线程,它们有以下异同点: 异同点: 相同点:内核线程和用户线程都是线程的一种,都可以执行任务。 不同点:内核线程是由操作系统内核创建和管理的,而用户线程是由应用程序创建和管理的。 不同点:内核线程运行在

    2024年02月16日
    浏览(35)
  • 基于FPGA的通用异步收发传输器(UART)设计

    基本目的:   (1)了解UART通讯原理,包括数据传输格式、电气特性等; (2)研究Basys3开发板与PC之间通讯电平规格的转换; (3)设计并实现UART的发送(TX)功能或接收(RX)功能。 高级任务(可选):可调。 (4)设计并实现UART的发送(TX)功能和接收(RX)功能,构建回

    2024年02月16日
    浏览(45)
  • 线程池创建线程异步获取Future超时

    其中,future.get是从开始进行get方法时进行计算的时间,非future生成开始计算的,即什么时候get什么时候开始计时。 线程池从生成线程,如果核心线程不为0,则有任务时一直生成核心线程,直至到核心线程,之后开始方队列中,最后任务多就开始开辟新线程到最大线程数。

    2024年02月02日
    浏览(37)
  • Linux 内核线程启动以及内核调用应用层程序

    #include linux/kthread.h //内核线程头文件   static task_struct *test_task; test_task = kthread_run(thread_function, NULL, \\\"test_thread_name\\\"); if(IS_ERR(test_task)) {         pr_err(\\\"test_thread_name create failn\\\"); } static int thread_function(void *arg) {     char *envp[3];     char *argv[3];     int ret= 0;     argv[0] = \\\"/bin/sh\\\";  

    2024年02月12日
    浏览(53)
  • @Async异步线程:Spring 自带的异步解决方案

            在项目应用中,使用MQ异步调用来实现系统性能优化,完成服务间数据同步是常用的技术手段。如果是在同一台服务器内部,不涉及到分布式系统,单纯的想实现部分业务的异步执行,这里介绍一个更简单的异步方法调用。         对于异步方法调用,从Spring3 开

    2023年04月24日
    浏览(62)
  • 计算机网络——18无连接传输UDP

    “尽力而为的”服务,报文段可能 丢失 送到应用进程的报文段乱序 无连接 UDP发送端和接收端之间没有握手 每个UDP报文段都被独立的处理 UDP被用于 流媒体 DNS SNMP 在UDP上实现可靠传输 在应用层增加可靠性 应用特定的差错格式 为什么要有UDP 不建立连接(会增加延时) 简单:

    2024年02月20日
    浏览(40)
  • ET介绍——单线程异步

    前面几个例子都是多线程实现的异步,但是异步显然不仅仅是多线程的。我们在之前的例子中使用了Sleep来实现时间的等待,每一个计时器都需要使用一个线程,会导致线程切换频繁,这个实现效率很低,平常是不会这样做的。一般游戏逻辑中会设计一个单线程的计时器,我

    2024年02月05日
    浏览(39)
  • 驱动开发:内核ShellCode线程注入

    还记得 《驱动开发:内核LoadLibrary实现DLL注入》 中所使用的注入技术吗,我们通过 RtlCreateUserThread 函数调用实现了注入DLL到应用层并执行,本章将继续探索一个简单的问题,如何注入 ShellCode 代码实现反弹Shell,这里需要注意一般情况下 RtlCreateUserThread 需要传入两个最重要的

    2024年02月08日
    浏览(48)
  • 【业务功能篇92】微服务-springcloud-多线程-异步处理-异步编排-CompletableFutrue

    一个商品详情页 展示SKU的基本信息 0.5s 展示SKU的图片信息 0.6s 展示SKU的销售信息 1s spu的销售属性 1s 展示规格参数 1.5s spu详情信息 1s   Future是Java 5添加的类,用来描述一个异步计算的结果。你可以使用 isDone 方法检查计算是否完成,或者使用 get 阻塞住调用线程,直到计算

    2024年02月10日
    浏览(55)
  • Flutter 的线程模型和异步原理

    本文字数:: 36130 字 预计阅读时间: 91 分钟 在 Android 应用中, 用户时常会遇到界面卡顿的情况,非常影响用户的体验。作为 Android 开发肯定都知道:应用在主线程里做了大量的耗时操作(例如文件读写, 数据库读写,网络访问,图片编解码等),就会导致 UI 不能及时刷新,

    2024年02月15日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包