本文主要是介绍proc文件系统与ctl_table,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
三、系统参数数据结构的组织
系统参数的数据结构是按层次结构进行组织的,最高一层是root,下面的一层依次
kernel、net、proc、fs、debug、dev。它们下面再分层,例如net下面又分为core
、ipv4等,这样依次类推,直到最底层。
树中各个层次的结点,不管是父结点还是子结点,都用一个结构ctl_table组成的数
组来表示。所有的系统参数构成一个由结构ctrl_table描述的具有层次结构的树。
下面是ctl_table的定义:
struct ctl_table
{
>>>>结点标识,同一层的结点用不同的数字来标识
int ctl_name; /* Binary ID */
>>>>如果支持proc,则该结点在/proc/sys下面的proc项目名称
const char *procname; /* Text ID for /proc/sys, or zero */
>>>>实际的系统参数在内核中的数据结构
void *data;
int maxlen;
mode_t mode;
>>>>子结点的clt_table结构指针
ctl_table *child;
>>>>对/proc/sys下面的文件读写的时候将调用这个例程
proc_handler *proc_handler; /* Callback for text formatting */
>>>>用sysctl读写系统参数时候,将调用这个例程
ctl_handler *strategy; /* Callback function for all r/w */
>>>>指向在/proc/sys中的结点(proc文件系统数据结构)
struct proc_dir_entry *de; /* /proc control block */
void *extra1;
void *extra2;
};
父子结点之间层次的维护就是靠上面的child域。先看看所有系统参数在内核中的层
次结构:
struct ctl_table_header
{
ctl_table *ctl_table;
struct list_head ctl_entry;
};
ctl_table_header结构用来维护ctr_table树的双向链表表头.
static struct ctl_table_header root_table_header =
{ root_table, LIST_HEAD_INIT(root_table_header.ctl_entry) };
>>>>这里定义了root的ctl_table数组
static ctl_table root_table[] = {
{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
{CTL_VM, "vm", NULL, 0, 0555, vm_table},
{CTL_NET, "net", NULL, 0, 0555, net_table},
{CTL_PROC, "proc", NULL, 0, 0555, proc_table},
{CTL_FS, "fs", NULL, 0, 0555, fs_table},
{CTL_DEBUG, "debug", NULL, 0, 0555, debug_table},
{CTL_DEV, "dev", NULL, 0, 0555, dev_table},
{0}
};
>>>>下面是net_table数组
ctl_table net_table[] = {
{NET_CORE, "core", NULL, 0, 0555, core_table},
{NET_802, "802", NULL, 0, 0555, e802_table},
{NET_ETHER, "ethernet", NULL, 0, 0555, ether_table},
{NET_IPV4, "ipv4", NULL, 0, 0555, ipv4_table},
....................................................
{0}
};
上面的ctl_table都是目录结点,所以data域并不对应于内存中的一个数据结构,下面的
是子结点,它们的data域都对应内存中的一个数据结构,例如字符数组、整数等:
>>>>下面是ipv4_table
ctl_table ipv4_table[] = {
{NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps",
&sysctl_tcp_timestamps, sizeof(int), 0644, NULL,
&proc_dointvec},
........................
{NET_IPV4_FORWARD, "ip_forward",
&ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
&ipv4_sysctl_forward,&ipv4_sysctl_forward_strategy},
{NET_IPV4_DEFAULT_TTL, "ip_default_ttl",
&sysctl_ip_default_ttl, sizeof(int), 0644, NULL,
&proc_dointvec},
..........................
{0}
};
四、基本例程的分析
1.register_sysctl_table
内核导出了register_sysclt_table函数,通过它可以将一个ctl_table注册到系统参数
树中,先看看它的实现过程:
struct ctl_table_header *register_sysctl_table(ctl_table * table,
int insert_at_head)
{
struct ctl_table_header *tmp;
>>>>分配一个ctl_table_header结构
tmp = kmalloc(sizeof(struct ctl_table_header), GFP_KERNEL);
......................
tmp->ctl_table = table;
INIT_LIST_HEAD(&tmp->ctl_entry);
>>>>将其链接到list_head的双向链表中
if (insert_at_head)
list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);
else
list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
>>>>注册到/proc/sys中
#ifdef CONFIG_PROC_FS
>>>>系统初始化的时候,proc_sys_root = proc_mkdir("sys", 0);
>>>>添加一项/proc/sys目录项
register_proc_table(table, proc_sys_root);
#endif
return tmp;
}
2.register_proc_table
/* Scan the sysctl entries in table and add them all into /proc */
static void register_proc_table(ctl_table * table, struct proc_dir_entry *ro
ot)
{
struct proc_dir_entry *de;
int len;
mode_t mode;
for (; table->ctl_name; table++) {
/* Can't do anything without a proc name. */
if (!table->procname)
continue;
/* Maybe we can't do anything with it... */
>>>>如果没有处理句柄,而且为子结点,则处理ctl_table数组的下一个元素。
>>>>对子结点是一定要处理句柄的
if (!table->proc_handler && !table->child) {
printk(KERN_WARNING "SYSCTL: Can't register %s/n",
table->procname);
continue;
}
len = strlen(table->procname);
mode = table->mode;
de = NULL;
>>>>有处理句柄,为一个子结点
if (table->proc_handler)
mode |= S_IFREG;
else {
>>>>没有处理句柄,则为一个目录结点
mode |= S_IFDIR;
>>>>查找有没有匹配的项,有则退出循环
for (de = root->subdir; de; de = de->next) {
if (proc_match(len, table->procname, de))
break;
}
/* If the subdir exists already, de is non-NULL */
}
>>>>对子结点,在相应的proc目录下添加一项
if (!de) {
de = create_proc_entry(table->procname, mode, root);
if (!de)
continue;
>>>>proc数据结构的data域指向该ctl_table结构
de->data = (void *) table;
>>>>对于子结点,赋值proc的文件操作例程
if (table->proc_handler) {
de->proc_fops = &proc_sys_file_operations;
de->proc_iops = &proc_sys_inode_operations;
}
}
table->de = de;
>>>>如果为目录,则递归添加它下面的子结点
if (de->mode & S_IFDIR)
register_proc_table(table->child, de);
}
}
3. create_proc_entry
该函数创建一个proc_dir_entry结构,然后注册到/proc/sys目录下
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
struct proc_dir_entry *parent)
{
struct proc_dir_entry *ent = NULL;
const char *fn = name;
int len;
len = strlen(fn);
>>>>分配一个proc_dir_entry结构,名称由name指向,并放在该结构的后面
ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
if (!ent)
goto out;
memset(ent, 0, sizeof(struct proc_dir_entry));
>>>>填写名称
memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);
>>>>name域指向名称所在的内存地址
ent->name = ((char *) ent) + sizeof(*ent);
ent->namelen = len;
>>>>对目录结点
if (S_ISDIR(mode)) {
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO | S_IXUGO;
>>>>为proc赋文件操作接口例程
ent->proc_fops = &proc_dir_operations;
ent->proc_iops = &proc_dir_inode_operations;
ent->nlink = 2;
} else {
if ((mode & S_IFMT) == 0)
mode |= S_IFREG;
if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO;
ent->nlink = 1;
}
ent->mode = mode;
>>>>将这一项注册到proc中
proc_register(parent, ent);
out:
return ent;
}
4.proc_sys_file_operations中的处理例程
struct file_operations proc_sys_file_operations = {
read: proc_readsys,
write: proc_writesys,
};
它们都调用下面当局,不同在于第一个参数不同,对proc_readsys为0,对proc_write为
1.
static ssize_t do_rw_proc(int write, struct file * file, char * buf,
size_t count, loff_t *ppos)
{
struct proc_dir_entry *de;
struct ctl_table *table;
de = (struct proc_dir_entry*) file->f_dentry->d_inode->u.generic_ip;
if (!de || !de->data)
return -ENOTDIR;
table = (struct ctl_table *) de->data;
......................
>>>>最终调用了clt_table的处理例程。
error = (*table->proc_handler) (table, write, file, buf, &res);
}
五、sysctl的分析
extern asmlinkage long sys_sysctl(struct __sysctl_args *args)
{
struct __sysctl_args tmp;
>>>>将程序的__sysctl_args传到内核空间
>>>> struct __sysctl_args {
>>>> int *name;
>>>> int nlen;
>>>> void *oldval;
>>>> size_t *oldlenp;
>>>> void *newval;
>>>> size_t newlen;
>>>> unsigned long __unused[4];
>>>> };
if (copy_from_user(&tmp, args, sizeof(tmp)))
return -EFAULT;
error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
tmp.newval, tmp.newlen);
......................
}
sysctl调用do_sysctl:
int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp,
void *newval, size_t newlen)
{
struct list_head *tmp;
........................
tmp = &root_table_header.ctl_entry;
do {
>>>>根据指向ctl_table的指针tmp,得到指向ctl_table_head的指针
>>>>方法是将tmp指针向低地址移动,因为ctl_table在ctl_table_head的高地址部分
struct ctl_table_header *head =
list_entry(tmp, struct ctl_table_header, ctl_entry);
int error = parse_table(name, nlen, oldval, oldlenp,
newval, newlen, head->ctl_table,
&context);
.........................
tmp = tmp->next;
} while (tmp != &root_table_header.ctl_entry);
return -ENOTDIR;
}
static int parse_table(int *name, int nlen,
void *oldval, size_t *oldlenp,
void *newval, size_t newlen,
ctl_table *table, void **context)
{
int n;
>>>>下面的repeat根据name数组,按层次编历ctl_table树,从最高层一直到底层
>>>>直到找到匹配的子结点
repeat:
if (!nlen)
return -ENOTDIR;
>>>>取得名称数组中的一项
if (get_user(n, name))
return -EFAULT;
>>>>对同层次中的所有的结点,查找与n(name)相同的项
for ( ; table->ctl_name; table++) {
if (n == table->ctl_name || table->ctl_name == CTL_ANY) {
int error;
>>>>是目录,还需要继续编历,得到下层的结点后,跳转到repeat
if (table->child) {
if (ctl_perm(table, 001))
return -EPERM;
if (table->strategy) {
error = table->strategy(
table, name, nlen,
oldval, oldlenp,
newval, newlen, context);
if (error)
return error;
}
name++;
nlen--;
table = table->child;
goto repeat;
}
>>>>找到最终的子结点,则调用do_sysctl_strategy
error = do_sysctl_strategy(table, name, nlen,
oldval, oldlenp,
newval, newlen, context);
return error;
}
}
return -ENOTDIR;
}
int do_sysctl_strategy (ctl_table *table,
int *name, int nlen,
void *oldval, size_t *oldlenp,
void *newval, size_t newlen, void **context)
{
>>>>如果ctl_table定义了strategy处理例程,就调用它
if (table->strategy) {
rc = table->strategy(table, name, nlen, oldval, oldlenp,
newval, newlen, context);
if (rc < 0)
return rc;
if (rc > 0)
return 0;
}
>>>>如果是获取
copy_to_user(oldval, table->data, len))
>>>>如果是设置
copy_from_user(table->data, newval, len))
}
这篇关于proc文件系统与ctl_table的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!