- 论坛徽章:
- 0
|
qiuhan
2007-7-20
1 重要数据结构
sys/sysctl.h
kern/kern_sysctl.c
SET_DECLARE(sysctl_set, struct sysctl_oid);
SYSINIT(sysctl, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_all, 0);
static SYSCTL_NODE(_sysctl, 1, name, CTLFLAG_RD, sysctl_sysctl_name, "");
全局变量sysctl__children,保存MIB树。其结构为:
sle_next指向兄弟节点, void指针oid_arg1指向子节点。我们总是从sysctl__children开始遍历MIB树,
不匹配时继续访问sle_next,匹配时访问其子节点,直到节点为空或者匹配节点数达到预定值或者遇到
oid_handler不为空的非叶子节点,然后调用该函数。
一般而言,非叶子节点(NODE)的oid_handler都为空,除非它想用oid_number序列来传递参数。
例如_kern_proc下的很多NODE都有oid_handler,用最后的oid_number来传递pid. 还有_sysctl下的NODE
(gdb) p *sysctl__children->slh_first
$72 = {oid_parent = 0xc0a13c40, oid_link = {sle_next = 0xc09a5f20}, oid_number = 0, oid_kind = 3221225473,
oid_arg1 = 0xc0a12244, oid_arg2 = 0, oid_name = 0xc0934198 "sysctl", oid_handler = 0, oid_fmt = 0xc0900a9e "N",
oid_refcnt = 0, oid_descr = 0xc092d95d "Sysctl internal magic"}
(gdb) p *sysctl__children->slh_first->oid_link->sle_next
$73 = {oid_parent = 0xc0a13c40, oid_link = {sle_next = 0xc09a5f60}, oid_number = 1, oid_kind = 3221225473,
oid_arg1 = 0xc0a12240, oid_arg2 = 0, oid_name = 0xc092d973 "kern", oid_handler = 0, oid_fmt = 0xc0900a9e "N",
oid_refcnt = 0, oid_descr = 0xc092d978 "High kernel, proc, limits &c"}
(gdb) p *sysctl__children->slh_first->oid_link->sle_next->oid_link->sle_next
$74 = {oid_parent = 0xc0a13c40, oid_link = {sle_next = 0xc09a5fa0}, oid_number = 2, oid_kind = 3221225473,
oid_arg1 = 0xc0a12248, oid_arg2 = 0, oid_name = 0xc0942340 "vm", oid_handler = 0, oid_fmt = 0xc0900a9e "N",
oid_refcnt = 0, oid_descr = 0xc092d995 "Virtual memory"}
(gdb) p *((struct sysctl_oid_list *)(sysctl__children->slh_first->oid_arg1))->slh_first
$78 = {oid_parent = 0xc0a12244, oid_link = {sle_next = 0xc09a8b40}, oid_number = 1, oid_kind = 2147483649,
oid_arg1 = 0xc0a13ca0, oid_arg2 = 0, oid_name = 0xc093b71a "name", oid_handler = 0xc068c030 ,
oid_fmt = 0xc0900a9e "N", oid_refcnt = 0, oid_descr = 0xc0924c8d ""}
/*
* This describes the access space for a sysctl request. This is needed
* so that we can use the interface from the kernel or from user-space.
*/
struct sysctl_req {
struct thread *td; /* used for access checking */
int lock; /* locking/wiring state */
//缓冲指针,用于传递__sysctl运行的结果
void *oldptr;
size_t oldlen; //oldptr大小,由调用者传入,内核会判断该区间的读写属性
size_t oldidx; //已copy大小,copy过程可能多次
//为sysctl_old_kernel或者sysctl_old_user,负责写入oldptr
int (*oldfunc)(struct sysctl_req *, const void *, size_t);
void *newptr;
size_t newlen;
size_t newidx;
//为sysctl_new_kernel或者sysctl_new_user,负责把newptr拷入内核
int (*newfunc)(struct sysctl_req *, void *, size_t);
size_t validlen;//用于记录写入oldptr的大小
int flags;
};
由于存在kernel_sysctl和userland_sysctl,所以会有这两种函数
SLIST_HEAD(sysctl_oid_list, sysctl_oid);
/*
* This describes one "oid" in the MIB tree. Potentially more nodes can
* be hidden behind it, expanded by the handler.
*/
struct sysctl_oid {
struct sysctl_oid_list *oid_parent;
SLIST_ENTRY(sysctl_oid) oid_link;
int oid_number;
u_int oid_kind;
void *oid_arg1;
int oid_arg2;
const char *oid_name;
int (*oid_handler)(SYSCTL_HANDLER_ARGS);
const char *oid_fmt;
int oid_refcnt;
const char *oid_descr;
};
2 __sysctl 功能
struct sysctl_args {
int *name;
u_int namelen;
void *old;
size_t *oldlenp;
void *new;
size_t newlen;
};
其中,name为oid_number的整型数组,namelen为该数组长度.
__sysctl按照name遍历sysctl__children,直到第一个oid_handler不为空的
节点(可以为非叶子节点),然后以name后面未用的元素、长度以及req(包含old,
oldlenp,new和newlen作为参数调用oid_handler指向的函数。
例1:
int
sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
int name2oid_oid[2];
int real_oid[CTL_MAXNAME+2];
int error;
size_t oidlen;
name2oid_oid[0] = 0;
name2oid_oid[1] = 3;
oidlen = sizeof(real_oid);
//依次找到的节点为sysctl,及子节点name2oid,然后调用oid_handler指向的函数
//sysctl_sysctl_name2oid,获得name
//对应的oid序列429, 411, 820, 823, oidlen=16
error = sysctl(name2oid_oid, 2, real_oid, &oidlen, (void *)name,
strlen(name));
if (error : 0xc0a508c0
(gdb) x/x 0xc0a48014
0xc0a48014 : 0xc0a50c00
(gdb) x/x 0xc0a4907c
0xc0a4907c : 0xc0a7ffcc
从上面我们可以分析得出:
1 &__start_set_sysctl_set 和 &__stop_set_sysctl_set分别标识set_sysctl_set段的开始和结束
大小为106c, (共有1051个oid), 所以代码中有
#define SET_BEGIN(set) \
(&__CONCAT(__start_set_,set))
#define SET_LIMIT(set) \
(&__CONCAT(__stop_set_,set))
#define SET_FOREACH(pvar, set) \
for (pvar = SET_BEGIN(set); pvar < SET_LIMIT(set); pvar++)
2 set_sysctl_set段中保存的只是指针,而具体的值却是在.data段中。例如,我们可以看到0xc0a48010指向的
是0xc0a508c0,而该地址位于.data段中.
3 我们并没有看到显示的对__start_set_sysctl_set 或者 __stop_set_sysctl_set进行赋值,很可能是在
内核链接时得到的[FIXME]
(gdb) macro expand TUNABLE_INT("hw.ata.ata_dma", &ata_dma);
expands to:
static struct tunable_int __tunable_int___LINE__ = {("hw.ata.ata_dma"), (&ata_dma),};
static struct sysinit __Tunable_init___LINE___sys_init = {
SI_SUB_TUNABLES, SI_ORDER_MIDDLE,
(sysinit_cfunc_t) (sysinit_nfunc_t) tunable_int_init,
((void *)(&__tunable_int___LINE__))};
static void const *const __set_sysinit_set_sym___Tunable_init___LINE___sys_init
__attribute__((__section__("set_" "sysinit_set")))
__attribute__((__used__)) = &__Tunable_init___LINE___sys_init;
初始化ata_dma的值.
在loader过程中会根据loader.conf等文件初始化一些环境变量,放在envp(参见loader),
其格式为(以0分割,以00结束):
name1=value10name2=value20...00
(gdb) p kern_envp
$24 = 0xc0eb5000 "LINES=24"
(gdb) x/s 0xc0eb5009
0xc0eb5009: "acpi_load=YES"
(gdb) x/s 0xc0eb5017
0xc0eb5017: "bootfile=kernel"
该地址在init386中传递给内核:
kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE;
在mi_startup中调用init_dynamic_kenv初始化动态内核环境变量列表kenvp
kenvp可以通过kenv(通过setenv, unsetenv)系统调用增加或减少
TUNABLE_INT通过在mi_startup中tunable_int_init,进而调用getenv_int((path), (var))
查找kenvp列表得到"hw.ata.ata_dma"的值,如果没有找到,则赋值为0
(gdb) bt
#0 name2oid (name=0xc24062e0 "security", oid=0xc89c5b58, len=0xc89c5b4c, oidpp=0xc89c5b50)
at ../../../kern/kern_sysctl.c:696
#1 0xc068c4b1 in sysctl_sysctl_name2oid (oidp=0xc09a8b80, arg1=0x0, arg2=0, req=0xc89c5c04)
at ../../../kern/kern_sysctl.c:736
#2 0xc068cd3b in sysctl_root (oidp=0x0, arg1=0x0, arg2=0, req=0xc89c5c04) at ../../../kern/kern_sysctl.c:1281
#3 0xc068cf3c in userland_sysctl (td=0xc89c5b4c, name=0xc89c5c74, namelen=2, old=0xc89c5c04, oldlenp=0xbfbfe998,
inkernel=0, new=0x280cc499, newlen=3365690188, retval=0xc89c5c70, flags=-929277108) at ../../../kern/kern_sysctl.c:1380
#4 0xc068cddf in __sysctl (td=0xc23e6300, uap=0xc89c5d04) at ../../../kern/kern_sysctl.c:1315
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/41770/showart_452854.html |
|