1. 程式人生 > 其它 >的write方法有哪些引數_一文讀懂 Linux 核心執行時引數配置

的write方法有哪些引數_一文讀懂 Linux 核心執行時引數配置

技術標籤:的write方法有哪些引數

0e076e81fc108d9d1acd02b2812dc477.gif

作者簡介:Daemon.Wu, Linux 核心效能優化工程師,就職於某微小手機廠從事手機效能優化。座右銘:知行合一。

版權宣告:本文最先發表於“泰曉科技” 微信公眾號,歡迎轉載,轉載時請在文章的開頭保留本宣告。

Linux 核心執行時配置簡介

Linux 核心的子系統有各種配置引數,比如記憶體管理中記憶體回收的水位資訊,CPU 排程中的各種排程器配置資訊,檔案回寫中 dirty page 的配置等。

無需修改核心原始碼,使用者就可以通過 sysctl 命令在執行時設定這些引數。

具體的配置引數可以檢視核心文件目錄 Documentation/sysctl/

下的各個檔案。

sysctl 命令配置舉例

比如,設定記憶體子系統的 dropcaches 引數,可使用:

$systcl-wvm.drop_caches=1

sysctl 命令使用了哪些介面

使用 strace 追蹤 sysctl 命令的系統呼叫可發現該命令最終是訪問 /proc/sys/drop_caches 這個檔案。

[email protected]:~$sudostracesysctl-wvm.drop_caches=1
execve("/sbin/sysctl",["sysctl","-w","vm.drop_caches=1"],[/*16vars*/])=0
brk(NULL)=0x2016000
...
stat("/proc/sys/vm/drop_caches",{st_mode=S_IFREG|0200,st_size=0,...})=0
open("/proc/sys/vm/drop_caches",O_WRONLY|O_CREAT|O_TRUNC,0666)=3
fstat(3,{st_mode=S_IFREG|0200,st_size=0,...})=0
write(3,"1\n",2)=2
close(3)=0
fstat(1,{st_mode=S_IFCHR|0620,st_rdev=makedev(136,1),...})=0
write(1,"vm.drop_caches=1\n",19vm.drop_caches=1
)=19
close(1)=0
close(2)=0
exit_group(0)=?
+++exitedwith0+++

由此就可知,systcl 命令是通過 /proc/sys/ 目錄下的各個介面檔案實現配置的。該目錄下包含以下子目錄:

[email protected]:/proc/sys$tree-L1
.
├──abi
├──debug
├──dev#裝置相關資訊
├──fs#特定的檔案系統,比如fd,inode,dentry,quotatuning
├──kernel#tuning全域性引數,比如cpu排程,printk,softirq,hung_task,numa,watchdog等
├──net#網路子系統相關引數,比如ipv4,ipv6,icmp,igmp等
└──vm#tuning記憶體管理相關引數,buffer和cache的管理

sysctl 介面暨 procfs 工作流程

那麼在核心中各子系統是如何匯出這些引數到 procfs,並允許使用者通過 echo, cat 等工具操作這些節點來設定引數的呢?

kernel/sysctl.c 中定義了某個子系統下的某個引數的相關 ctl_table,比如 vm.dropcaches

  • 先設定 vm 目錄的引數,訪問許可權為 555,並設定 child 屬性為 vm_table
  • vm_table 結構體陣列包含了 VM 子系統的引數,比如 dropcaches 引數,設定了該節點的訪問許可權為 644;data 屬性值為 sysctl_drop_caches,該變數在 fs/drop_caches.c 中定義;
  • 該節點的讀寫處理函式 drop_caches_sysctl_hander,在 fs/drop_caches.c 中實現,通過 dointvec_minmax 來讀出資料 。
  • 最後填充好 ctl_table 結構體後在 sysctl_init 入口函式註冊這些結構體陣列。

相關程式碼如下:

/*Thedefaultsysctltables:*/
staticstructctl_tablesysctl_base_table[]={
{
.procname="kernel",///proc/sys/kernel
.mode=0555,
.child=kern_table,
},
{
.procname="vm",///proc/sys/vm
.mode=0555,
.child=vm_table,
},
{
.procname="fs",///proc/sys/fs
.mode=0555,
.child=fs_table,
},
{
.procname="debug",///proc/sys/debug
.mode=0555,
.child=debug_table,
},
{
.procname="dev",///proc/sys/dev
.mode=0555,
.child=dev_table,
},
{}
};

