开源中文网

您的位置: 首页 > 编程开发 > C语言 > 正文

关于sysctl系统调用

来源:  作者:

sysctl系统调用用于读写内核参数,比如主机名,最大可打开的文件数目等等的信息。其函数原型是: 
#include <unistd.h> 
#include <linux/unistd.h> 
#include <linux/sysctl.h> 

_syscall1(int, _sysctl, struct __sysctl_args *, args); 
int _sysctl(struct __sysctl_args *args); 
syscall1是一个宏,展开为一个嵌入汇编函数,用于定义_sysctl函数,具体不再展开。关于内核参数的具体项及其值,我们可以查看 /proc/sys目录(如果内核配置了CONFIG_PROC_FS的话)。以下是我的机器上/proc/sys内容(全部为目录项): 
debug dev fs kernel net proc sunrpc vm 
一个目录代表一种类型的内核参数,比如fs目录下全部是文件系统相关的内核参数,而net下则全部是网络相关的内核参数。在这些目录下还有子目录,比如net目录下: 
core ethernet ipv4 ipv6 token-ring unix 
所有的内核参数在/proc/sys形成一个树状结构。 
以上是内核参数在/proc/sys目录下的表示,对于这些参数,最简单地,我们通过文件系统的基本操作就可完成对其的读写。下面讲讲这些参数在内核代码中的表示以及相应地如何通过sysctl系统调用在内核代码中进行读写。 
在内核中,这个树状结构的每个节点(包括枝节点和叶节点)都是通过一个结构体struct ctl_table来表示的,每个节点都有一个数值型的ID,来唯一标识这个节点,因为是树状结构,所以这个ID不必唯一,但通过ID从根节点开始的搜索路径必须唯一。同时,每个节点都有一个文本ID,就是该节点在/proc/sys中的目录名(文件名)。同时,有一个指向子节点的指针。 
root_table[]是一个数组,含有所有第一级子目录节点。第一级子目录节点对应的数组分别是:kern_table[],vm_table[], net_table[],proc_table[],fs_table[],debug_table[],dev_table[]。 
所有的这些数组(包括你将要新添加的数组)都以一个list的形式组织在一个变量root_table_header中。 root_table_header不体现树型结构,树型结构是通过struct ctl_table的指向子节点的指针,或者数组中的root_dir项来体现。 
我们现在看一下kern_table[]数组中的两个子节点:ostype,osrelease。在/proc/sys目录中,我们可以直接得到它们的文件内容,在我的安装有FC3的系统中,得到内容如下: 
[helq@localhost kernel]$ cat osrelease 
2.6.12-1.1381_FC3 
[helq@localhost kernel]$ cat ostype 
Linux 
这两个子节点的数值型ID分别是:KERN_OSTYPE,KERN_OSRELEASE,它们的父节点的数值型ID是CTL_KERN。我们通过sysctl系统调用得到这两个值的代码如下: 
#include <linux/unistd.h> 
#include <linux/types.h> 
#include <linux/sysctl.h> 
#include <stdio.h> 

_syscall1(int, _sysctl, struct __sysctl_args *, args); 
int sysctl(int *name, int nlen, void *oldval, size_t *oldlenp, 
void *newval, size_t newlen) 

struct __sysctl_args args={name,nlen,oldval,oldlenp,newval,newlen}; 
return _sysctl(&args); 


#define SIZE(x) sizeof(x)/sizeof(x[0]) 
#define OSNAMESZ 100 
#define OSRELEASESZ 256 

char osname[OSNAMESZ]; 
char osrelease[OSRELEASESZ]; 
int osnamelth; 
int name[] = { CTL_KERN, KERN_OSTYPE }; 
int osreleaseth; 
int release[] = { CTL_KERN, KERN_OSRELEASE }; 
main(){ 
osnamelth = sizeof(osname); 
if (sysctl(name, SIZE(name), osname, &osnamelth, 0, 0)){ 
perror("sysctl"); 
return -1; 

osreleaseth = sizeof(osrelease); 
if(sysctl(release, SIZE(release), osrelease, &osreleaseth, 0, 0)){ 
perror("sysctl"); 
return -1; 


printf("This machine is running %s %s\n", osname, osrelease); 
return 0; 

下面是在安装有FC5的虚拟机上的运行结果: 
This machine is running Linux 2.6.16-1.2080_FC5.stk16 
sysctl系统调用调用到的内核函数是sys_sysctl,该函数调用do_sysctl,do_sysctl首先检查struct __sysctl_args.nlen的值,保证树的深度不超过10,然后通过调用parse_table函数,匹配root_table_header 中每一个数组的每一项的ctl_name,匹配到后,会通过ctl_table.child指针,进行子项的逐步匹配。 
如果匹配完成,则结构体struct ctl_table有一个成员函数strategy会被调用到(如果存在),以上面的例子程序为例,该程序取操作系统的类型与版本,都是字符型数据,所以在叶子节点KERN_OSTYPE, KERN_OSRELEASE上都有strategy成员存在,其作用是把struct ctl_table.data中的节点数据保存到传入的参数oldval中,并把传入的参数newval保存到struct ctl_table.data中(如果需要的话),因为非叶节点是没有数据的,所以非员节点的strategy成员值为NULL。 
匹配完成后,最后的读写在do_sysctl_strategy中完成。

Tags:系统
关于开源中文网 - 联系我们 - 广告服务 - 网站地图 - 版权声明