概论
内核通过各种不同的接口把内部信息输出到用户空间。
除了系统调用外,还有三个特殊接口(其中两个是虚拟文件系统)
procfs (/proc 文件系统)
- 虚拟文件系统,通过 mount 在 /proc,允许内核以文件的形式向用户控件输出内部信息
sysctl (/proc/sys 目录)
此接口允许用户空间读取或修改内核变量的值
用户空间可以使用两种方式访问 sysctl 输出的变量
- sysctl 系统调用 (man sysctl)
- procfs, 当内核支持 procfs 时,会在
/proc中添加一个特殊目录(/proc/sys),为每个由 sysctl 所输出的内核变量引入一个文件 - sysctl 命令可用于配置由 sysctl 接口所输出的变量
sysfs (/sys 文件系统)
- 新的文件系统,以非常干净而有组织的方式输出很多信息
你还可以使用以下接口向内核发送命令,既可以用于配置某些内容,也可以用于转储其他内容的配置。
ioctl 系统调用
- ioctl(输入/输出控制)系统调用操作的对象是一个文件
Netlink 套接字 (socket)
- 网络应用程序与内核通信时的首选机制
procfs 与 sysctl
相同点:
- 输出内核内部信息
不同点:
procfs
- 主要输出只读数据
- 复杂的数据结构且需要特殊格式时,如缓存和统计数据
sysctl
- 大多数都可写入,但只有 superuser 能写入
- 一个简单的内核变量或数据结构相关联的一些文件
procfs
大多数网络功能在其初始化时都会在
/proc中注册一个或多个文件。网络代码所注册的文件位于
/proc/net。
1 | // 创建 /proc 中的目录 |
示例:ARP 协议如何在 /proc/net 中注册其 arp 文件
1 | static struct file_operations arp_seq_fops = { |
当用户读取该文件时,使用 file_operations 数据结构,允许 procfs 返回相当多的数据给用户。
arp_seq_open 会做另一次重要的初始化:注册一个函数指针数组
1 | static struct seq_operations arp_seq_ops = { |
sysctl: 目录 /proc/sys
用户在 /proc/sys 下看到的一个文件,实际上是一个内核变量。
/proc/sys/net/ipv4中可以找到与 IPv4 相关的文件
/proc/sys 中的文件和目录都是以 ctl_table 结构定义
1 | /* A sysctl table is an array of struct ctl_table: */ |


ctl_table 初始化的实例
/proc/sys/net/ipv4/conf/default/forwarding 文件所用的 ctl_table 实体的初始化定义在 net/ipv4/devinet.c 中
1 | { |
- r:4, w:2, x:1
kernel/sysctl.c 中一个目录的声明实例:
1 | { |
- 定义了
/proc/sys/net目录 - 不需要 proc_handler
- child 指针,指向另一个 ctl_table 实体,ctl_table 实体列表的头元素
在 /proc/sys 中注册文件
1 | struct ctl_table_header *register_sysctl_table(ctl_table * table, |
示例:文件 logging_level 的定义以及如何放置到 /proc/sys/dev/scsi 目录
1 | static ctl_table scsi_table[] = { |

核心网络文件和目录
- 每个目录以及目录中的每个文件,都是一个
ctl_table实体

图 3-3 中的三个方块显示了 ctl_table 初始化的三个实例:
netdev_max_backlog 文件被分派了一个 proc_handler 例程,但没有 strategy。因为 netdev_max_backlog 是一个整数,来自于用户的输入由 proc_dointvec 读取。
min_delay 文件被分派了 proc_handler 和 strategy 。因为内核变量 ip_rt_min_delag 已 jiffies 表示,但是用户的输入和输出都是以秒来表示,这两个例程可以完成秒转换为 jiffies。
ip_local_port_range 文件允许用户配置一个范围,定义两个值,所选的 strategy 和 proc_handler 例程必须能够管理一个整数值的数组。extra1 和 extra2 表示这个范围。

ioctl
ifconfig 命令使用 ioctl 与内核通信。
当系统管理员输入 ifconfig eth0 mtu 1250 命令改变 eth0 的 MTU 时,
- ifconfig 会打开一个套接字,用从系统管理员那里接受的信息初始化一个本地数据结构
- 然后以 ioctl 调用传送给内核
1 | struct ifreq data; |

图3-4 显示了网络代码最常用的 ioctl 命令如何由 sock_ioctl 分派,并且路由至正确的函数处理例程。
SIOC ADD RT : 把一条路径新增至路由表的命令 (SIOCADDRT)
ADD: 添加
RT: 路由
G: 取得
S: 设置
SIOCGIFADDR, SIOCSIFADDR 两个命令为接口新增或删除 IP 地址。
SIOCSIFMTU 是设定(S)接口(IF)的最大传输单元(MTU),由 dev_ioctl 所做。
网络用的 ioctl 命令在 include/linux/sockios.h 中。
设备驱动程序可以定义新的(私有)命令,其范围介于 SIOCDEVPRIVATE 和 SIOCDEVPRIVATE+15之间。
include/linux/if_tunnel.h 中定义的(虚拟)隧道设备使用的四个私有命令。
1 |
Netlink
用户空间与内核的 IP 网络配置之间的首选接口
也可作为内核内部以及多个用户空间进程之间的消息传输系统
通过 Netlink 套接字,可以使用标准套接字 API
1 |
|
Netlink 使用新的 PF_NETLINK 协议族,只支持 SOCK_DGRAM 类型,在include/linux/netlink.h定义了几种协议:
NETLINK_ROUTE: 大多数网络功能,如路由和邻居协议NETLINK_FIREWALL: 防火墙 (Netfilter)
使用 Netlink 套接字时,endpoints 通常由打开此套接字的进程ID (PID) 标识,0 表示内核。
可传送单播和多播消息:目的地可以使一个 PID,一个多播群组 ID 或两者组合。
Netlink 多播群组:
- 传出特定种类事件的通知信息,用户程序如果对这类通知信息感兴趣,可以向这些群组注册。
- include/linux/rtnetlink.h 中
PTMGRP_XXX
1 |
|
Netlink 的优点之一,内核可以启动传输,而不只是仅限于响应用户控件请求而返回数据。
配置改变串行化
每当应用配置改变时,内核中负责处理此事的例程都会取得一个信号量(rtnl_sem),以确保对存储网络配置内容的数据结构的访问具有互斥性。