1. 程式人生 > >[Linux] 核心的 /proc 檔案系統介紹及使用方法

[Linux] 核心的 /proc 檔案系統介紹及使用方法

核心現在採用的是 sysfs 檔案系統。
在 sysfs 誕生之前我們採用的是 proc 檔案系統。
sysfs 是一個與 /proc 類似的檔案系統,但是它的組織更好(從 /proc 中學習了很多教訓)。不過 /proc 已經確立了自己的地位,因此即使 sysfs 與 /proc 相比有一些優點,/proc 也依然會存在。本文對 /proc 檔案系統一些基礎的知識進行歸納和整理。

另外還有一個 debugfs 檔案系統,不過(顧名思義)它提供的更多是除錯介面。debugfs 的一個優點是它將一個值匯出給使用者空間非常簡單(實際上這不過是一個呼叫而已)。

proc 是一個虛擬的檔案系統,我們利用它實現 Linux 核心空間與使用者空間

的通訊。在 proc 檔案系統中,我們可以將對虛擬檔案的讀寫作為與核心中實體進行通訊的一種手段,但是與普通檔案不同的是,這些虛擬檔案的內容都是動態建立的。

proc 虛擬檔案系統介紹

最初這個是為了提供有關檔案系統中程序的資訊。
後來因為很有用,核心中其他元素也用其報告資訊,或進行動態執行配置。
/proc 檔案系統包含了一些目錄和虛擬檔案。
虛擬檔案向用戶呈現核心中的資訊,同時也是使用者空間向核心傳送資訊的手段。

呈現核心資訊

[root@younixPC]# ls /proc
1     2040  2347  2874  474          fb           mdstat      sys
104
2061 2356 2930 9 filesystems meminfo sysrq-trigger 113 2073 2375 2933 acpi fs misc sysvipc 1375 21 2409 2934 buddyinfo ide modules tty 1395 2189 2445 2935 bus interrupts mounts uptime 1706 2201 2514 2938 cmdline iomem mtrr version 179
2211 2515 2947 cpuinfo ioports net vmstat 180 2223 2607 3 crypto irq partitions 181 2278 2608 3004 devices kallsyms pci 182 2291 2609 3008 diskstats kcore self 2 2301 263 3056 dma kmsg slabinfo 2015 2311 2805 394 driver loadavg stat 2019 2337 2821 4 execdomains locks swaps

左邊是一系列數字編號的檔案。每個實際上都是一個目錄,表示系統中的一個程序。
由於在 GNU/Linux 中建立的第一個程序是 init 程序,因此它的 process-id 為 1。
然後對這個目錄執行一個 ls 命令,這會顯示很多檔案。

[root@younixPC]# ls /proc/1
auxv     cwd      exe  loginuid  mem     oom_adj    root  statm   task
cmdline  environ  fd   maps      mounts  oom_score  stat  status  wchan

每個檔案都提供了有關這個特殊程序的詳細資訊。例如,要檢視 init 的 command-line 項的內容,只需對 cmdline 檔案執行 cat 命令。

[root@younixPC]# cat /proc/1/cmdline
init [5]

/proc 中另外一些有趣的檔案有:cpuinfo,它標識了處理器的型別和速度;pci,顯示在 PCI 總線上找到的裝置;modules,標識了當前載入到核心中的模組。

配置核心資訊

下面對 /proc 中的一個虛擬檔案進行讀寫(配置),首先檢查核心的 TCP/IP 棧中 IP 轉發的目前設定,然後啟動這種功能。

root@younixPC:/# cat /proc/sys/net/ipv4/ip_forward
0
root@younixPC:/# echo "1" > /proc/sys/net/ipv4/ip_forward
root@younixPC:/# cat /proc/sys/net/ipv4/ip_forward
1

我們還可以用 sysctl 來配置這些核心條目。

核心模組簡介

可載入核心模組(LKM)是用來展示 /proc 檔案系統的一種方法。
它用來動態地向 Linux 核心新增或者刪除程式碼。
LKM 也是 Linux 核心中為裝置驅動程式和檔案系統使用的一種機制。

