1. 前言
在fs/seq_file.c中定義了關於seq操作的一系列順序讀取的函式,這些函式最早是在2023年就引入了,但以前核心中一直用得不多,而到了2.6核心後,許多/proc的唯讀檔案中大量使用了seq函式處理。
以下核心原始碼版本為2.6.17.11。
2. seq相關資料結構
2.1 seq檔案結構
struct seq_file ;
struct seq_file描述了seq處理的緩衝區及處理方法,buf是動態分配的,大小不小於page_size,通常這個結構是通過struct file結構中的private_data來指向的。
char *buf:seq流的緩衝區
size_t size:緩衝區大小
size_t from:from指向當前要顯示的資料頭位置
size_t count:緩衝區中已有的資料長度
loff_t index:資料記錄索引值
loff_t version:版本號,是struct file的版本號的拷貝
struct mutex lock:seq鎖
struct seq_operations *op:seq操作結構,定義資料顯示的操作函式
void *private:私有資料
2.2 seq操作結構
/* include/linux/seq_file.h */
struct seq_operations ;
3. seq操作函式
int seq_open(struct file *, struct seq_operations *);
開啟seq流,為struct file分配struct seq_file結構,並定義seq_file的操作;
ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);
從seq流中讀資料到使用者空間,其中迴圈呼叫了struct seq_file中的各個函式來讀資料;
ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
/* if not empty - flush it first */
// count表示當時有多少資料還沒有傳給使用者空間
// 盡量先將這些資料傳出
if (m->count)
// 進行主要傳資料過程,緩衝區中至少要有乙個記錄單位的資料
/* we need at least one record in buffer */
while (1)
m->op->stop(m, p);
m->count = 0;
goto done;
fill:
// 繼續讀資料到緩衝區
/* they want more? let's try to get some more */
while (m->count < size)
err = m->op->show(m, p);
if (err || m->count == m->size)
pos = next; }
// 停seq
m->op->stop(m, p);
n = min(m->count, size);
// 將資料拷貝到使用者空間
err = copy_to_user(buf, m->buf, n);
if (err)
goto efault;
copied += n;
m->count -= n;
if (m->count)
m->from = n;
else
pos++;
m->index = pos;
done:
if (!copied)
copied = err;
else
*ppos += copied;
file->f_version = m->version;
mutex_unlock(&m->lock);
return copied;
enomem:
err = -enomem;
goto done;
efault:
err = -efault;
goto done; }
loff_t seq_lseek(struct file *, loff_t, int);
定位seq流當前指標偏移;
int seq_release(struct inode *, struct file *);
釋放seq流所分配的動態記憶體空間,即struct seq_file的buf及其本身;
int seq_escape(struct seq_file *, const char *, const char *);
將seq流中需要進行轉義的字元轉換為8進製數字;
int seq_putc(struct seq_file *m, char c);
向seq流中寫乙個字元
int seq_puts(struct seq_file *m, const char *s);
向seq流中寫乙個字串
int seq_printf(struct seq_file *, const char *, ...)
__attribute__ ((format (printf,2,3)));
向seq流方式寫格式化資訊;
int seq_path(struct seq_file *, struct vfsmount *, struct dentry *, char *);
在seq流中新增路徑資訊,路徑字元都轉換為8進製數。
int seq_release_private(struct inode *, struct file *);
釋放seq_file的private然後進行seq_release
3. 用seq流填寫/proc檔案
以下使用檔案/proc/net/ip_conntrack的生成**來說明seq流的使用:
3.1 創立檔案
以前2.4版本中使用proc_net_create()來建立/proc/net下的檔案,現在使用seq流時要使用proc_net_fops_create()函式來建立,區別在於函式的最後乙個引數,proc_net_create()的是乙個函式指標,而proc_net_fops_create()的是乙個檔案操作指標:
......
proc = proc_net_fops_create("ip_conntrack", 0440, &ct_file_ops);
......
proc_net_fops_create()函式其實也很簡單,呼叫create_proc_entry()函式建立/proc檔案項,然後將檔案項的操作結構指標指向所提供的檔案操作指標:
static inline struct proc_dir_entry *proc_net_fops_create(const char *name,
mode_t mode, const struct file_operations *fops)
3.2 檔案操作結構
/proc/net/ip_conntrack所用的檔案結構如下:
static struct file_operations ct_file_ops = ;
可見,結構中除了open()函式是需要自定義外,其他的讀、定位、釋放函式都可以用seq標準函式。
3.3 open函式定義
open函式主要就是呼叫seq_open()函式將乙個struct seq_operations結構和struct file鏈結起來,如果需要有私有資料的話,需要分配出動態空間作為struct seq_file的私有資料:
static int ct_open(struct inode *inode, struct file *file)
簡單的如exp_open()函式,就只呼叫seq_open()函式就完了:
static int exp_open(struct inode *inode, struct file *file)
3.4 seq操作結構
static struct seq_operations ct_seq_ops = ;
這個結構就是填寫4個操作函式:
start()函式完成讀資料前的一些預先操作,通常如加鎖,定位資料記錄位置等,該函式返回值就是show()函式第二個引數:
static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
stop()函式完成讀資料後的一些恢復操作,如解鎖等:
static void ct_seq_stop(struct seq_file *s, void *v)
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
show()函式實現讀資料過程,將要輸出的資料直接用seq_printf()函式列印到seq流緩衝區中,由seq_printf()函式輸出到使用者空間:
static int ct_seq_show(struct seq_file *s, void *v)
4. 結論
seq流函式的使用保證了資料能順序輸出,這也就是/proc唯讀檔案中使用它的最大原因吧。
Linux核心中的位操作 ffs h,fls h
今天閱讀原始碼時遇到乙個函式 ffs,它時核心中實現的位操作函式,用來查詢二進位制表示數中第乙個為1的位。與ffs對應的還有fls.h,用來查詢二進位制數中最後乙個為1的位。例如 整數32,對應的二進位制為100000,即第乙個和最後乙個為1的位是6 整數34,對應的二進位制為100010,第乙個為...
Linux核心中的list for each
在linux核心原始碼中,經常要對鍊錶進行操作,其中乙個很重要的巨集是list for each entry 意思大體如下 假設只有兩個結點,則第乙個member代表head,list for each entry的作用就是迴圈遍歷每乙個pos中的member子項。巨集list for each e...
Linux核心中的Namespace
linux核心中的namespace提供了乙個輕量級的基於系統呼叫層面的虛擬化解決方案。相比傳統的使用 vmware,qemu,xen,kvm,hurd的虛擬 圖1所示 基於namespace的輕量級虛擬具有易使用,易管理,無需硬體虛擬化支援,低 成本等優點。圖 1.namespace又稱conta...