itop4412 设备树 驱动 kthread_worker

itop4412 设备树 驱动 kthread_worker

大概概念

  • 这个主要是在驱动中传输大量数据时候用的,利用内核线程,来传输大量的数据,提高效率,这样就可以避免一个进程出现阻塞的情况。

主要的结构体

  • kthread_worker

    /* 这个可以理解为工人,一直在检查自己的 work_list 里面的工作有没有,如果有
     * 就执行,如果没有就还是检查,相当于一个死循环,一直检查有没有任务添加到 work_list
     * 列表中,如果添加到了,就执行里面的工作
    */
    struct kthread_worker {
    	unsigned int		fl  ags;
    	spinlock_t		lock; 
    	struct list_head	work_list;       // 具体执行的工作
    	struct list_head	delayed_work_list;  // 需要延时执行的工作
    	struct task_struct	*task;         // 任务
    	struct kthread_work	*current_work; // 当前的工作
    };
    
    • 那么怎样初始化这个结构体呢?

      struct kthread_worker hiworker;
      kthread_init_worker(&hiworker);
      // 实际这个函数只初始化了其中一部分变量,其中大部分变量并没有真正的给他赋值
      
  • kthread_work

    /* 这个结构体可以理解为具体的工作,里面有func,这个就是执行的具体的函数
     * 那个worker的指针 里面存放的是哪个工人 
     * 可以理解为哪个工人,执行我这个工作。
     */
    struct kthread_work {
    	struct list_head	node;  // 连接到 kthread_worker->work_list
    	kthread_work_func_t	func;
    	struct kthread_worker	*worker;
    	/* Number of canceling calls that are running at the moment. */
    	int			canceling;
    };
    
  • kthread_flush_work

    /* 这个结构体主要是为了清空,工人的所有任务,
     * 也就是 让 kthread_worker结构体里面的 work_list 里面的任务全部执行完
     * 这个实际也是一个具体的工作,把该工作插入work_list 列表的队尾
     * 它的工作Linux 内核已经写好了 kthread_flush_work_fn,就是这个工作,
     * 而且有专门的函数去掉用,一行代码就调用成功了
     */
    struct kthread_flush_work {
    	struct kthread_work	work; // 工作
    	struct completion	done; // 完成量
    };
    

