admin管理员组

文章数量:1531793

2024年5月5日发(作者:)

netlink socket编程why & how

作者: Kevin Kaichuan He@2005-1-5

翻译整理:duanjigang @2008-9-15

原文:

/article/7356

开发和维护内核是一件很繁杂的工作,因此,只有那些最重要或者与系统性能息息相关的代码才将其

安排在内核中。其它程序,比如GUI,管理以及控制部分的代码,一般都会作为用户态程序。在linux系统

中,把系统的某个特性分割成在内核中和在用户空间中分别实现一部分的做法是很常见的(比如linux系统

的防火墙就分成了内核态的Netfilter和用户态的iptables)。然而,内核程序与用户态的程序又是怎样行通讯

的呢?

答案就是通过各种各样的用户态和内核态的IPC(interprocess communication )机制来实现。比如系统

调用,ioctl接口,proc文件系统以及netlink socket,本文就是要讨论netlink socekt并向读者展示这种用网络

通讯接口方式实现的IPC机制的优点。

介绍:

netlink socekt是一种用于在内核态和用户态进程之间进行数据传输的特殊的IPC。它通过为内核模块提

供一组特殊的API,并为用户程序提供了一组标准的socket 接口的方式,实现了一种全双工的通讯连接。

类似于TCP/IP中使用AF_INET地址族一样,netlink socket使用地址族AF_NETLINK。每一个netlink

socket在内核头文件include/linux/netlink.h中定义自己的协议类型。

下面是netlink socket 目前的特性集合以及它支持的协议类型:

¾ NETLINK_ROUTE: 用户空间的路由守护程序之间的通讯通道,比如BGP,OSPF,RIP以及内核数据转

发模块。用户态的路由守护程序通过此类型的协议来更新内核中的路由表。

¾

¾

¾

NETLINK_FIREWALL:接收IPV4防火墙代码发送的数据包。

NETLINK_NFLOG:用户态的iptables管理工具和内核中的netfilter模块之间通讯的通道。

NETLINK_ARPD:用来从用户空间管理内核中的ARP表。

为什么以上的功能在实现用户程序和内核程序通讯时,都使用netlink方法而不是系统调用,ioctls

或者proc文件系统呢?原因在于:为新的特性添加一个新的系统调用,ioctls或者一个proc文件的做法并不

是很容易的一件事情,因为我们要冒着污染内核代码并且可能破坏系统稳定性的风险去完成这件事情。

然而,netlink socket却是如此的简单,你只需要在文件netlink.h中添加一个常量来标识你的协议类型,然

后,内核模块和用户程序就可以立刻使用socket风格的API进行通讯了!

Netlink提供了一种异步通讯方式,与其他socket API一样,它提供了一个socket队列来缓冲或者平滑

瞬时的消息高峰。发送netlink消息的系统调用在把消息加入到接收者的消息对列后,会触发接收者的接收

处理函数。接收者在接收处理函数上下文中,可以决定立即处理消息还是把消息放在队列中,在以后其它

上下文去处理它(因为我们希望接收处理函数执行的尽可能快)。系统调用与netlink不同,它需要一个同步

的处理,因此,当我们使用一个系统调用来从用户态传递消息到内核时,如果处理这个消息的时间很长的

话,内核调度的粒度就会受到影响。

内核中实现系统调用的代码都是在编译时静态链接到内核的,因此,在动态加载模块中去包含一个系

统调用的做法是不合适的,那是大多数设备驱动的做法。使用netlink socket时,动态加载模块中的netlink

程序不会和linux内核中的netlink部分产生任何编译时依赖关系。

Netlink优于系统调用,ioctls和proc文件系统的另外一个特点就是它支持多点传送。一个进程可以把

消息传输给一个netlink组地址,然后任意多个进程都可以监听那个组地址(并且接收消息)。这种机制为内

核到用户态的事件分发提供了一种近乎完美的解决方案。

系统调用和ioctl都属于单工方式的IPC,也就是说,这种IPC会话的发起者只能是用户态程序。但是,

如果内核有一个紧急的消息想要通知给用户态程序时,该怎么办呢?如果直接使用这些IPC的话,是没办

法做到这点的。通常情况下,应用程序会周期性的轮询内核以获取状态的改变,然而,高频度的轮询势必

会增加系统的负载。Netlink 通过允许内核初始化会话的方式完美的解决了此问题,我们称之为netlink

socket的双工特性。

最后,netlink socket提供了一组开发者熟悉的BSD风格的API函数,因此,相对于使用神秘的系统

调用API或者ioctl而言,netlink开发培训的费用会更低些。

与BSD的Routing socket的关系

在BSD TCP/IP的协议栈实现中,有一种特殊的socket叫做Routing socket.它的地址族为AF_ROUTE, 协议

族为PF_ROUTE, socket类型为SOCK_RAW. 这种Routing socket是用户态进程用来向内核中的路由表增加

或者删除路由信息用的。在Linux系统中,netlink socket通过协议类型NETLINK_ROUTE实现了与Routing

socket相同的功能,可以说,netlink socket提供了BSD Routing socket功能的超集。

Netlink Socket 的API

标准的socket API函数-socket(), sendmsg(), recvmsg()和close()-都能够被用户态程序直接调用来访问netlink

socket.你可以访问man手册来获取这些函数的详细定义。在本文,我们只讨论怎样在netlink socket的上下

文中为这些函数选择参数。这些API对于使用TCP/IP socket写过一些简单网络程序的读者来说应该很熟悉

了。

使用socket()函数创建一个socket,输入:

本文标签: 内核用户系统