sysctl是一種用戶應用來設置和獲得運行時內核的配置參數的一種有效方式,通過這種方式,用戶應用可以在內核運行的任何時刻來改變內核的配置參數,也可以在任何時候獲得內核的配置參數,通常,內核的這些配置參數也出現在proc文件系統的/proc/sys目錄下,用戶應用可以直接通過這個目錄下的文件來實現內核配置的讀寫操作,例如,用戶可以通過
cat /proc/sys/net/ipv4/ip_forward
來得知內核IP層是否允許轉發IP包,用戶可以通過
echo 1 > /proc/sys/net/ipv4/ip_forward
把內核 IP 層設置為允許轉發 IP 包,即把該機器配置成一個路由器或網關。 一般地,所有的 Linux 發布也提供了一個系統工具 sysctl,它可以設置和讀取內核的配置參數,但是該工具依賴于 proc 文件系統,為了使用該工具,內核必須支持 proc 文件系統。下面是使用 sysctl 工具來獲取和設置內核配置參數的例子:
# 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
注意,參數 net.ipv4.ip_forward 實際被轉換到對應的 proc 文件/proc/sys/net/ipv4/ip_forward,選項 -w 表示設置該內核配置參數,沒有選項表示讀內核配置參數,用戶可以使用 sysctl -a 來讀取所有的內核配置參數,對應更多的 sysctl 工具的信息,請參考手冊頁 sysctl(8)。
但是 proc 文件系統對 sysctl 不是必須的,在沒有 proc 文件系統的情況下,仍然可以,這時需要使用內核提供的系統調用 sysctl 來實現對內核配置參數的設置和讀取。
在源代碼中給出了一個實際例子程序,它說明了如何在內核和用戶態使用sysctl。頭文件 sysctl-exam.h 定義了 sysctl 條目 ID,用戶態應用和內核模塊需要這些 ID 來操作和注冊 sysctl 條目。內核模塊在文件 sysctl-exam-kern.c 中實現,在該內核模塊中,每一個 sysctl 條目對應一個 struct ctl_table 結構,該結構定義了要注冊的 sysctl 條目的 ID(字段 ctl_name),在 proc 下的名稱(字段procname),對應的內核變量(字段data,注意該該字段的賦值必須是指針),條目允許的最大長度(字段maxlen,它主要用于字符串內核變量,以便在對該條目設置時,對超過該最大長度的字符串截掉后面超長的部分),條目在proc文件系統下的訪問權限(字段mode),在通過 proc設置時的處理函數(字段proc_handler,對于整型內核變量,應當設置為&proc_dointvec,而對于字符串內核變量,則設置為 &proc_dostring),字符串處理策略(字段strategy,一般這是為&sysctl_string)。
sysctl 條目可以是目錄,此時 mode 字段應當設置為 0555,否則通過 sysctl 系統調用將無法訪問它下面的 sysctl 條目,child 則指向該目錄條目下面的所有條目,對于在同一目錄下的多個條目,不必一一注冊,用戶可以把它們組織成一個 struct ctl_table 類型的數組,然后一次注冊就可以,但此時必須把數組的最后一個結構設置為NULL,即
{
??????? .ctl_name = 0
}
注冊sysctl條目使用函數register_sysctl_table(struct ctl_table *, int),第一個參數為定義的struct ctl_table結構的sysctl條目或條目數組指針,第二個參數為插入到sysctl條目表中的位置,如果插入到末尾,應當為0,如果插入到開頭,則為非0。內核把所有的sysctl條目都組織成sysctl表。
當模塊卸載時,需要使用函數unregister_sysctl_table(struct ctl_table_header *)解注冊通過函數register_sysctl_table注冊的sysctl條目,函數register_sysctl_table在調用成功時返 回結構struct ctl_table_header,它就是sysctl表的表頭,解注冊函數使用它來卸載相應的sysctl條目。 用戶態應用sysctl-exam-user.c通過sysctl系統調用來查看和設置前面內核模塊注冊的sysctl條目(當然如果用戶的系統內核已經支持proc文件系統,可以直接使用文件操作應用如cat, echo等直接查看和設置這些sysctl條目)。
下面是作者運行該模塊與應用的輸出結果示例:
# 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
內核模塊代碼??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);
}
?
評論
查看更多