Linux使用者態與核心態通訊的幾種方式
本文首發於我的公眾號 Linux雲端計算網路(id: cloud_dev),專注於乾貨分享,號內有 10T 書籍和視訊資源,後臺回覆「1024」即可領取,歡迎大家關注,二維碼文末可以掃。
Linux 使用者態和核心態由於 CPU 許可權的限制,通訊並不像想象中的使用程序間通訊方式那麼簡單,今天這篇文章就來看看 Linux 使用者態和核心態究竟有哪些通訊方式。
我們平常在寫程式碼時,一般是在使用者空間,通過系統呼叫函式來訪問核心空間,這是最常用的一種使用者態和核心態通訊的方式。(關於 Linux 使用者態和核心態可以參考 xx)
除此之外,還有以下四種方式:
- procfs(/proc)
- sysctl(/proc/sys)
- sysfs(/sys)
- netlink 套介面
procfs(/proc)
procfs
是 程序檔案系統 的縮寫,它本質上是一個偽檔案系統,為什麼說是 偽 檔案系統呢?因為它不佔用外部儲存空間,只是佔用少量的記憶體,通常是掛載在 /proc
目錄下。
我們在該目錄下看到的一個檔案,實際上是一個核心變數。核心就是通過這個目錄,以檔案的形式展現自己的內部資訊,相當於 /proc
目錄為使用者態和核心態之間的互動搭建了一個橋樑,使用者態讀寫 /proc
下的檔案,就是讀寫核心相關的配置引數。
比如常見的 /proc/cpuinfo
、/proc/meminfo
和 /proc/net
就分別提供了 CPU、記憶體、網路的相關引數。除此之外,還有很多的引數,如下所示:
root@ubuntu:~# ls /proc/ 1 1143 1345 1447 2 2292 29 331 393 44 63 70 76 acpi diskstats irq locks sched_debug sysvipc zoneinfo 10 1145 1357 148 20 23 290 332 396 442 64 7019 77 asound dma kallsyms mdstat schedstat thread-self 1042 1149 1361 149 2084 2425 291 34 398 45 65 7029 8 buddyinfo driver kcore meminfo scsi timer_list 1044 1150 1363 15 2087 25 3 3455 413 46 66 7079 83 bus execdomains keys misc self timer_stats 1046 1151 1371 16 2090 256 30 35 418 47 6600 7080 884 cgroups fb key-users modules slabinfo tty 1048 1153 1372 17 21 26 302 36 419 5 67 71 9 cmdline filesystems kmsg mounts softirqs uptime 11 1190 1390 18 22 27 31 37 420 518 6749 72 96 consoles fs kpagecgroup mtrr stat version 1126 12 143 182 2214 28 32 373 421 524 68 73 97 cpuinfo interrupts kpagecount net swaps version_signature 1137 1252 1434 184 2215 280 327 38 422 525 69 74 98 crypto iomem kpageflags pagetypeinfo sys vmallocinfo 1141 13 144 190 2262 281 33 39 425 5940 7 75 985 devices ioports loadavg partitions sysrq-trigger vmstat
可以看到,這裡面有很多的數字表示的檔案,這些其實是當前系統執行的程序檔案,數字表示程序號(PID),每個檔案包含該程序所有的配置資訊,包括程序狀態、檔案描述符、記憶體對映等等,我們可以看下:
root@ubuntu:~# ls /proc/1/
attr/ cmdline environ io mem ns/ pagemap schedstat stat timers
autogroup comm exe limits mountinfo numa_maps personality sessionid statm uid_map
auxv coredump_filter fd/ loginuid mounts oom_adj projid_map setgroups status wchan
cgroup cpuset fdinfo/ map_files/ mountstats oom_score root/ smaps syscall
clear_refs cwd/ gid_map maps net/ oom_score_adj sched stack task/
綜上,核心通過一個個的檔案來暴露自己的系統配置資訊,這些檔案,有些是隻讀的,有些是可寫的,有些是動態變化的,比如程序檔案,當應用程式讀取某個 /proc/
檔案時,核心才會去註冊這個檔案,然後再呼叫一組核心函式來處理,將相應的核心引數拷貝到使用者態空間,這樣使用者讀這個檔案就可以獲取到核心的資訊。一個大概的圖示如下所示:
sysctl
我們熟悉的 sysctl 是一個 Linux 命令,man sysctl
可以看到它的功能和用法。它主要是被用來修改核心的執行時引數,換句話說,它可以在核心執行過程中,動態修改核心引數。
它本質上還是用到了檔案的讀寫操作,來完成使用者態和核心態的通訊。它使用的是 /proc
的一個子目錄 /proc/sys
。和 procfs 的區別在於:
procfs 主要是輸出只讀資料,而 sysctl 輸出的大部分資訊是可寫的。
例如,我們比較常見的是通過 cat /proc/sys/net/ipv4/ip_forward
來獲取核心網路層是否允許轉發 IP 資料包,通過 echo 1 > /proc/sys/net/ipv4/ip_forward
或者 sysctl -w net.ipv4.ip_forward=1
來設定核心網路層允許轉發 IP 資料包。
同樣的操作,Linux 也提供了檔案 /etc/sysctl.conf
來讓你進行批量修改。
sysfs
sysfs 是 Linux 2.6 才引入的一種虛擬檔案系統,它的做法也是通過檔案 /sys
來完成使用者態和核心的通訊。和 procfs 不同的是,sysfs 是將一些原本在 procfs 中的,關於裝置和驅動的部分,獨立出來,以 “裝置樹” 的形式呈現給使用者。
sysfs 不僅可以從核心空間讀取裝置和驅動程式的資訊,也可以對裝置和驅動進行配置。
我們看下 /sys
下有什麼:
# ls /sys
block bus class dev devices firmware fs hypervisor kernel module power
可以看到這些檔案基本上都跟計算機的裝置和驅動等息息相關的。更多關於這些檔案的解釋大家可以自行了解,這裡就不過多展開了。
netlink
netlink 是 Linux 使用者態與核心態通訊最常用的一種方式。Linux kernel 2.6.14 版本才開始支援。它本質上是一種 socket,常規 socket 使用的標準 API,在它身上同樣適用。比如建立一個 netlink socket,可以呼叫如下的 socket 函式:
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
netlink_socket = socket(AF_NETLINK, socket_type, netlink_family);
netlink 這種靈活的方式,使得它可以用於核心與多種使用者程序之間的訊息傳遞系統,比如路由子系統,防火牆(Netfilter),ipsec 安全策略等等。
引申:
net-tools
工具通過 procfs(/proc) 和 ioctl 系統呼叫去訪問和改變核心網路引數配置,而 iproute2
則通過 netlink 套接字介面與核心通訊,前者已經被淘汰了,後者逐步成為標準。
總結
Linux 使用者態和核心態通訊主要的四種方式,其中 netlink 和 procfs 是最常見的方式。
後臺回覆“加群”,帶你進入高手如雲交流群
我的公眾號 「Linux雲端計算網路」(id: cloud_dev) ,號內有 10T 書籍和視訊資源,後臺回覆 「1024」 即可領取,分享的內容包括但不限於 Linux、網路、雲端計算虛擬化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++程式設計技術等內容,歡迎大家關注。
參考:
https://www.ibm.com/developerworks/cn/linux/l-kerns-usrs/index.html
https://fasionchan.com/blog/2017/06/16/procfs-wei-wen-jian-xi-tong-yuan-li/
https://zh.wikipedia.org/wiki/Netl