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/Netlink


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM