一个Linux内核驱动程序我开发的是在内核中使用网络通信( sock_create()
sock->ops->bind()
,等等)。
问题是,将有多个插槽,从接收数据。 所以,我需要的东西,将模拟select()
或poll()
在内核空间。 由于这些函数使用的文件描述符,我不能使用系统调用,除非我用的系统调用来创建套接字,但似乎没有必要,因为我在内核中很努力。
所以我在想包装默认的sock->sk_data_ready
处理我自己的处理程序( custom_sk_data_ready()
这将开启一个信号。 然后,我可以写我自己的kernel_select()
试图锁定信号和做了阻塞等待,直到它是开放的功能。 这样的内核函数进入睡眠状态,直到信号被解除锁定custom_sk_data_ready()
一旦kernel_select()
获得锁,它解锁并调用custom_sk_data_ready()
重新锁定它。 所以,唯一的额外的初始化是运行custom_sk_data_ready()
绑定到套接字所以在第一次调用之前custom_select()
不误触发。
我看到一个可能出现的问题。 如果有多个接收发生,那么多次调用custom_sk_data_ready()
将尝试解锁信号。 因此,为了不失去多个呼叫和跟踪sock
被使用,但必须是正在使用的表或指针插座的列表。 和custom_sk_data_ready()
将不得不标志表/列表哪个插座它获得通过。
这是法的声音? 或者我应该使用标准的系统调用时,与用户/内核空间问题而大伤脑筋?
初步调查结果:
在所有的回调函数sock
结构被称为在中断上下文。 这意味着他们无法入睡。 为了让主内核线程准备插座的名单上睡觉,互斥体被使用,但custom_sk_data_ready()
必须像对互斥自旋锁(调用mutex_trylock()
反复)。 这也意味着,任何动态分配必须使用GFP_ATOMIC
标志。
另一种可能性:
对于每一个开放式插槽,替换每个插座的sk_data_ready()
有一个自定义( custom_sk_data_ready()
并创建一个工作( struct work_struct
)和工作队列( struct workqueue_struct
)。 一个常见的process_msg()
函数将使用每个工人。 创建一个内核模块级的全局列表,其中每个列表元素的指针指向插座和包含劳动者结构。 当数据准备好一个插座上, custom_sk_data_ready()
将签署并找到具有相同插座匹配列表元素,然后调用queue_work()
与列表元素的工作队列和工人。 然后process_msg()
函数将被调用,并且可以通过内容查找匹配列表元素struct work_struct *
参数(地址),或使用container_of()
宏来获取用于保存列表结构的地址工人结构。
哪种技术是最声音?