我们首先看文件和目录所用的ctl_table结构的初始化是什么,然后再看其如何使用。
/proc/sys/net/ipv4/default/forwarding文件所用的ctl_table实体的初始化定义在net/ipv4/devinet.c中,如下所示,其用法在第三十六章予以描述。
{
ctl_name =
.procname
.data
.maxlen
.mode
.proc_handler
}
从上述的简单介绍看不出这个文件被放在/proc/sys中的何处,等一下你会看到如何找到这些信息。从代码可知,该文件名称为forwarding,通过forwarding文件输出其值的内核变量是ipv4_devconf.forwarding ,参数声明为整数,该文件的访问权是0644,而proc_handler函数被初始化位devint_sysctl_forward
现在,我们看kernel/sysctl,c中的一个目录的声明实例
{
ctl_name
procname
mode
child
}
这个ctl_table 实体定义了/proc/sys/net目录,不需要proc_handler默认函数,适用于所有的目录的需要,但是却有一个child字段,child是一个指针,指向另一个ctl_table实体,这个实体只是ctl_table实体列表的头元素。
在/proc/sys 中注册文件
我们知道可以分别用register_sysctl_table和unregister_sysctl_table 在/proc/sys 中注册或除名文件。注册函数需要两个输入参数。
指向一个ctl_table实体的指针
一个标识,指出新元素应该放在位于相同目录中ctl_table 实体列表的何处,头或者尾。
注意,register_sysctl_table的输入并不包括输入参数ctl_table应该添加到/proc/sys文件系统中何处的参考值。原因在于所有的插入都是针对/proc/sys目录进行,如果你想要把一个文件注册到/proc/sys 的子目录,就必须建立一棵树(意味着多个child字段链接的ctl_table实体)以提供完整路径,然后把代表你刚建的树根的ctl_table实体传给register_sysctl_table, 当该树的任何节点尚未存在时,就会被创建。
我们看两个实例,先从简单的着手,下面这段代码源自drivers/scsi/scsi_sysctl.c 显示出文件logging_level 的定义以及如何置放到/proc/sys/dev/scsi 目录。
static ctl_table scsi_table[] = {
.ctl_name = DEV_SCSI_LOGGING_LEVEL,
.proname = "logging_level"
.data = &scsi_logging_level,
.maxlen = sizeof(scsi_logging_level)
.proc_handler = &proc_dointvec
};
static ctl_table scsi_dir_table[]={
.ctl_name = DEV_SCSI
.proname = "scsi"
.mode = 0555
.child = scsi_table
}
static ctl_table scsi_root_table[] = {
ctl_name = CTL_DEV,
.proname = "dev"
.mode = 0555
.child = scsi_dir_table
};
int __init scsi_init_sysctl(void)
{
scsi_table_header = register_sysctl_table(scsi_root_table, 1);
}
注意,register_sysctl_table接收的scsi_root_table, 也就是代码中定义的ctl_table树的根。
还注意到,如果稍后你想把另一个文件添加到同一个目录,例如abc文件,你需要定义一颗类似的树,也就是dev和scsi目录是两个相同的ctl_table实体,为新文件abc另加一个新的ctl_table实体。
proc
sys
dev
scsi
logging_level
scsi_root_table
.proname = dev
.child = scsi_dir_table
.proname = scsi
.child = scsi_table
.proname = logging_level
struct ctl_table的实体
有时候,开发人员为了简化把新文件添加到已经存在的目录而定义一个模版,然后,每次有新文件要添加到相同目录就予以重用,使用模版的好处是ctl_table实体只需要初始化一次便可以贯穿整个目录。之后,每次增加一个新文件时,只需要对叶节点初始化,例如,你可以去看一看邻居子系统如何使用在net/core/neighbour.c 中的neigh_sysctl_register定义neigh_sysctl_template 。