1. 程式人生 > >Linux使用者態與核心態通訊的幾種方式

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 是 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