Linux设备驱动器 之一 工作(worker)线程

数据结构

struct kthread_worker {
    unsigned int        flags;
    raw_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_work {
    struct list_head    node;
    kthread_work_func_t func;
    struct kthread_worker   *worker;
    /* Number of canceling calls that are running at the moment. */
    int         canceling;
};

struct kthread_delayed_work {
    struct kthread_work work;
    struct timer_list timer;
};

Linux APIs

产生工作线程 kthread_create_worker

struct kthread_worker *kthread_create_worker(unsigned int flags, const char namefmt[], …)

  1. unsigned int flags : 指定任务工作线程默认行为
  2. const char namefmt[] : kthread 任务工作线程的printf样式名称。
  3. … : 变量参数

如果成功,则返回一个指针, 它指向已产生的任务工作线程;
如果无法分配所需的结构,则返回 ERR_PTR(-ENOMEM);
如果调用者收到一个致命信号,则返回 ERR_PTR(-EINTR)。

初始化工作 kthread_init_work

kthread_init_work(work, fn)
这是一个宏定义,它初始化struct kthread_work结构变量work, 并设置fn为完成该工作的程序代码。就是调用fn去完成需要的工作。

排队工作 kthread_queue_work

bool kthread_queue_work(struct kthread_worker *worker, struct kthread_work *work)
功能: 排队一个kthread_work
参数:

  1. struct kthread_worker *worker : 任务工作线程
  2. struct kthread_work *work: 需要排队的工作

将工作排队到工作处理器任务以进行异步执行。任务必须已使用 kthread_worker_create() 创建。如果工作已成功排队,则返回 true;如果工作已处于挂起状态,则返回 false。
如果工作需要由其他任务工作线程使用,请重新初始化该工作。例如,当任务工作线程停止并再次启动时。

在Linux中的应用实列

SPI 驱动器与imx SPI

spi_init_queue调用kthread_create_worker,kthread_init_work, 去产生SPI任务工作线程,细节见下面的代码段。

static int spi_init_queue(struct spi_controller *ctlr)
{
    ctlr->running = false;
    ctlr->busy = false;

    ctlr->kworker = kthread_create_worker(0, dev_name(&ctlr->dev));
    if (IS_ERR(ctlr->kworker)) {
        dev_err(&ctlr->dev, "failed to create message pump kworker\n");
        return PTR_ERR(ctlr->kworker);
    }

    kthread_init_work(&ctlr->pump_messages, spi_pump_messages);

    /*
     * Controller config will indicate if this controller should run the
     * message pump with high (realtime) priority to reduce the transfer
     * latency on the bus by minimising the delay between a transfer
     * request and the scheduling of the message pump thread. Without this
     * setting the message pump thread will remain at default priority.
     */
    if (ctlr->rt)
        spi_set_thread_rt(ctlr);

    return 0;
}

imx SPI 驱动器支持NXP i.MX 8M Nano。
下列的流程图描绘了imx SPI 驱动器如何调用spi_init_queue去初始化任务工作线程。

Created with Raphaël 2.3.0 spi_imx_probe imx 专用初始化 spi_bitbang_start 结束
Created with Raphaël 2.3.0 spi_bitbang_start spi_bitbang_init bitbang初始化成功 ? spi_register_master 结束 yes no

spi_register_master 由下列的宏定义

#define spi_register_master(_ctlr)  spi_register_controller(_ctlr)
Created with Raphaël 2.3.0 spi_register_controller spi_controller_initialize_queue 结束
Created with Raphaël 2.3.0 spi_controller_initialize_queue spi_init_queue 结束

任务工作线程代码

static void spi_pump_messages(struct kthread_work *work)

Created with Raphaël 2.3.0 spi_pump_messages __spi_pump_messages 结束
Created with Raphaël 2.3.0 __spi_pump_messages 任务工作线程空闲 ? prepare_transfer_hardware, 来自SPI控制结构 prepare_message, 来自SPI控制结构 spi_map_msg, 来自SPI控制结构 transfer_one_message, 来自SPI控制结构 结束 kthread_queue_work yes no

如果SPI任务工作线程忙,则调用kthread_queue_work,将任务工作加入到等待队列。

启动任务工作线程

工作线程(worker)

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;

