全部博文(685)
分类: LINUX
2014-09-03 19:28:57
Sysctl:目录/proc/sys
在/proc/sys下看到的一个文件,实际都是一个内核变量。对于每个变量,内核可以将其放在/proc/sys的位置(与相同内核组件或功能相关联的变量通常都位于同一个目录中,如/proc/sys/net/ipv4目录中都是与ipv4相关的文件)、命名(多数时候文件名都是和相关联的内核变量相同的名字)、访问权限(如一个文件可以由任何人读,但只能由超级用户修改)。
输出到/proc/sys中的变量内容可借助相关联的文件进行读写或直接用sysctl系统调用。有些目录和文件在引导期间静态定义,而其他则是在允许期间添加的。当一个内核模块实现一项新功能或一个协议被加载或卸载时;当一个新的网络设备被注册或注销时。都有可能创建目录或文件。
/proc/sys中的文件和目录都是以ctl_table结构定义的。ctl_table结构的注册和注销是通过调用kernel/sysctl.c中定义的register_sysctl_table和unregister_sysctl_table:
struct ctl_table_header *register_sysctl_table(struct ctl_table *table);
void unregister_sysctl_table(struct ctl_table_header * header);
struct ctl_table
{
const char *procname; /* Text ID for /proc/sys, or zero */
void *data;
int maxlen;
mode_t mode;
struct ctl_table *child;
struct ctl_table *parent; /* Automatically set */
proc_handler *proc_handler; /* Callback for text formatting */
void *extra1;
void *extra2;
};
ctl_table的关键成员:
procname:在/proc/sys中所使用的文件名
maxlen:输出的内核变量的大小
mode:创建的/proc/sys猪相关文件或目录的访问权限
child:用于建立目录和文件直接的父子关系
proc_handler:当在/proc/sys中读取或写入一个文件时,完成读取或写入操作的函数。所有和文件相关联的ctl_instance都必须有proc_handler初始化,内内核会给目录分派一个默认值
extra1,extra2:可选参数,通常用于定义变量的最小值和最大值
根据与文件相关联的变量类型的不同,proc_handler所指的函数也不相同。
proc_dostring:读/写一个字符串
proc_dointvec:读写一个包含一个或多个整数的数组
proc_dointvec_minmax:同上,但是要确定输入数据在min/max范围内,不在该范围内的值会被拒绝
proc_dointvec_jiffies:读写一个整数数组,但此内核变量以jiffies为单位表示,在返回用户前会转化为秒数,写入前转化为jiffies
proc_dointvec_ms_jiffies:同上,只是这里是转化为毫秒数
proc_doulongvec_minmax:类似proc_dointvec_minmax,但其值为长整数。
proc_doulongvec_ms_jiffies_minmax:读取一个长整数数组。此内核变量以jiffies为单位,而用户空间已毫秒为单位。此值也必须指定min和max区间
ctl_table注册文件的结构实例:
static struct ctl_table ctl_forward_entry[] = {
{
.procname = "ip_forward", //文件名
.data = &ipv4_devconf.data[
IPV4_DEVCONF_FORWARDING - 1], //输出参数
.maxlen = sizeof(int), //参数大小
.mode = 0644, //文件权限
.proc_handler = devinet_sysctl_forward, //指定函数
.extra1 = &ipv4_devconf,
.extra2 = &init_net,
},
{ },
};
在这里还看不出这个文件在/proc/sys下的那个目录中。
这里是/proc/sys目录下建的默认目录:
static struct ctl_table root_table[] = {
{
.procname = "kernel",
.mode = 0555,
.child = kern_table,
},
{
.procname = "vm",
.mode = 0555,
.child = vm_table,
},
{
.procname = "fs",
.mode = 0555,
.child = fs_table,
},
{
.procname = "debug",
.mode = 0555,
.child = debug_table,
},
{
.procname = "dev",
.mode = 0555,
.child = dev_table,
},
{ }
};
目录不需要proc_handler函数指针但却有一个child字段。child是一个指针,指向另一个ctl_table结构,这个地址是ctl_table结构列表的头元素地址
在/proc/sys中注册文件
函数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[] = { //logging_level文件
.procname = "logging_level",
.data = &scsi_logging_level,
.maxlen = sizeof(scsi_logging_level),
.mode = 0644,
.proc_handler = proc_dointvec },
{ }
};
static ctl_table scsi_dir_table[] = { //scsi目录
{ .procname = "scsi",
.mode = 0555,
.child = scsi_table },
{ }
};
static ctl_table scsi_root_table[] = { //dev目录
{ .procname = "dev",
.mode = 0555,
.child = scsi_dir_table },
{ }
};
static struct ctl_table_header *scsi_table_header;
int __init scsi_init_sysctl(void) //注册
{
scsi_table_header = register_sysctl_table(scsi_root_table);
if (!scsi_table_header)
return -ENOMEM;
return 0;
}
void scsi_exit_sysctl(void) //注销
{
unregister_sysctl_table(scsi_table_header);
}
若需要添加多个文件到同一个目录下,可以定义一个模板,每次有新文件要添加到同一个目录下就重用。使用模板的好处是ctl_table结构只需要初始化一个便可贯穿整个目录。
下图显示了/proc/sys/net目录下的组织结构: