12

我正在开发的 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()每个工人都将使用一个通用功能。创建一个内核模块级全局列表,其中每个列表元素都有一个指向套接字的指针并包含工作结构。当一个socket上的数据准备好时,custom_sk_data_ready()会执行并找到与同一个socket匹配的list元素,然后queue_work()用list元素的工作队列和worker调用。然后process_msg()调用该函数,既可以通过struct work_struct *参数的内容(地址)找到匹配的列表元素,也可以使用container_of()宏获取保存工作结构的列表结构的地址。

哪种技术最可靠?

4

1 回答 1

3

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

CEPH 代码看起来像是在做类似的事情,请参阅net/ceph/messenger.c.

于 2012-08-16T04:21:24.347 回答