主要的函数

  • kthread_init_worker()

    /* 这个函数主要是初始化 kthread_worker 结构体
     * 相当于初始化工人 下面这个宏,实际调用了下面的这个初始化函数
     */
    #define kthread_init_worker(worker)					\
    	do {								\
    		static struct lock_class_key __key;			\
    		__kthread_init_worker((worker), "("#worker")->lock", &__key); \
    	} while (0)
    
    /* 下面的代码很好看懂,就是初始化 kthread_worker 里面的变量,
     *  里面并没有放任何有价值的数据
    */
    void __kthread_init_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_run(kthread_worker_fn, &hiworker, …)

    /* 这个函数也是个宏, 展开就是下面的样子,
     * 返回一个一个任务结构体指针 注意第一个参数,传入进去的是一个函数,
     * 也就是工人要干的任务,这个任务Linux 内核已经提供了,我们只要调用就好了
     * 来看看这个函数
    */
    struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
    					   void *data,
    					   int node,
    					   const char namefmt[], ...);
    // 传入的参数 就是我们定义的那个工人
    int kthread_worker_fn(void *worker_ptr)
    {
    	struct kthread_worker *worker = worker_ptr;
    	struct kthread_work *work;
    
    	/*
    	 * FIXME: Update the check and remove the assignment when all kthread
    	 * worker users are created using kthread_create_worker*() functions.
    	 */
    	WARN_ON(worker->task && worker->task != current);
    	worker->task = current;  // 把kthread_worker 里面的 task指向当前线程
    
    	if (worker->flags & KTW_FREEZABLE)
    		set_freezable();
    
    repeat:
        // 设置下当前的状态
    	set_current_state(TASK_INTERRUPTIBLE);	/* mb paired w/ kthread_stop */
    	
        // 判断当前线程是否要停止,停止之后,把task 变量赋值为NULL
    	if (kthread_should_stop()) {
    		__set_current_state(TASK_RUNNING);
    		spin_lock_irq(&worker->lock);
    		worker->task = NULL;
    		spin_unlock_irq(&worker->lock);
    		return 0;
    	}
    
    	work = NULL;
    	spin_lock_irq(&worker->lock); // 加上自旋锁
    	if (!list_empty(&worker->work_list)) { 
            /*  遍历 work_list里面的具体任务
             *  如果有任务就执行,把该任务的函数指针赋值到work结构体里面
             *  赋值完之后 把它从work_list列表里面删除。
             */
    		work = list_first_entry(&worker->work_list,
    					struct kthread_work, node);
    		list_del_init(&work->node);
    	}
        // 设置当前执行的工作
    	worker->current_work = work;
    	spin_unlock_irq(&worker->lock); // 解锁
    
    	if (work) { // 如果work 里面有任务
            // 设置当前为运行态
    		__set_current_state(TASK_RUNNING);
            // 这个地方才是真正执行任务的地方
    		work->func(work);
    	} else if (!freezing(current))
    		schedule(); // 调度
    
    	try_to_freeze();
    	cond_resched();
    	goto repeat;   // 再次循环下去
    }
    
  • kthread_init_work()

    /* 这个就是个宏定义,可以看到
     * 先初始化节点链表
     * 把函数赋值给 (work)->func
     */ 
    #define kthread_init_work(work, fn)					\
    	do {								\
    		memset((work), 0, sizeof(struct kthread_work));		\
    		INIT_LIST_HEAD(&(work)->node);				\
    		(work)->func = (fn);					\
    	} while (0)
    
  • kthread_queue_work()

    /*  这个函数主要是把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)) {
            // 添加work 到 worker
    		kthread_insert_work(worker, work, &worker->work_list);
    		ret = true;
    	}
    	spin_unlock_irqrestore(&worker->lock, flags); // 解锁
    	return ret;
    }
    
  • kthread_flush_worker()

    /* 这个函数主要是清空 worker 里面的任务,
     * KTHREAD_WORK_INIT() 这个主要也是向 worker 里面添加一个 work
     * work 的内容是 kthread_flush_work_fn ,这个函数在Liunx 内核定义了
    */
    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),
    	};
    	
        // 添加到 worker的链表下面,实际就是 添加到work_list的最后面
    	kthread_queue_work(worker, &fwork.work);
    	wait_for_completion(&fwork.done);
    }
    
    static void kthread_flush_work_fn(struct kthread_work *work)
    {
    	struct kthread_flush_work *fwork =
    		container_of(work, struct kthread_flush_work, work);
    	complete(&fwork->done); // 完成
    }
    
  • kthread_stop()

    // 这个部分大概就是释放资源,通知该线程停止
    int kthread_stop(struct task_struct *k)
    {
    	struct kthread *kthread;
    	int ret;
    
    	trace_sched_kthread_stop(k);
    
    	get_task_struct(k);
    	kthread = to_kthread(k);
    	// 可以看到这个设置了 KTHREAD_SHOULD_STOP 这个变量,刚好对应
        // kthread_run() 这个函数里面检测,线程是否结束的标志
    	set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
    	kthread_unpark(k);
    	wake_up_process(k);
    	wait_for_completion(&kthread->exited);
    	ret = k->exit_code;
    	put_task_struct(k);
    
    	trace_sched_kthread_stop_ret(ret);
    	return ret;
    }
    

使用的大概流程

  • 先初始化 worker(工人)这个结构体调用 kthread_init_worker()

  • 在open函数中,创建一个线程 调用kthread_run()

  • 在write 函数中,加入一个work(工作),kthread_init_work();

    ​ kthread_queue_work();

  • 在release函数中,停止该线程 kthread_flush_worker()

    kthread_stop

  • 用这几个函数就完全够用了。

总结

  • 大概就是 创建一个线程,把数据处理的工作,放在线程中,这样应用程序,就不会太阻塞的太严重,从而影响应用程序。
  • … 以后想起来 再补充,其实我想画图呢。。 太懒了。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值