    if (worker->flags & KTW_FREEZABLE)
        set_freezable();

repeat:
    set_current_state(TASK_INTERRUPTIBLE);  /* mb paired w/ kthread_stop */

    if (kthread_should_stop()) {
        __set_current_state(TASK_RUNNING);
        raw_spin_lock_irq(&worker->lock);
        worker->task = NULL;
        raw_spin_unlock_irq(&worker->lock);
        return 0;
    }

    work = NULL;
    raw_spin_lock_irq(&worker->lock);
    if (!list_empty(&worker->work_list)) {
        work = list_first_entry(&worker->work_list,
                    struct kthread_work, node);
        list_del_init(&work->node);
    }
    worker->current_work = work;
    raw_spin_unlock_irq(&worker->lock);

    if (work) {
        kthread_work_func_t func = work->func;
        __set_current_state(TASK_RUNNING);
        trace_sched_kthread_work_execute_start(work);
        work->func(work);
        /*
         * Avoid dereferencing work after this point.  The trace
         * event only cares about the address.
         */
        trace_sched_kthread_work_execute_end(work, func);
    } else if (!freezing(current))
        schedule();

    try_to_freeze();
    cond_resched();
    goto repeat;
}                                                                                                        

这个线程的流程图如下

Created with Raphaël 2.3.0 开始 设置当前任务为可中断 任务工作线程是否应该继续? 从任务链表取下一个任务 有下一个任务工作吗 调用任务工作的回调函数 尝试冻结当前线程 重新规划线程调用 yes no yes no

它检查这个任务线程的任务工作链表,如果非空,那么就从任务工作链表上取下一个任务工作 并运行它的回调函数。这个回调函数在任务工作初始化是设置。
从这个程序段看出,任务工作一定要初始化。

Linux管理线程

Linux系统初始时,产生了一个工作线程的管理线程,这里称其为任务线程产生的监视线程。

kthread_create_worker产生一个struct kthread_create_info变量,并将这个变量加入到一个待产生线程链表中,这个链表的表头是kthread_create_list

监视线程监视这个待产生线程链表,一旦新的变量加入这个链表,这个监视线程就产生一个新的任务工作(worker)线程。

  • 27
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GUI线程Worker线程是常见的多线程编程模型,用于实现图形用户界面(GUI)应用程序的并发处理。 1. GUI线程(也称为主线程):GUI线程负责处理用户界面的绘制、响应用户输入等任务。在大多数GUI框架中,GUI线程也是事件循环线程,负责监听和分发用户事件(如鼠标点击、键盘输入)并对其作出相应的响应。在这种模型下,GUI线程通常是单线程的,意味着在GUI线程中执行的任何耗时操作都会导致界面冻结,造成用户体验下降。 2. Worker线程Worker线程用于执行耗时的任务,以避免在GUI线程中阻塞。这些任务可以是计算密集型的操作,如图像处理、数据分析等,也可以是I/O密集型的操作,如文件读写、网络请求等。通过将这些任务放在Worker线程中执行,可以保持GUI线程的响应性,使用户能够继续与界面进行交互。 在实际开发中,通常采用以下方式来实现GUI线程Worker线程的协同工作: 1. 任务分发:GUI线程接收用户事件,并将耗时任务委托给Worker线程处理。这可以通过消息队列、事件驱动等机制来实现,GUI线程将任务放入队列中,Worker线程从队列中取出任务并执行。 2. 线程间通信:GUI线程Worker线程之间需要进行数据交换和同步。常见的线程间通信方式包括使用线程安全的队列、事件、互斥锁、条件变量等机制。 3. 界面更新:在Worker线程执行完耗时任务后,需要将结果返回给GUI线程,并更新界面显示。通常可以通过回调函数、信号槽机制等方式来实现。 需要注意的是,在多线程编程中,要注意线程安全性和资源管理,避免竞态条件、死锁等问题的发生。此外,对于一些特殊的操作,如对GUI组件的修改,可能需要在GUI线程中执行,以避免跨线程访问的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值