Linux核心中的seq操作

2021-05-23 23:25:26 字數 4640 閱讀 7879

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...