如果一個驅動程式被直接編譯到了核心中,那麼即使這個驅動程式沒有執行,它的程式碼和靜態資料也會佔據一部分空間。為了節省空間,我們採用動態模組編譯的方法,將這個驅動程式編譯成一個模組 .ko ,那麼就只在其需要記憶體並將其載入進核心時才佔用記憶體空間。

這樣可以根據可用硬體和連線的裝置來載入對應的模組。

一個最簡單的 LKM 例項(simple-lkm.c)

#include <linux/module.h>
/* Defines the license for this LKM */
MODULE_LICENSE("GPL");
/* Init function called on module entry */
int my_module_init( void )
{
  printk(KERN_INFO "my_module_init called.  Module is now loaded.\n");
  return 0;
}
/* Cleanup function called on module exit */
void my_module_cleanup( void )
{
  printk(KERN_INFO "my_module_cleanup called.  Module is now unloaded.\n");
  return;
}
/* Declare entry and exit functions */
module_init( my_module_init );
module_exit( my_module_cleanup );

對於這個檔案 simple-lkm.c 我們可以建立一個 Makefile ,其內容如下:

obj-m += simple-lkm.o

使用 make 來進行編譯:

younix@younixPC:~/Code/Linux/Moudle$ make -C /usr/src/linux-headers-4.4.0-24-generic SUBDIRS=$PWD modules
make: Entering directory '/usr/src/linux-headers-4.4.0-24-generic'
  CC [M]  /home/younix/Code/Linux/Moudle/simple-lkm.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/younix/Code/Linux/Moudle/simple-lkm.mod.o
  LD [M]  /home/younix/Code/Linux/Moudle/simple-lkm.ko
make: Leaving directory '/usr/src/linux-headers-4.4.0-24-generic'

結果生成了一個 simple-lkm.ko 的檔案。
現在可以載入或者解除安裝這個模組了,然後可以檢視其輸出。
insmod 命令載入
rmmod 命令解除安裝
lsmod 顯示當前的載入項

[email protected]:/home/younix/Code/Linux/Moudle# insmod simple-lkm.ko
[email protected]:/home/younix/Code/Linux/Moudle# lsmod
Module                  Size  Used by
simple_lkm             16384  0
[email protected]:/home/younix/Code/Linux/Moudle# rmmod simple-lkm

另外,核心的輸出進入了 核心的環形緩衝區中,並沒有列印到 stdout 上。
使用 dmesg 工具或者使用 cat /proc/kmsg 來檢視其內容。

[email protected]:/home/younix/Code/Linux/Moudle# dmesg > dmesg.txt
my_module_init called.  Module is now loaded.
my_module_cleanup called.  Module is now unloaded.

將 LKM 整合到 /proc 檔案系統中

這裡簡單地介紹一下在展示一個更有用的 LKM 時所使用的幾個元素。

建立並刪除 /proc 項

create_proc_entry

建立一個虛擬檔案。
這個函式可以接收一個檔名、一組許可權和這個檔案在 /proc 檔案系統中出現的位置。create_proc_entry 的返回值是一個 proc_dir_entry 指標(或者為 NULL,說明在 create 時發生了錯誤)。然後就可以使用這個返回的指標來配置這個虛擬檔案的其他引數,例如在對該檔案執行讀操作時應該呼叫的函式。create_proc_entry 的原型和 proc_dir_entry 結構中的一部分如下所示。

struct proc_dir_entry *create_proc_entry( const char *name, mode_t mode,
                                             struct proc_dir_entry *parent );
struct proc_dir_entry {
    const char *name;           // virtual file name
    mode_t mode;                // mode permissions
    uid_t uid;              // File's user id
    gid_t gid;              // File's group id
    struct inode_operations *proc_iops; // Inode operations functions
    struct file_operations *proc_fops;  // File operations functions
    struct proc_dir_entry *parent;      // Parent directory
    ...
    read_proc_t *read_proc;         // /proc read function
    write_proc_t *write_proc;       // /proc write function
    void *data;             // Pointer to private data
    atomic_t count;             // use count
    ...
};

read_proc / write_proc

插入對這個虛擬檔案進行讀寫的函式。

remove_proc_entry

需要提供檔名字串,以及這個檔案在 /proc 檔案系統中的位置(parent)。

