sysctl是一種用戶應(yīng)用來設(shè)置和獲得運行時內(nèi)核的配置參數(shù)的一種有效方式,通過這種方式,用戶應(yīng)用可以在內(nèi)核運行的任何時刻來改變內(nèi)核的配置參數(shù),也可以在任何時候獲得內(nèi)核的配置參數(shù),通常,內(nèi)核的這些配置參數(shù)也出現(xiàn)在proc文件系統(tǒng)的/proc/sys目錄下,用戶應(yīng)用可以直接通過這個目錄下的文件來實現(xiàn)內(nèi)核配置的讀寫操作,例如,用戶可以通過
cat /proc/sys/net/ipv4/ip_forward
來得知內(nèi)核IP層是否允許轉(zhuǎn)發(fā)IP包,用戶可以通過
echo 1 > /proc/sys/net/ipv4/ip_forward
把內(nèi)核 IP 層設(shè)置為允許轉(zhuǎn)發(fā) IP 包,即把該機器配置成一個路由器或網(wǎng)關(guān)。 一般地,所有的 Linux 發(fā)布也提供了一個系統(tǒng)工具 sysctl,它可以設(shè)置和讀取內(nèi)核的配置參數(shù),但是該工具依賴于 proc 文件系統(tǒng),為了使用該工具,內(nèi)核必須支持 proc 文件系統(tǒng)。下面是使用 sysctl 工具來獲取和設(shè)置內(nèi)核配置參數(shù)的例子:
# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0
# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
注意,參數(shù) net.ipv4.ip_forward 實際被轉(zhuǎn)換到對應(yīng)的 proc 文件/proc/sys/net/ipv4/ip_forward,選項 -w 表示設(shè)置該內(nèi)核配置參數(shù),沒有選項表示讀內(nèi)核配置參數(shù),用戶可以使用 sysctl -a 來讀取所有的內(nèi)核配置參數(shù),對應(yīng)更多的 sysctl 工具的信息,請參考手冊頁 sysctl(8)。
但是 proc 文件系統(tǒng)對 sysctl 不是必須的,在沒有 proc 文件系統(tǒng)的情況下,仍然可以,這時需要使用內(nèi)核提供的系統(tǒng)調(diào)用 sysctl 來實現(xiàn)對內(nèi)核配置參數(shù)的設(shè)置和讀取。
在源代碼中給出了一個實際例子程序,它說明了如何在內(nèi)核和用戶態(tài)使用sysctl。頭文件 sysctl-exam.h 定義了 sysctl 條目 ID,用戶態(tài)應(yīng)用和內(nèi)核模塊需要這些 ID 來操作和注冊 sysctl 條目。內(nèi)核模塊在文件 sysctl-exam-kern.c 中實現(xiàn),在該內(nèi)核模塊中,每一個 sysctl 條目對應(yīng)一個 struct ctl_table 結(jié)構(gòu),該結(jié)構(gòu)定義了要注冊的 sysctl 條目的 ID(字段 ctl_name),在 proc 下的名稱(字段procname),對應(yīng)的內(nèi)核變量(字段data,注意該該字段的賦值必須是指針),條目允許的最大長度(字段maxlen,它主要用于字符串內(nèi)核變量,以便在對該條目設(shè)置時,對超過該最大長度的字符串截掉后面超長的部分),條目在proc文件系統(tǒng)下的訪問權(quán)限(字段mode),在通過 proc設(shè)置時的處理函數(shù)(字段proc_handler,對于整型內(nèi)核變量,應(yīng)當(dāng)設(shè)置為&proc_dointvec,而對于字符串內(nèi)核變量,則設(shè)置為 &proc_dostring),字符串處理策略(字段strategy,一般這是為&sysctl_string)。
sysctl 條目可以是目錄,此時 mode 字段應(yīng)當(dāng)設(shè)置為 0555,否則通過 sysctl 系統(tǒng)調(diào)用將無法訪問它下面的 sysctl 條目,child 則指向該目錄條目下面的所有條目,對于在同一目錄下的多個條目,不必一一注冊,用戶可以把它們組織成一個 struct ctl_table 類型的數(shù)組,然后一次注冊就可以,但此時必須把數(shù)組的最后一個結(jié)構(gòu)設(shè)置為NULL,即
{
??????? .ctl_name = 0
}
注冊sysctl條目使用函數(shù)register_sysctl_table(struct ctl_table *, int),第一個參數(shù)為定義的struct ctl_table結(jié)構(gòu)的sysctl條目或條目數(shù)組指針,第二個參數(shù)為插入到sysctl條目表中的位置,如果插入到末尾,應(yīng)當(dāng)為0,如果插入到開頭,則為非0。內(nèi)核把所有的sysctl條目都組織成sysctl表。
當(dāng)模塊卸載時,需要使用函數(shù)unregister_sysctl_table(struct ctl_table_header *)解注冊通過函數(shù)register_sysctl_table注冊的sysctl條目,函數(shù)register_sysctl_table在調(diào)用成功時返 回結(jié)構(gòu)struct ctl_table_header,它就是sysctl表的表頭,解注冊函數(shù)使用它來卸載相應(yīng)的sysctl條目。 用戶態(tài)應(yīng)用sysctl-exam-user.c通過sysctl系統(tǒng)調(diào)用來查看和設(shè)置前面內(nèi)核模塊注冊的sysctl條目(當(dāng)然如果用戶的系統(tǒng)內(nèi)核已經(jīng)支持proc文件系統(tǒng),可以直接使用文件操作應(yīng)用如cat, echo等直接查看和設(shè)置這些sysctl條目)。
下面是作者運行該模塊與應(yīng)用的輸出結(jié)果示例:
# insmod ./sysctl-exam-kern.ko
# cat /proc/sys/mysysctl/myint
0
# cat /proc/sys/mysysctl/mystring
# ./sysctl-exam-user
mysysctl.myint = 0
mysysctl.mystring = ""
# ./sysctl-exam-user 100 "Hello, World"
old value: mysysctl.myint = 0
new value: mysysctl.myint = 100
old vale: mysysctl.mystring = ""
new value: mysysctl.mystring = "Hello, World"
# cat /proc/sys/mysysctl/myint
100
# cat /proc/sys/mysysctl/mystring
Hello, World
#
示例:
頭文件:sysctl-exam.h:
//header: sysctl-exam.h
#ifndef _SYSCTL_EXAM_H
#define _SYSCTL_EXAM_H
#include
#define MY_ROOT (CTL_CPU + 10)
#define MY_MAX_SIZE 256
enum {
MY_INT_EXAM = 1,
MY_STRING_EXAM = 2,
};
#endif
內(nèi)核模塊代碼??sysctl-exam-kern.c:
//kernel module: sysctl-exam-kern.c
#include
#include
#include
#include "sysctl-exam.h"
static char mystring[256];
static int myint;
static struct ctl_table my_sysctl_exam[] = {
{
.ctl_name = MY_INT_EXAM,
.procname = "myint",
.data = &myint,
.maxlen = sizeof(int),
.mode = 0666,
.proc_handler = &proc_dointvec,
},
{
.ctl_name = MY_STRING_EXAM,
.procname = "mystring",
.data = mystring,
.maxlen = MY_MAX_SIZE,
.mode = 0666,
.proc_handler = &proc_dostring,
.strategy = &sysctl_string,
},
{
.ctl_name = 0
}
};
static struct ctl_table my_root = {
.ctl_name = MY_ROOT,
.procname = "mysysctl",
.mode = 0555,
.child = my_sysctl_exam,
};
static struct ctl_table_header * my_ctl_header;
static int __init sysctl_exam_init(void)
{
my_ctl_header = register_sysctl_table(&my_root, 0);
return 0;
}
static void __exit sysctl_exam_exit(void)
{
unregister_sysctl_table(my_ctl_header);
}
module_init(sysctl_exam_init);
module_exit(sysctl_exam_exit);
MODULE_LICENSE("GPL");
用戶程序 sysctl-exam-user.c:
//application: sysctl-exam-user.c
#include
#include
#include
#include "sysctl-exam.h"
#include
#include
_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
int oldmyint;
int oldmyintlen;
int newmyint;
int newmyintlen;
char oldmystring[MY_MAX_SIZE];
int oldmystringlen;
char newmystring[MY_MAX_SIZE];
int newmystringlen;
int myintctl[] = {MY_ROOT, MY_INT_EXAM};
int mystringctl[] = {MY_ROOT, MY_STRING_EXAM};
int main(int argc, char ** argv)
{
if (argc < 2)
{
oldmyintlen = sizeof(int);
if (sysctl(myintctl, SIZE(myintctl), &oldmyint, &oldmyintlen, 0, 0)) {
perror("sysctl");
exit(-1);
}
else {
printf("mysysctl.myint = %d\n", oldmyint);
}
oldmystringlen = MY_MAX_SIZE;
if (sysctl(mystringctl, SIZE(mystringctl), oldmystring, &oldmystringlen, 0, 0)) {
perror("sysctl");
exit(-1);
}
else {
printf("mysysctl.mystring = "%s"\n", oldmystring);
}
}
else if (argc != 3)
{
printf("Usage:\n");
printf("\tsysctl-exam-user\n");
printf("Or\n");
printf("\tsysctl-exam-user aint astring\n");
}
else
{
newmyint = atoi(argv[1]);
newmyintlen = sizeof(int);
oldmyintlen = sizeof(int);
strcpy(newmystring, argv[2]);
newmystringlen = strlen(newmystring);
oldmystringlen = MY_MAX_SIZE;
if (sysctl(myintctl, SIZE(myintctl), &oldmyint, &oldmyintlen, &newmyint, newmyintlen)) {
perror("sysctl");
exit(-1);
}
else {
printf("old value: mysysctl.myint = %d\n", oldmyint);
printf("new value: mysysctl.myint = %d\n", newmyint);
}
if (sysctl(mystringctl, SIZE(mystringctl), oldmystring, &oldmystringlen, newmystring, newmystringlen))
{
perror("sysctl");
exit(-1);
}
else {
printf("old vale: mysysctl.mystring = "%s"\n", oldmystring);
printf("new value: mysysctl.mystring = "%s"\n", newmystring);
}
}
exit(0);
}
?
評論
查看更多