staticstructctl_tablevm_table[]={
...
{
.procname="drop_caches",
.data=&sysctl_drop_caches,
.maxlen=sizeof(int),//vm.drop_caches變數4各位元組
.mode=0644,///proc/sys/vm/drop_caches訪問許可權"644"
.proc_handler=drop_caches_sysctl_handler,//handler
.extra1=&one,
.extra2=&four,
},
...
};

int__initsysctl_init(void)
{
structctl_table_header*hdr;

//註冊ctl_table
hdr=register_sysctl_table(sysctl_base_table);
kmemleak_not_leak(hdr);

return0;
}

intdrop_caches_sysctl_handler(structctl_table*table,intwrite,
void__user*buffer,size_t*length,loff_t*ppos)
{
intret;

ret=proc_dointvec_minmax(table,write,buffer,length,ppos);
if(ret)
returnret;
if(write){//如果是寫資料
staticintstfu;

if(sysctl_drop_caches&1){
//如果drop_caches=1則清pagecache
iterate_supers(drop_pagecache_sb,NULL);
count_vm_event(DROP_PAGECACHE);
}
if(sysctl_drop_caches&2){
//如果drop_caches=2則清pagecache和slab
drop_slab();
count_vm_event(DROP_SLAB);
}
if(!stfu){
pr_info("%s(%d):drop_caches:%d\n",
current->comm,task_pid_nr(current),
sysctl_drop_caches);
}
stfu|=sysctl_drop_caches&4;
}
return0;
}

通過上述分析,大致梳理了 sysctl 介面在 kernel 中執行的大致流程。

如何新增一個 sysctl 介面

接下來,學以致用,我們可以在 /proc/sys 這個根目錄下寫一個 my_sysctl 的節點,首先定義並填充 ctl_table 結構體,並通過 register_sysctl_table 註冊到系統。

#include
#include
#include

staticintdata;
staticstructctl_table_header*my_ctl_header;

intmy_sysctl_callback(
structctl_table*table,
intwrite,void__user*buffer,
size_t*lenp,loff_t*ppos)
{
intrc=proc_dointvec(
table,write,buffer,lenp,ppos);

if(write){
printk("writeoperation,curdata=%d\n",
*((unsignedint*)table->data));
}
}

/*Thedefaultsysctltables:*/
staticstructctl_tablemy_sysctl_table[]={
{
.procname="my_sysctl",
.mode=0644,
.data=&data,
.maxlen=sizeof(unsignedint),
.proc_handler=my_sysctl_callback,
},
{

},
};

staticint__initsysctl_test_init(void)
{
printk("sysctltestinit...\n");

my_ctl_header=register_sysctl_table(my_sysctl_table);

return0;
}

staticvoid__exitsysctl_test_exit(void)
{
printk("sysctltestexit...\n");

unregister_sysctl_table(my_ctl_header);
}

通過 qemu 進入目標檔案系統,使用 insmod 註冊驅動,在 /proc/sys 目錄下出現 my_sysctl 節點,此時就可以通過 cat/echo 命令向該節點讀寫資料,也可以直接通過 systcl 設定該引數。

/mnt#insmodsysctl_test.ko
[89.904485]sysctltestinit...

/mnt#sysctlmy_sysctl
my_sysctl=0

/mnt#sysctl-wmy_sysctl=2
[151.278213]writeoperation,curdata=2

/mnt#sysctlmy_sysctl
my_sysctl=2
/mnt#cat/proc/sys/my_sysctl
2

為簡便起見,大家也可以直接用 Linux Lab 來快速開展實驗,具體可以參考 “Linux Lab 文件的 4.1.2 節” https://gitee.com/tinylab/linux-lab#412-使用核心模組

獎金+贈書+星球會員,"泰曉科技" 社群推出三大舉措加速原創孵化,速速看過來(請點選下方圖片連結):

bdd7bf766b1f64d3b5cffad9e537103e.png

1988646889e458175ce6aa53e7da67f1.png

掃 碼關 注 我們

再 + 好 友 tinylab

進 泰 曉技 術群

泰 曉 科 技

bc41991fff66088fc7bd44d1b2c2d018.png

cc2fda8095494fd3c884506bb46c7731.png關注“泰曉科技”!點“在看”