void remove_proc_entry( const char *name, struct proc_dir_entry *parent );

parent 引數可以為 NULL(表示 /proc 根目錄),也可以是很多其他值,這取決於我們希望將這個檔案放到什麼地方。下面列出了可以使用的其他一些父 proc_dir_entry,以及它們在這個檔案系統中的位置。

proc_dir_entry 在檔案系統中的位置
proc_root_fs /proc
proc_net /proc/net
proc_bus /proc/bus
proc_root_driver /proc/driver

寫回調函式

write 函式原型

int mod_write( struct file *filp, const char __user *buff,
               unsigned long len, void *data );

filp 引數實際上是一個開啟檔案結構(我們可以忽略這個引數)。
buff 引數是傳遞給您的字串資料。緩衝區地址實際上是一個使用者空間的緩衝區,因此我們不能直接讀取它。
len 引數定義了在 buff 中有多少資料要被寫入。
data 引數是一個指向私有資料的指標。
在這個模組中,我們聲明瞭一個這種型別的函式來處理到達的資料。
我們使用 copy_from_user 函式來維護使用者空間的資料。

讀回撥函式

read 寫回調函式

int mod_read( char *page, char **start, off_t off,
              int count, int *eof, void *data );

page 引數是這些資料寫入到的位置
count 定義了可以寫入的最大字元數。
在返回多頁資料(通常一頁是 4KB)時,我們需要使用 start 和 off 引數。
當所有資料全部寫入之後,就需要設定 eof(檔案結束引數)。
與 write 類似,data 表示的也是私有資料。此處提供的 page 緩衝區在核心空間中。因此,我們可以直接寫入,而不用呼叫 copy_to_user。

其他有用的 proc 函式

proc_mkdir、symlinks 以及 proc_symlink 在 /proc 檔案系統中建立目錄。
對於只需要一個 read 函式的簡單 /proc 項來說,可以使用 create_proc_read_entry,這會建立一個 /proc 項,並在一個呼叫中對 read_proc 函式進行初始化。

/* Create a directory in the proc filesystem */
struct proc_dir_entry *proc_mkdir( const char *name,
                                     struct proc_dir_entry *parent );
/* Create a symlink in the proc filesystem */
struct proc_dir_entry *proc_symlink( const char *name,
                                       struct proc_dir_entry *parent,
                                       const char *dest );
/* Create a proc_dir_entry with a read_proc_t in one call */
struct proc_dir_entry *create_proc_read_entry( const char *name,
                                                  mode_t mode,
                                                  struct proc_dir_entry *base,
                                                  read_proc_t *read_proc,
                                                  void *data );
/* Copy buffer to user-space from kernel-space */
unsigned long copy_to_user( void __user *to,
                              const void *from,
                              unsigned long n );
/* Copy buffer to kernel-space from user-space */
unsigned long copy_from_user( void *to,
                                const void __user *from,
                                unsigned long n );
/* Allocate a 'virtually' contiguous block of memory */
void *vmalloc( unsigned long size );
/* Free a vmalloc'd block of memory */
void vfree( void *addr );
/* Export a symbol to the kernel (make it visible to the kernel) */
EXPORT_SYMBOL( symbol );
/* Export all symbols in a file to the kernel (declare before module.h) */
EXPORT_SYMTAB

由 proc 檔案系統實現財富分發

下面是可以支援讀寫的 LKM。
在載入了這個模組後,使用者可以使用 echo 命令向其中匯入文字財富。再利用 cat 命令逐一輸出。
下面給出了基本的模組函式和變數。
init 函式(init_fortune_module)負責使用 vmalloc 來為這個點心罐分配空間,然後使用 memset 將其全部清零。
使用所分配並已經清空的 cookie_pot 記憶體,我們在 /proc 中建立了一個 proc_dir_entry 項,並將其稱為 fortune。
當 proc_entry 成功建立之後,對自己的本地變數和 proc_entry 結構進行了初始化。
我們載入了 /proc read 和 write 函式,並確定這個模組的所有者。
cleanup 函式簡單地從 /proc 檔案系統中刪除這一項,然後釋放 cookie_pot 所佔據的記憶體。
cookie_pot 是一個固定大小(4KB)的頁,它使用兩個索引進行管理。第一個是 cookie_index,標識了要將下一個 cookie 寫到哪裡去。變數 next_fortune 標識了下一個 cookie 應該從哪裡讀取以便進行輸出。在所有的 fortune 項都讀取之後,我們簡單地回到了 next_fortune。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Fortune Cookie Kernel Module");
MODULE_AUTHOR("M. Tim Jones");
#define MAX_COOKIE_LENGTH       PAGE_SIZE
static struct proc_dir_entry *proc_entry;
static char *cookie_pot;  // Space for fortune strings
static int cookie_index;  // Index to write next fortune
static int next_fortune;  // Index to read next fortune
int init_fortune_module( void )
{
  int ret = 0;
  cookie_pot = (char *)vmalloc( MAX_COOKIE_LENGTH );
  if (!cookie_pot) {
    ret = -ENOMEM;
  } else {
    memset( cookie_pot, 0, MAX_COOKIE_LENGTH );
    proc_entry = create_proc_entry( "fortune", 0644, NULL );
    if (proc_entry == NULL) {
      ret = -ENOMEM;
      vfree(cookie_pot);
      printk(KERN_INFO "fortune: Couldn't create proc entry\n");
    } else {
      cookie_index = 0;
      next_fortune = 0;
      proc_entry->read_proc = fortune_read;
      proc_entry->write_proc = fortune_write;
      proc_entry->owner = THIS_MODULE;
      printk(KERN_INFO "fortune: Module loaded.\n");
    }
  }
  return ret;
}
void cleanup_fortune_module( void )
{
  remove_proc_entry("fortune", &proc_root);
  vfree(cookie_pot);
  printk(KERN_INFO "fortune: Module unloaded.\n");
}
module_init( init_fortune_module );
module_exit( cleanup_fortune_module );

向這個罐中新寫入一個 cookie 非常簡單。
使用這個寫入 cookie 的長度,我們可以檢查是否有這麼多空間可用。如果沒有,就返回 -ENOSPC,它會返回給使用者空間。否則,就說明空間存在,我們使用 copy_from_user 將使用者緩衝區中的資料直接拷貝到 cookie_pot 中。然後增大 cookie_index(基於使用者緩衝區的長度)並使用 NULL 來結束這個字串。最後,返回實際寫入 cookie_pot 的字元的個數,它會返回到使用者程序。

ssize_t fortune_write( struct file *filp, const char __user *buff,
                        unsigned long len, void *data )
{
  int space_available = (MAX_COOKIE_LENGTH-cookie_index)+1;
  if (len > space_available) {
    printk(KERN_INFO "fortune: cookie pot is full!\n");
    return -ENOSPC;
  }
  if (copy_from_user( &cookie_pot[cookie_index], buff, len )) {
    return -EFAULT;
  }
  cookie_index += len;
  cookie_pot[cookie_index-1] = 0;
  return len;
}

對 fortune 進行讀取也非常簡單,如下所示。由於我們剛才寫入資料的緩衝區(page)已經在核心空間中了,因此可以直接對其進行操作,並使用 sprintf 來寫入下一個 fortune。如果 next_fortune 索引大於 cookie_index(要寫入的下一個位置),那麼我們就將 next_fortune 返回為 0,這是第一個 fortune 的索引。在將這個 fortune 寫入使用者緩衝區之後,在 next_fortune 索引上增加剛才寫入的 fortune 的長度。這樣就變成了下一個可用 fortune 的索引。這個 fortune 的長度會被返回並傳遞給使用者。

int fortune_read( char *page, char **start, off_t off,
                   int count, int *eof, void *data )
{
  int len;
  if (off > 0) {
    *eof = 1;
    return 0;
  }
  /* Wrap-around */
  if (next_fortune >= cookie_index) next_fortune = 0;
  len = sprintf(page, "%s\n", &cookie_pot[next_fortune]);
  next_fortune += len;
  return len;
}

從這個簡單的例子中,我們可以看出通過 /proc 檔案系統與核心進行通訊實際上是件非常簡單的事情。現在讓我們來看一下這個 fortune 模組的用法:

[root@younixPC]# insmod fortune.ko
[root@younixPC]# echo "Success is an individual proposition.  
          Thomas Watson" > /proc/fortune
[root@younixPC]# echo "If a man does his best, what else is there?  
                Gen. Patton" > /proc/fortune
[root@younixPC]# echo "Cats: All your base are belong to us.  
                      Zero Wing" > /proc/fortune
[root@younixPC]# cat /proc/fortune
Success is an individual proposition.  Thomas Watson
[root@younixPC]# cat /proc/fortune
If a man does his best, what else is there?  General Patton

/proc 虛擬檔案系統可以廣泛地用來報告核心的資訊,也可以用來進行動態配置。我們會發現它對於驅動程式和模組程式設計來說都是非常完整的。

相關推薦

[Linux] 核心的 /proc 檔案系統介紹使用方法

核心現在採用的是 sysfs 檔案系統。 在 sysfs 誕生之前我們採用的是 proc 檔案系統。 sysfs 是一個與 /proc 類似的檔案系統,但是它的組織更好(從 /proc 中學習了很多教訓)。不過 /proc 已經確立了自己的地位,因此即使

linux Proc檔案系統介紹運用總結

引言 先說一個剛剛發現的問題: 前兩天開啟測試機發現速度非常慢,top一看,發現java佔用CPU 99% 檢視對應pid,發現這個是新的話單採集程式gather: #ps -ef | grep java Root 5762 1 99 14:41 pts/0 00:0

Linuxproc檔案系統介紹與應用

proc檔案系統的作用 (1) 核心專案太龐大,除錯困難,引入新的除錯方法。 (2) proc檔案系統的思路是:在核心中構建一個虛擬檔案系統/proc,核心執行時將核心中一些關鍵的資料結構以檔案的方式呈現在/proc目錄下的一些特定檔案中, 這樣相當於將不可見的核心中的資料結構以

linuxproc檔案系統

linux中的/proc檔案系統是一個虛擬的檔案系統,由核心在執行時動態生成。它提供了核心執行時的配置和狀態資訊。使用者可以通過這些檔案來獲取、或修改核心的資訊。 關於proc,可以通過 man proc 獲取詳細的說明。執行 mount 命令,可以看到 proc 檔案系統

[原始碼和文件分享]NTFS檔案系統介紹檔案定位

背景 在日常生活中,我們開啟我們的電腦操作各種檔案。我們都知道,檔案資料都儲存在硬碟上,但是,硬碟中儲存的資料都是0、1的二進位制資料,我們的電腦怎麼從這一大堆0、1資料中知道哪個是哪個檔案呢? 這就是檔案系統在起作用,它對硬碟的資料設定格式規則,儲存資料的時候,就按這個儲存規則進行儲存,那

核心proc檔案系統簡介

/proc檔案系統是一個特殊的由核心建立的檔案系統,她僅存在於記憶體之中而不在外存(硬碟、flash)上。核心用她來向用戶空間程序輸出訊息,可以說是核心向用戶空間開啟的一扇窗戶。 最初開發/proc檔案系統是為提供一種核心及其模組向程序 (proc

linuxproc檔案系統 -- ldd3讀書筆記

     1./proc 檔案系統概述      /proc 檔案系統是由軟體建立,被核心用來向外界報告資訊的一個檔案系統。/proc 下面的每一個檔案都和一個核心函式相關聯,當檔案的被讀取時,與之對應的核心函式用於產生檔案的內容。我們已經見到了很多這樣的檔案,例如,/pr

Linux-(1)Linux樹狀檔案系統結構各資料夾的作用

  Linux檔案系統為一個倒轉的單根樹狀結構.   檔案系統的根為"/"   Linux中所有的東西都是檔案.   如下圖所示:     各資料夾的作用如下:   bin: 存放所有使用者都可以執行的可執行檔案.   boot: 存放系統引導,啟動相關的內容. 其中的

Linux最新UBI檔案系統介紹

嵌入式linux中文站關注嵌入式linux檔案系統的發展。在linux-2.6.27以前,談到Flash檔案系統,大家很多時候多會想到cramfs、jffs2、yaffs2等檔案系統。它們也都是基於檔案系統+mtd+flash裝置的架構。linux-2.6.27後,核心加

【轉】FatFS檔案系統介紹使用例程

FatFS檔案系統包含了檔案 ff.h         :檔案系統實現標頭檔案,定義有檔案系統所需的資料結構 diskio.h  :底層驅動標頭檔案,就一些狀態巨集的定義和底層驅動函式的申明 integer.h:僅實現資料型別重定義,增加系統的可移植性 ffcon

Linux核心讀取檔案流程原始碼阻塞點超詳解

以linux核心3.13版本為例,首先核心通過系統呼叫read(),執行sys_read()函式,在檔案linux/fs/read_write.c中: //linux/fs/read_write.c SYSCALL_DEFINE3(read, unsigne

Linux 核心/sys 檔案系統之sysfs 屬性檔案

屬性檔案分為:匯流排屬性檔案,CLASS屬性檔案,裝置屬性檔案,驅動屬性檔案 DEVICE_ATTR 巨集宣告有四個引數,分別是名稱、許可權位、讀函式、寫函式 28 struct attribute { 29 const char *name; //設定該檔案的名字 30

Linux 核心檔案系統(結構篇)

ok,繼前面概念篇之後,我們開始正式的探討下Linux的檔案系統。 檔案系統是對一個儲存裝置上的資料和元資料進行組織的機制(教材式還是需要的),在前面的概念篇有說到,Linux支援大多數檔案系統,可以預料到Linux檔案系統介面實現為分層的體系結構,從而將使用

使用 /proc 檔案系統來訪問 Linux 核心的內容

最初開發 /proc 檔案系統是為了提供有關係統中程序的資訊。但是由於這個檔案系統非常有用,因此核心中的很多元素也開始使用它來報告資訊,或啟用動態執行時配置。 /proc 檔案系統包含了一些目錄(用作組織資訊的方式)和虛擬檔案。虛擬檔案可以向用戶呈現核心中的一些資訊,

用 /proc 檔案系統來訪問 Linux 核心的內容

最初開發 /proc 檔案系統是為了提供有關係統中程序的資訊。但是由於這個檔案系統非常有用,因此核心中的很多元素也開始使用它來報告資訊,或啟用動態執行時配置。 /proc 檔案系統包含了一些目錄(用作組織資訊的方式)和虛擬檔案。虛擬檔案可以向用戶呈現核心中的一些資訊,也可以用作一種從使用者空間向核心傳送

嵌入式 使用 /proc 檔案系統來訪問 Linux 核心的內容

/* Create a directory in the proc filesystem */ struct proc_dir_entry *proc_mkdir( const char *name, struct proc_dir_e

Linux核心模組程式設計-proc檔案系統

什麼是proc proc檔案系統是一個偽檔案系統,它只存在記憶體當中,而不佔用外存空間。它以檔案系統的方式為訪問系統核心資料的操作提供介面。使用者和應用程式可以通過proc得到系統的資訊,並可以改變核心的某些引數。由於系統的資訊,如程序,是動態改變的,所以使用

Linux核心通訊之---proc檔案系統(詳解)

使用 /proc 檔案系統來訪問 Linux 核心的內容,這個虛擬檔案系統在核心空間和用戶空間之間打開了一個通訊視窗: /proc 檔案系統是一個虛擬檔案系統,通過它可以使用一種新的方法在 Linux核心空間和使用者間之間進行通訊。在 /proc 檔案系統中,我

Linux系統下的bootloader、Linux核心、root檔案系統介紹

簡介:三部分:bootloader、linux kernel(linux核心)、rootfile(根檔案系統)。那麼這3部分是怎麼相互協作來構成這個系統的呢?各自有什麼用呢?三者有什麼聯絡?怎麼聯絡?系統的執行流程又是怎麼樣的呢?搞清楚這個問題你對整個系統的執行就很清楚了,

Linux文件系統介紹創建目錄和文件

var 子文件夾 啟動 proc make aaa 維護 program 51cto Linux文件系統Linux系統是一個為文件設計的操作系統,在Linux中萬物皆文件,其文件結構為樹形結構,/為根目錄,左右其他目錄都在從/根目錄衍發出去的。 下面介紹幾個根目錄下比較重要