内核和用户空间的信息交互主要有如下接口:
1. procfs(/proc文件系统)
这是一个虚拟文件系统,挂载在/proc目录下。允许内核以文件的形式向用户空间输出内部信息,这些文件并不实际存在于磁盘上,但可以通过cat或more以及‘>’shell重定向字符予以写入。
2. sysctl(/proc/sys目录)
此接口允许用户读取或修改内核变量的值。从用户空间可以用两种方式访问sysctl输出的变量,一是sysctl系统调用,另一种是procfs。
3. sysfs
以非常干净而有组织的方式输出很多信息,主要解决了procfs与sysctl滥用的问题而出现的。
Procfs与sysctl
procfs和sysctl都输出内核内部信息,但procfs主要是输出只读数据,而大多数sysctl信息是可写入的。与一个简单的内核变量或数据结构相关联的一些文件,可以用sysctl输出。其他涉及更为复杂的数据结构而需要特殊格式时,就以procfs输出,如缓存和统计数据。
Procfs
大多数网络功能会在引导时或在模块加载时其初始化期间在/proc中注册一个或多个文件。当用户读取该文件时,会引起内核间接运行一组内核函数,返回某种输出内容。网络代码所注册的文件位于/proc/net。
/proc中的目录可以使用proc_mkdir创建。/proc/net中的文件可以使用定义在include/linux/proc_fs.h中的proc_net_fops_create和proc_net_remove予以注册和注销。还有两个通用的API函数create_proc_entry和remove_proc_entry。proc_net_fops_create负责调用proc_net_create创建文档,然后初始化器文件操作处理函数。
sysctl
用户在/proc/sys下看到的一个文件,实际都是一个内核变量。对于每个变量,内核可以定义:
- 将其放在/proc/sys的何处。与相同内核组件或功能相关联的变量通常都位于同一个目录中,如/proc/sys/net/ipv4目录中都是与ipv4相关的文件)。
- 命名。多数时候文件名只是简单的命名为与相关联的内核变量相同的名字。
- 访问权限。如一个文件可以由任何人读,但只能由超级用户修改。
- 当一个内核模块实现一项新功能或一个协议被加载或卸载时;
- 当一个新的网络设备被注册或注销时。都有可能创建目录或文件。
socket操作的ioctl命令命令有一定的规范,如新增一条路由的命令SIOCADDRT中SIOC表示Socket Ioctl,ADD表示添加,RT表示添加路由。当一个对象类型还可以被读写时,命令中还会增加G表示获取,S表示设置。如SIOCGIFADDR和SIOCSIFADDR分别表示为指定的网络接口新增或删除一条IP地址。
网络使用的ioctl命令都定义在include/linux/sockios.h文件中。设备驱动程序可以定义自己的私有命令,其范围介于SIOCDEVPRIVATE和SIOCDEVPRIVATE+15之间,但使用自己的私有命令是不被推荐的。因为各种协议都可以在这个范围定义自己的私有命令。
Netlink
Netlink套接字代表用户空间与内核的IP网络配置之间的首选接口,Netlink也可作为内核内部以及多个用户空间进程之间的消息传输系统。利用Netlink套接字,可以使用标准套接字API打开或关闭套接字、使用套接字传输数据或者接收套接字数据:
int socket(int domain, int type, int protocol)
同其他套接字一样,在打开一个Netlink套接字时,必须提供domain、type、以及protocol参数。Netlink使用新的PF_NETLINK协议簇,只支持SOCK_DGRAM类型、并且定义了几种协议,每一种都用于网络协议栈的不同组件。如NETLINK_ROUTE协议用于大多数网络功能,如路由和邻居协议。NETLINK_FIREWALL用于防火墙。Netlink的协议定义在include/linux/netlink.h文件中:
#define NETLINK_ROUTE 0 /* Routing/device hook */
#define NETLINK_UNUSED 1 /* Unused number */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Firewalling hook */
#define NETLINK_INET_DIAG 4 /* INET socket monitoring */
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ISCSI 8 /* Open-iSCSI */
#define NETLINK_AUDIT 9 /* auditing */
#define NETLINK_FIB_LOOKUP 10
#define NETLINK_CONNECTOR 11
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_GENERIC 16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
#define MAX_LINKS 32
使用Netlink套接字时,端点是打开此套接字的进程的ID标识,而特殊值0表示是内核。Netlink的功能之一是传送单播和多播消息。目的端的端点地址可以是一个PID、一个多播群组ID或两者的组合。内核定义Netlink多播群组的目的是传出特定种类事件爱你的通知信息,而若用户程序对这类信息感兴趣,可以向这些群组注册。这些群组列在include/linux/rtnetlink.h文件中的RTMGRP_XXX /* RTnetlink multicast groups - backwards compatibility for userspace */
#define RTMGRP_LINK 1
#define RTMGRP_NOTIFY 2
#define RTMGRP_NEIGH 4 //用于通知L3到L2的地址映射的改变
#define RTMGRP_TC 8
#define RTMGRP_IPV4_IFADDR 0x10
#define RTMGRP_IPV4_MROUTE 0x20
#define RTMGRP_IPV4_ROUTE 0x40 //用于通知有关路由表的改变
#define RTMGRP_IPV4_RULE 0x80
#define RTMGRP_IPV6_IFADDR 0x100
#define RTMGRP_IPV6_MROUTE 0x200
#define RTMGRP_IPV6_ROUTE 0x400
#define RTMGRP_IPV6_IFINFO 0x800
#define RTMGRP_DECnet_IFADDR 0x1000
#define RTMGRP_DECnet_ROUTE 0x4000
#define RTMGRP_IPV6_PREFIX 0x20000
Netlink相对于ioctl接口的优点之一是内核可以启动传输,而不仅限于响应用户空间请求而返回信息。
在Linux网络子系统中,每次应用配置改变时,内核中负责处理此事的例程都会取得一个信号量rtnl_sem,以确保对存储网络配置内容的数据结构的访问具有互斥性,无论该配置的改变是通过ioctl还是netlink,都是如此。