我正在开发的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()
将执行并找到具有相同套接字的匹配列表元素,然后使用list元素的工作队列和worker调用queue_work()
。然后将调用process_msg()
函数,并且可以通过struct work_struct *
参数(地址)的内容找到匹配的列表元素,或者使用container_of()
宏来获取地址包含工作者结构的列表结构。
哪种技术最健全?
答案 0 :(得分:3)
你的第二个想法听起来更像是可行的。
CEPH代码看起来类似,请参阅net/ceph/messenger.c
。