如何给work回调函数传递用户参数

背景

Linux驱动开发中,经常会用到work queue,该数据结构管理的是一个个的work_struct结构体:

struct work_struct {
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

最近在做pcie驱动开发,有个需求是1个pcie端口传输5块采集卡的数据,每块卡的收发是独立且随机的,所以需要用work queue,它有一个内置的内核线程,可以轮询每块板卡的状态。

每块板卡都有一个work_struct,用于work queue启动work时 分辨板卡是哪个槽位(slot)的。

struct board{
	int slot;  // 板卡所属槽位号
	work_struct retrieve_work; // 板卡的work_struct
}
// 定义board数组
struct board boards[5];  //有5张板卡,分属5个槽位

// 驱动初始化时绑定用户函数retrieve_func到work item
for (int i = 0; i < 5; i++) {
	INIT_WORK(&boards[i]->retrieve_work, retrieve_func);
}

// 驱动ISR用work queue启动特定板卡的retrieve_work
int slot = get_field(STATUS_REG);
schedule_work(&boards[slot].retrieve_work);

work结构体中有个回调函数字段func,类型work_func_t

typedef void (*work_func_t)(struct work_struct *work);

问题

发现一个问题,该回调函数只有1个入参,即work_struct结构体自身,而没有预留一个用户自定义参数,这样当用户想要传递槽位号时,就需要查状态寄存器之类的特殊途径,不够通用。

错误的思路

根据以往经验,work结构体应该像device结构体有个void *型的driver_data字段那样,有个自己的void * work_data字段,但是看来看去,只有atomic_long_t data字段像是干这个的,于是打算板卡初始化时这样

for (int i = 0; i < 5; i++) {
	INIT_WORK(&boards[i]->retrieve_work, retrieve_func);
	boards[i].retrieve_work.data = slot;  // 将槽位号信息储存到data字段
}

在回调函数retrieve_func里这样做:

void retrieve_func(struct work_struct *work) {
	int slot = (int)work->data; // 提取槽位号
	transfer(slot);  // 传输特定槽位的板卡数据
}

但是搜遍内核代码,也没看到有利用work_structdata字段的。仔细查看相关头文件,原来这个data字段最开始确实是用于传递用户数据的,但是后来就变成work queue管理work_struct内部状态的flag字段,所以驱动无法使用。

解决方法

灵机一动想到可以用container_of宏,通过work_struct结构体的指针获取外围的board结构体指针,然后再通过常规的pBoard->slot之类的表达式得到槽位号

void retrieve_func(struct work_struct *work) {
	// 获取外围结构体
	struct board *b = container_of(work, struct board, retrieve_work);
	transfer(b->slot);  // 传输特定槽位的板卡数据
}

对于container_of宏三个参数的解释:

  • 第一个参数workwork_struct实例的地址
  • 第二个参数struct board外围结构体类型名
  • 第三个参数retrieve_workwork_struct在外围结构体中的字段名

不过后来去网上一搜,原来这是标准做法,自己重新发明了一次轮子😓

总结

回调函数传递用户自定义参数的2种方法:

  1. 回调函数自己预留void *指针
  2. 将用户参数跟回调函数的某个参数一起嵌入到用户自定义结构体中

方法2的好处是,如果回调函数不需要处理多种数据,则用户不需要像模式1那样给void *参数传递NULL指针,更自由些,扩展性也更好。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的回调函数是一种编程模式,也称为回调机制。它许将一段代码作为参数传递给另一个方法,并在需要时执行。回调函数通常用于异步编程或事件处理,可以将程序的控制权转移到回调函数上,以便在特定事件发生时执行相关的操作。 举个例子,假设我们有一个方法A,它需要执行一些操作,但是这些操作需要在另一个方法B完成后才能进行。我们可以将方法A作为参数传递给方法B,然后在方法B完成后调用方法A,这就是回调函数的基本思想。 下面是一个简单的Java回调函数的例子: ```java public class CallbackExample { public static void main(String[] args) { Worker worker = new Worker(); worker.doWork(new Callback() { @Override public void onComplete() { System.out.println("Work completed!"); } }); } } interface Callback { void onComplete(); } class Worker { public void doWork(Callback callback) { // 模拟工作 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 工作完成后调用回调函数 callback.onComplete(); } } ``` 在这个例子中,我们定义了一个Worker类,它有一个doWork方法,该方法接受一个Callback对象作为参数。在doWork方法中,我们模拟了一些工作,并在工作完成后调用了回调函数的onComplete方法。 在main方法中,我们创建了一个Worker对象,并将一个匿名的Callback对象传递给它的doWork方法。当工作完成后,回调函数的onComplete方法将被调用,并输出"Work completed!"。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值