簡介
procfs檔案系統是核心中的一個特殊檔案系統。它是一個虛拟檔案系統: 它不是實際的儲存設備中的檔案,而是存在于記憶體中。procfs中的檔案是用來允許使用者空間的程式通路核心中的某些資訊(比如程序資訊在 /proc/[0-9]+/中),或者用來做調試用途(/proc/ksyms,這個檔案列出了已經登記的核心符号,這些符号給出了變量或函數的位址。每行給出一個符号的位址,符号名稱以及登記這個符号的子產品。程式ksyms、insmod和kmod使用這個檔案。它還列出了正在運作的任務數,總任務數和最後配置設定的pid。)
這個文檔描述了核心中procfs檔案系統的使用。它以介紹所有和管理檔案系統相關的函數開始。在函數介紹後,它還展示了怎麼和使用者空間通信,和一些小技巧。在文檔的最後,還給出了一個完整的例子。
注意/proc/sys中的檔案屬于sysctl檔案,它們不屬于procfs檔案系統,被另外一套完全不同的api管理。
seq_file
procfs在處理大檔案時有點笨拙。為了清理procfs檔案系統并且使核心程式設計簡單些,引入了seq_file機制。seq_file機制提供了大量簡單的接口去實作大核心虛拟檔案。
seq_file機制适用于你利用結構序列去建立一個傳回給使用者空間的虛拟檔案。要使用seq_file機制,你必須建立一個”iterator”對象,這個對象指向這個序列,并且能逐個指向這個序列中的對象,此外還要能輸出這個序列中的任一個對象。它聽起來複雜,實際上,操作過程相當簡單。接下來将用實際的例子展示到底怎麼做。
首先,你必須包含頭檔案<linux/seq_file.h>。接下來,你必須建立疊代器方法:start, next, stop, and show。
start方法通常被首先調用。這個方法的函數原型是:
void *start(struct seq_file *sfile, loff_t *pos);
sfile沒什麼作用,通常被忽略。pos參數是一個整型,表示從哪個位置開始讀。關于位置的定義完全取決于函數實作;它不一定要是結果檔案中的一個位元組位置。 由于seq_file機制通常是利用一個特定的結構序列實作的,是以位置通常是一個指向序列中下一個結構體的指針。在wing驅動中,每一個裝置表示序列中的一個結構,是以,入參pos代表g_pstwingdevices數組的索引。是以,在wing驅動中start方法的實作為:
static void *wing_seq_start(struct seq_file *s, loff_t *pos)
{
if (*pos >= g_iwingdevicesnum)
return null; /* no more to read */
return g_pstwingdevices + *pos;
}
傳回值如果不為null,代表一個可以被疊代器使用的私有資料。
next函數應該移動疊代器到下一個位置,如果序列中沒有資料,傳回null。這個方法的函數原型為:
void *next(struct seq_file *sfile, void *v, loff_t *pos);
這裡,參數v代表上一個函數調用(可能是start函數,或者是next函數)傳回的疊代器,, 參數pos是檔案中的目前位置。next函數應該改變pos的指向,具體是逐漸改變還是跳躍改變取決于疊代器的工作機制。next函數在wing驅動中的實作為:
static void* wing_seq_next(struct seq_file *s, void *v, loff_t *pos)
(*pos)++;
return null;
當核心停止了疊代器的工作,它調用stop函數清理現場:
void stop(struct seq_file *sfile, void *v);
wing驅動沒有清理工作要做,是以stop函數為空。
void wing_seq_stop(struct seq_file *sfile, void *v)
要是seq_file代碼在調用start和stop時不執行睡眠或是非原子的操作,那麼這種機制将毫無意義。你要保證從start函數調用到stop函數調用是很短暫的。是以,在開始函數中獲得一個信号量或者自旋鎖是比較安全的做法。要是seq_file其他方法是原子的,整個調用鍊必須是原子的。
在這些函數調用中,核心調用call函數向核心空間輸出特性的資訊。這個函數的函數原型是:
int show(struct seq_file *sfile, void *v);
這個方法應該建立序列中由訓示器v指定項的輸出。不能使用printk,而是使用以下這些特定函數:
int seq_printf(struct seq_file *sfile, const char *fmt, ...)
這個函數是seq_file機制中類似于printf的實作;它使用通常的格式字元串和參數組成輸出字元串。你必須把show函數中的seq_file結構體傳給這個函數。如果它傳回一個非零的值,表示buffer已經填充好,輸出被丢出去了。在大多數實作中,都選擇忽略傳回值。
int seq_putc(struct seq_file *sfile, char c);
int seq_puts(struct seq_file *sfile, const char *s);
這兩個函數相當于使用者層的putc和puts。
int seq_escape(struct seq_file *m, const char *s, const char *esc);
這個函數是 seq_puts 的對等體, 除了 s 中的任何也在 esc 中出現的字元以八進制格式列印. esc 的一個通用值是”\t\n\”, 它使内嵌的空格不會搞亂輸出和可能搞亂 shell 腳本.
int seq_path(struct seq_file *sfile, struct vfsmount *m, struct dentry *dentry, char *esc);
這個函數能夠用來輸出和給定指令項關聯的檔案名子. 它在裝置驅動中不可能有用;我們是為了完整在此包含它.
wing裝置中的show函數例子:
static int wing_seq_show(struct seq_file *s, void *v)
st_wing_dev_type* pdev = (st_wing_dev_type* ) v;
seq_printf(s, "\nthis device is %i\n", pdev->idata);
return 0;
在我的例子中,我将一個st_wing_dev_type結構體表示為疊代器。
上面就是完整的疊代器操作,wing驅動必須将它們打包到一起好連接配接到procfs檔案系統。首先要做的就是利用它們組成一個seq_operations結構體:
static struct seq_operations s_stwingseqops = {
.start = wing_seq_start,
.next = wing_seq_next,
.stop = wing_seq_stop,
.show = wing_seq_show
};
有了這個結構,我們必須建立一個核心能了解的檔案實作。我們不使用前面說過的read_proc方法;在使用seq_file時, 最好在一個稍低的級别上連接配接到procfs。這意味着建立一個file_operations(和字元裝置一樣的結構),這個結構實作了核心對檔案的reads和seeks操作。幸運的是,這個操做很簡單。首先建立一個把檔案和seq_file方法聯接起來的open方法:
static int wing_proc_open(struct inode *inode, struct file *file)
return seq_open(file, &s_stwingseqops);
調用seq_open函數的時候将檔案和上面定義的序列操作關聯到一起。open是唯一要我們實作的函數接口,是以我們的file_operations結構體是:
static struct file_operations s_stwingprocfops = {
.owner = this_module,
.open = wing_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
最後我們要在procfs檔案系統中建立檔案:
proc_create("wingdevices", 0644, null, &s_stwingprocfops);
關鍵結構體
struct proc_dir_entry代表的是/proc目錄下的一個目錄或者檔案,他是procfs檔案系統的主要結構體,它的定義在/fs/internal.h中:
/*
* this is not completely implemented yet. the idea is to
* create an in-memory tree (like the actual /proc filesystem
* tree) of these proc_dir_entries, so that we can dynamically
* add new files to /proc.
*
* the "next" pointer creates a linked list of one /proc directory,
* while parent/subdir create the directory structure (every
* /proc file has a parent, but "subdir" is null for all
* non-directory entries).
*/
struct proc_dir_entry {
unsigned int low_ino;
umode_t mode;
nlink_t nlink;
kuid_t uid;
kgid_t gid;
loff_t size;
const struct inode_operations *proc_iops;
const struct file_operations *proc_fops;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
atomic_t count; /* use count */
atomic_t in_use; /* number of callers into module in progress; */
/* negative -> it's going away rsn */
struct completion *pde_unload_completion;
struct list_head pde_openers; /* who did ->open, but not ->release */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
u8 namelen;
char name[];
主要接口
procfs應該包含的頭檔案<linux/proc_fs.h>。
在3.x核心中procfs主要接口有:
proc_symlink
proc_mkdir
proc_mkdir_data
proc_mkdir_mode
proc_create_data
proc_create
proc_set_size
proc_set_user
pde_data
proc_get_parent_data
proc_remove
remove_proc_entry
remove_proc_subtree
說明:在/proc下建立目錄
函數原型:
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent)
參數:
name
要建立的目錄名稱
parent
父目錄,如果為null,表示直接在/proc下面建立目錄。
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode, struct proc_dir_entry *parent, void *data)
mode
指定要建立目錄的權限
data
說明:建立proc虛拟檔案系統檔案
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops, void *data)
你要建立的檔案名。
為建立的檔案指定權限
為你要在哪個檔案夾下建立名字為name的檔案,如:init_net.proc_net是要在/proc/net/下建立檔案。
proc_fops
為struct file_operations
儲存私有資料的指針,如不要為null。
例子:
////////////////////////test.c////////////////////////////////////////
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <net/net_namespace.h>
#include <linux/mm.h>
module_license("gpl");
typedef struct {
int data1;
int data2;
}st_data_info_type;
static st_data_info_type g_astdatainfo[2];
static int test_proc_show(struct seq_file *m, void *v)
st_data_info_type* pinfo = (st_data_info_type*)m->private;
if(pinfo != null)
{
seq_printf(m, "%d----%d\n", pinfo->data1, pinfo->data2);
}
static int test_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, test_proc_show, pde_data(inode));
}
static const struct file_operations dl_file_ops = {
.owner = this_module,
.open = test_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct proc_dir_entry *s_pstroottestdir;
void init_mem(void)
{
/* create /proc/test */
s_pstroottestdir = proc_mkdir("test", null);
if (!s_pstroottestdir)
return;
g_astdatainfo[0].data1=1;
g_astdatainfo[0].data2=2;
proc_create_data("proc_test1", 0644, s_pstroottestdir, &dl_file_ops, &g_astdatainfo[0]);
g_astdatainfo[1].data1=3;
g_astdatainfo[1].data2=4;
proc_create_data("proc_test2", 0644, s_pstroottestdir, &dl_file_ops, &g_astdatainfo[1]);
}
static int __init test_module_init(void)
printk("[test]: module init\n");
init_mem();
return 0;
static void __exit test_module_exit(void)
printk("[test]: module exit\n");
remove_proc_entry("proc_test1", s_pstroottestdir);
remove_proc_entry("proc_test2", s_pstroottestdir);
remove_proc_entry("test", null);
module_init(test_module_init);
module_exit(test_module_exit);
struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops)
注意:這個接口和proc_create_data的差別在于他不能儲存私有資料指針。
擷取proc_create_data傳入的私有資料。
說明:這個函數在procfs目錄下建立一個從name指向dest的符号連結. 它在使用者空間等效為ln -s dest name。
struct proc_dir_entry *proc_symlink(const char *name, struct proc_dir_entry *parent, const char *dest)
原始符号。
符号所在的目錄。
dest
所要建立的符号連結名字。
說明:删除procfs檔案系統中的檔案或者目錄。
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
要删除的檔案或者目錄名。
符号所在的目錄,如果為null,表示在/proc目錄下。
本文作者:佚名
來源:51cto