在内核中使用sock_create,accept,bind等

时间:2018-10-14 15:40:26

标签: c linux sockets linux-kernel kernel

我正在尝试将echo TCP服务器实现为可加载的内核模块。 我应该使用sock_create还是sock_create_kern?

我应该使用accept还是kernel_accept?

我的意思是说我应该使用例如kernel_accept确实有意义;但我不知道为什么。我不能在内核中使用普通套接字吗?

2 个答案:

答案 0 :(得分:1)

问题是,您正在尝试将 user space 应用程序塞入 kernel

套接字(和文件等)是内核通过内核用户空间API/ABI提供给用户空间应用程序的东西。 有些,但不是全部,也有一个内核内可调用的,以防其他内核希望使用提供给用户空间的某些东西。


让我们在内核源代码中的net/socket.c中查看socket()accept()系统调用的Linux内核实现;寻找SYSCALL_DEFINE3(socket,SYSCALL_DEFINE3(accept,SYSCALL_DEFINE4(recv,,依此类推。

(我建议您使用例如Elixir Cross Referencer在Linux内核源代码中找到特定的标识符,然后在官方的官方Git树之一中在线查找实际代码;无论如何,这就是我要做的。)

请注意指针参数如何具有__user限定符:这意味着指向的数据必须驻留在用户空间中,并且这些函数最终将使用copy_from_user() / {{ 1}}来检索或设置数据。此外,操作会访问文件描述符表,该表是进程上下文的一部分:通常只存在于用户空间进程中的某些文件。

从本质上讲,这意味着您的内核模块必须创建一个用户空间“进程”(足够使用一个进程,以满足使用内核接口时跨越用户空间内核边界的要求),以“保留”最少的内存和文件描述符。这是很多工作,最后,它的性能不会比用户空间应用程序高。 (Linux内核开发人员实际上已经进行了数十年的研究。在某些专有操作系统中,在“内核空间”中执行操作的速度可能更快,但在Linux中并非如此。在用户空间中执行操作的成本是一些上下文切换,可能还有一些内存副本(用于传输的数据)。

尤其是,TCP / IP和UDP / IP接口(例如,对于UDP / IPv4,请参见net/ipv4/udp.c)似乎没有用于内核端缓冲区的任何接口(除了直接访问rx / tx套接字外)缓冲区,它们位于内核内存中。)


您可能听说过TUX web server,这是IngoMolnár的子系统patch to the Linux kernel。即使那不是“内核模块服务器”,但更像是一个子系统,用户空间进程可以使用该子系统来实现主要在内核空间中运行的服务器。

提供TCP / IP和/或UDP / IP服务器的内核模块的想法就像试图用锤子拧入螺丝一样。经过一番尝试,它会起作用,但结果不会很漂亮。

但是,对于回声服务器的特殊情况,可能有可能将其锚定在类似于ICMP数据包的IPv4(请参见net/ipv4/)和/或IPv6(请参见net/ipv6/)之上(net/ipv4/icmp.cnet/ipv6/icmp.c)。仅当您打算专门研究内核端网络方面的东西时,我才会考虑采用这种方法,因为否则您将学到的所有知识都是非常专门的,实际上并没有那么有用。


如果您需要在练习中实现某些内核功能,我建议您不要使用“应用程序”类型的想法(服务或类似的想法)。

相反,我强烈建议开发一个字符设备驱动程序,可能实现某种类型的进程间通信层,最好是总线风格的(即,一个发送者,任意数量的接收者)。诸如此类的东西有许多实际的实际用例(包括硬件驱动程序,以及诸如kdbus类型的东西之类的陌生事物),因此您将学到的任何东西在现实世界中都是适用的。

(实际上,回声字符设备(仅输出写入其中的任何内容)是一个很好的第一个目标。尽管LDD3是用于Linux内核2.6.10的,但对于任何从事Linux内核开发的人如果您使用的是较新的内核,请记住示例代码可能无法按原样编译,因此您可能需要进行一些研究,包括Linux内核Git存储库和/或内核源交叉引用程序。就像上面的Elixir。)

答案 1 :(得分:0)

简而言之,套接字只是一种机制,它使两个进程可以进行本地或远程对话。

如果要将某些数据从内核发送到用户空间,则必须使用内核套接字sock_create_kern()及其一系列功能。

将TCP回显服务器作为内核模块有什么好处?

仅当您的TCP服务器提供的数据无法从用户空间访问时(例如,读取一些无法正常读取的验尸NVRAM,然后通过套接字将其发送到rsyslog