在内核套接字编程中模拟select()和poll()的效果

时间:2012-07-04 22:31:27

标签: linux sockets networking kernel system-calls

我正在开发的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()宏来获取地址包含工作者结构的列表结构。

哪种技术最健全?

1 个答案:

答案 0 :(得分:3)

你的第二个想法听起来更像是可行的。

CEPH代码看起来类似,请参阅net/ceph/messenger.c