proc檔案系統相關內容

kidking2010發表於2015-08-24

   proc檔案系統和sysfs檔案系統類似,是虛擬檔案系統,存在於記憶體中,用於核心和使用者程式互動等,檢視核心資訊。

          基於/proc檔案系統如上所述的特殊性,其內的檔案也常被稱作虛擬檔案,並具有一些獨特的特點。例如,其中有些檔案雖然使用檢視命令檢視時會返回大量資訊,但檔案本身的大小卻會顯示為0位元組。此外,這些特殊檔案中大多數檔案的時間及日期屬性通常為當前系統時間和日期,這跟它們隨時會被重新整理(儲存於RAM中)有關。 
           為了檢視及使用上的方便,這些檔案通常會按照相關性進行分類儲存於不同的目錄甚至子目錄中,如/proc/scsi目錄中儲存的就是當前系統上所有SCSI裝置的相關資訊,/proc/N中儲存的則是系統當前正在執行的程式的相關資訊,其中N為正在執行的程式(可以想象得到,在某程式結束後其相關目錄則會消失)。


            大多數虛擬檔案可以使用檔案檢視命令如cat、more或者less進行檢視,有些檔案資訊表述的內容可以一目瞭然,但也有檔案的資訊卻不怎麼具有可讀性。其中使用者程式ps、top、free等的內容就來自proc檔案系統中的資訊。

 

一、核心資訊

  1. /proc/cmdline:在啟動時傳遞至核心的相關引數資訊,這些資訊通常由lilo或grub等啟動管理工具進行傳遞

    ](FHQ0GEL0DXT2T2$GZCNHL

  2. /proc/cpuinfo:處理器的相關資訊

    FHTW2M%T45FZ%Q8A59A`IV6

  3. /proc/crypto:系統上已安裝的核心使用的密碼演算法及每個演算法的詳細資訊列表

    3SH}CS[~5$G`EQVMJIW5LTR

  4. /proc/devices:系統已經載入的所有塊裝置和字元裝置的資訊

    LB`L%B}``QZFANTIHT795)2

  5. /proc/diskstats:每塊磁碟裝置的磁碟I/O統計資訊列表

    {L)7YDPVPW]$3]Q8GW)Z)TB

  6. /proc/dma:每個正在使用且註冊的ISA DMA通道的資訊列表

    ]}IJZ_`59J}BPRF6KO0078J

  7. /proc/filesystems:當前被核心支援的檔案系統型別列表檔案,被標示為nodev的檔案系統表示不需要塊裝置的支援;通常mount一個裝置時,如果沒有指定檔案系統型別將透過此檔案來決定其所需檔案系統的型別

    $M(D1B5GWNHDQE~74P%1YU9

  8. /proc/iomem:每個物理裝置上的記憶體(RAM或者ROM)在系統記憶體中的對映資訊

    U@S[5D~6CS_]APHW})7_RCW

  9. /proc/meminfo:系統中關於當前記憶體的利用狀況等的資訊,常由free命令使用

    2D0F3)0@BL{5H7`)UWDDYG0

  10. /proc/mounts:此檔案的內容為系統當前掛載的所有檔案系統
  11. HAK[%YDHJ82DI44TI0]$9BC

  12. /proc/modules:當前裝入核心的所有模組名稱列表,可以由lsmod命令使用,也可以直接檢視
  13. B2L%[JO5%ZPBT]2FAZ30@AT

  14. /proc/partitions:塊裝置每個分割槽的主裝置號(major)和次裝置號(minor)等資訊,同時包括每個分割槽所包含的塊(block)數目
  15. T]B3%((5U9M`7@W`J2Y6$2A

  16. /proc/slabinfo:物件相關slap的資訊
  17. /proc/uptime:系統上次啟動以來的執行時間,如下所示,其第一個數字表示系統執行時間,第二個數字表示系統空閒時間,單位是秒;
  18. OVB1@PAU)`4(DZP`B~EEVID

  19. /proc/version:當前系統執行的核心版本號
  20. U1VOQR7(@EPI0[BK_WK]3(T

 

二、使用者程式資訊

/proc目錄中包含許多以數字命名的子目錄,這些數字表示系統當前正在執行程式的程式號,裡面包含對應程式相關的多個資訊檔案。

  1. cmdline — 啟動當前程式的完整命令,但殭屍程式目錄中的此檔案不包含任何資訊
  2. cwd — 指向當前程式執行目錄的一個符號連結
  3. exe — 指向啟動當前程式的可執行檔案(完整路徑)的符號連結,透過/proc/N/exe可以啟動當前程式的一個複製
  4. fd — 這是個目錄,包含當前程式開啟的每一個檔案的檔案描述符(file descriptor),這些檔案描述符是指向實際檔案的一個符號連結
  5. limits — 當前程式所使用的每一個受限資源的軟限制、硬限制和管理單元;此檔案僅可由實際啟動當前程式的UID使用者讀取;(2.6.24以後的核心版本支援此功能);
  6. maps — 當前程式關聯到的每個可執行檔案和庫檔案在記憶體中的對映區域及其訪問許可權所組成的列表;
  7. mem — 當前程式所佔用的記憶體空間,由open、read和lseek等系統呼叫使用,不能被使用者讀取;
  8. root — 指向當前程式執行根目錄的符號連結;
  9. stat — 當前程式的狀態資訊,包含一系統格式化後的資料列,可讀性差,通常由ps命令使用;
  10. statm — 當前程式佔用記憶體的狀態資訊,通常以“頁面”(page)表示;
  11. status — 與stat所提供資訊類似,但可讀性較好
  12. task — 目錄檔案,包含由當前程式所執行的每一個執行緒的相關資訊,每個執行緒的相關資訊檔案均儲存在一個由執行緒號(tid)命名的目錄中,這類似於其內容類似於每個程式目錄中的內容;

三、proc程式設計

   與sysfs虛擬檔案系統相比,proc的使用沒有sysfs那麼組織嚴謹,更加隨意,因此在核心模組中用proc檔案系統與使用者空間進行資訊互動還是很方便的。

     在核心模組中使用proc,需要包含標頭檔案

     其中常用的函式包括:

  1. 建立一個檔案:

     struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,   struct proc_dir_entry *parent); 

      name: 檔名稱

      mode:檔案屬性,常用S_IWUSR | S_IRUGO

      parent:父目錄,即建立的檔案在那個目錄下,若為NULL,則在/proc目錄下建立檔案

   2.   建立一個只讀的檔案:

      struct proc_dir_entry* create_proc_read_entry(const char* name, mode_t mode, struct proc_dir_entry* parent, read_proc_t*  read_proc, void* data);

     read_proc是讀操作回撥函式,data是多個檔案共享一個操作函式時區分檔案的私有資料

   3.   建立一個目錄:

      struct proc_dir_entry *proc_mkdir(const char *name,  struct proc_dir_entry *parent); 

      引數意義同上,只是這個函式在proc檔案系統中建立的是一個目錄

   4.    建立一個符號連結:

      struct proc_dir_entry* proc_symlink(const chat *name, struct proc_dir_entry *parent, const char *dest)

      在parent目錄下建立符號連結,效果如同:ln –s dest name

   5.   建立一個裝置節點

      struct proc_dir_entry* proc_mknod(const char *name, mode_t mode, struct proc_dir_entry *parent, kdev_t rdev)

      在parent目錄下建立一個裝置節點,其中rdev可以由MKDEV宏生成,mode引數必須包含S_IFBLK或S_IFCHR表示建立的是塊裝置還是字元裝置。

      效果如同:mknod --mode=mode name rdev

   6.   移除proc檔案系統中的入口

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

      上面建立的所有物件,都可以透過這個函式移除

   

      為了對proc檔案系統中的檔案進行讀寫操作,需要設定相應的回撥函式,在返回的對應proc檔案的struct proc_dir_entry結構中,設定其中的:

      entry->read_proc和entry->write_proc引數。

      其中entry->read_proc的函式原型為:

      int read_func(char* page, char** start, off_t off, int count,  int* eof, void* data); 從核心讀取資料

      核心返回的資料需要寫入page,其中page為核心地址,off和count分別是寫入在page中的偏移地址和可以寫入的最大位元組,則兩個引數主要是用於more和less命令,一般若內容較少可以忽略這兩個引數。若這兩個引數被使用,則需要設定eof為1來表示已到達檔案尾部。

     data引數用於同一個read_func函式為多個proc檔案服務時,進行區分所操作的proc檔案,並可以存放私有資料。

     這個函式的返回值是複製到page記憶體中的位元組數。

      entry->write_proc的函式原型為:

      int write_func(struct file* file, const char* buffer, unsigned long count, void* data); 寫入資料到核心

      引數count表示可以從buffer中訪問的最大位元組數,由於buffer是使用者空間記憶體,需要使用copy_from_user複製buffer中的資料到核心空間。file引數一般被忽略,而data同上也是用於區分多個檔案時的操作物件。

      引數data的使用例項一般為:

      struct proc_dir_entry* entry; 
      struct my_file_data *file_data; //檔案資料 

      file_data = kmalloc(sizeof(struct my_file_data), GFP_KERNEL); //分配空間 
      entry->data = file_data; //這裡是重點,需要將其賦值給對應proc檔案的proc_dir_entry結構,在read和write是才能區分

      int foo_read_func(char *page, char **start, off_t off, int count, int *eof, void *data) 
      { 
               int len;


              if(data == file_data) { 
                  /* 操作對應的檔案*/ 
               }

              else { 
                   /* 操作其他檔案*/ 
                 } 

                return len; 
         }

         需要注意的是,若entry->data的引數是動態分配的,在呼叫remove_proc_entry時,需要先將對應的data空間釋放。

#include 
#include 
#include 
#include 
#include 
#include

#define MODULE_VERSION "1.0" 
#define MODULE_NAME "procfs_example" 
#define FOOBAR_LEN 8

/*檔案私有資料結構*/ 
struct fb_data_t { 
    char name[FOOBAR_LEN + 1]; 
    char value[FOOBAR_LEN + 1]; 
};

static struct proc_dir_entry *example_dir, *foo_file, *bar_file, *jiffies_file, *tty_device, *symlink; 
struct fb_data_t foo_data, bar_data;

/*讀取核心jiffies*/ 
static int proc_read_jiffies(char *page, char **start, off_t off, int count, int *eof, void *data) 

    int len;

    len = sprintf(page, "jiffies = %ld\n", jiffies);

    return len; 
}

/*讀取foo和bar檔案*/ 
static int proc_read_foobar(char *page, char **start, off_t off, int count, int *eof, void *data) 

    int len; 
    struct fb_data_t *fb_data = (struct fb_data_t *)data;

    len = sprintf(page, "%s = ’%s’\n", fb_data->name, fb_data->value);

    return len; 
}

/*寫入foo和bar檔案*/ 
static int proc_write_foobar(struct file *file, const char *buffer, unsigned long count, void *data) 

    int len; 
    struct fb_data_t *fb_data = (struct fb_data_t *)data;

    if(count > FOOBAR_LEN) 
        len = FOOBAR_LEN; 
    else 
        len = count; 
    if(copy_from_user(fb_data->value, buffer, len)) 
        return -EFAULT;

    fb_data->value[len] = ’\0’;

    return len; 
}

/*模組初始化函式*/ 
static int __init init_procfs_example(void) 

    int rv = 0; 
    /* 在/proc下建立一個父目錄 */ 
    example_dir = proc_mkdir(MODULE_NAME, NULL); 
    if(example_dir == NULL) { 
        rv = -ENOMEM; 
        goto out; 
    }

    /* 建立讀取jiffies的proc檔案 */ 
    jiffies_file = create_proc_read_entry("jiffies", 0444, example_dir, proc_read_jiffies, NULL); 
    if(jiffies_file == NULL) { 
        rv = -ENOMEM; 
        goto no_jiffies; 
    }

    /* 建立foo和bar檔案,使用同一讀寫回撥函式 */ 
    foo_file = create_proc_entry("foo", 0644, example_dir); 
    if(foo_file == NULL) { 
        rv = -ENOMEM; 
        goto no_foo; 
    } 
    /*設定foo檔案的私有資料和讀寫回撥函式*/ 
    strcpy(foo_data.name, "foo"); 
    strcpy(foo_data.value, "foo"); 
    foo_file->data = &foo_data; 
    foo_file->read_proc = proc_read_foobar; 
    foo_file->write_proc = proc_write_foobar; 
    /* 建立bar檔案 */ 
    bar_file = create_proc_entry("bar", 0644, example_dir); 
    if(bar_file == NULL) { 
        rv = -ENOMEM; 
        goto no_bar; 
    }

    /*設定bar私有資料和讀寫回撥函式*/ 
    strcpy(bar_data.name, "bar"); 
    strcpy(bar_data.value, "bar"); 
    bar_file->data = &bar_data; 
    bar_file->read_proc = proc_read_foobar; 
    bar_file->write_proc = proc_write_foobar;

    /* 建立裝置檔案 */ 
    tty_device = proc_mknod("tty", S_IFCHR | 0666, example_dir, MKDEV(5, 0)); 
    if(tty_device == NULL) { 
        rv = -ENOMEM; 
        goto no_tty; 
    }

    /* 建立符號連結 */ 
    symlink = proc_symlink("jiffies_too", example_dir, "jiffies"); 
    if(symlink == NULL) { 
        rv = -ENOMEM; 
        goto no_symlink; 
    }

    printk(KERN_INFO "%s %s initialised\n", MODULE_NAME, MODULE_VERSION); 
    return 0;

no_symlink: 
    remove_proc_entry("tty", example_dir); 
no_tty: 
    remove_proc_entry("bar", example_dir); 
no_bar: 
    remove_proc_entry("foo", example_dir); 
no_foo: 
    remove_proc_entry("jiffies", example_dir); 
no_jiffies: 
    remove_proc_entry(MODULE_NAME, NULL); 
out: 
    return rv; 
}

/*模組解除安裝函式*/ 
static void __exit cleanup_procfs_example(void) 

    remove_proc_entry("jiffies_too", example_dir); 
    remove_proc_entry("tty", example_dir); 
    remove_proc_entry("bar", example_dir); 
    remove_proc_entry("foo", example_dir); 
    remove_proc_entry("jiffies", example_dir); 
    remove_proc_entry(MODULE_NAME, NULL); 
    printk(KERN_INFO "%s %s removed\n", 
    MODULE_NAME, MODULE_VERSION); 
}

module_init(init_procfs_example); 
module_exit(cleanup_procfs_example); 
MODULE_AUTHOR("Erik Mouw"); 
MODULE_DESCRIPTION("procfs examples");

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23141985/viewspace-1781256/,如需轉載,請註明出處,否則將追究法律責任。

相關文章