歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> Linux那些事兒之我是Sysfs(12)舉例三:sysfs讀入文件夾內容

Linux那些事兒之我是Sysfs(12)舉例三:sysfs讀入文件夾內容

日期:2017/3/3 12:41:42   编辑:Linux技術
上回我們說到,如何創建文件夾和文件。我們發現,在sysfs中,inode並不那麼重要。這是因為我們所要讀寫的信息已經就在內存中,並且已經形成了層次結構。我們只需有dentry,就可以dentry->fsdata,就能找到我們讀些信息的來源 --- sysfs_dirent結構。這也是我覺得有必要研究 sysfs的原因之一,因為它簡單,而且不涉及具體的硬件驅動,但是從這個過程中,我們可以把文件系統中的一些基本數據結構搞清楚。接下來,我以讀取sysfs文件和文件夾的內容為例子,講講文件讀的流程。那麼關於寫,還有關於symblink的東西完全可以以此類推了。
我們新建文件夾時,設置了
inode->i_op = &sysfs_dir_inode_operations;
inode->i_fop = &sysfs_dir_operations;
struct file_operations sysfs_dir_operations = {
.open = sysfs_dir_open,
.release = sysfs_dir_close,
.llseek = sysfs_dir_lseek,
.read = generic_read_dir,
.readdir = sysfs_readdir,
};
用一個簡短的程序來做實驗。

#include<sys/types.h>

#include<dirent.h>

#include<unistd.h>

int main(){

DIR * dir;

struct dirent *ptr;

dir = opendir("/sys/bus/");

while((ptr = readdir(dir))!=NULL){

printf("d_name :%s
",ptr->d_name);

}

closedir(dir);

return -1;

}
在用戶空間,用gcc編譯執行即可。我們來看看它究竟做了什麼。
(1)sysfs_dir_open()
這是個用戶空間的程序。opendir()是glibc的函數,glibc也就是著名的標准c庫。至於opendir ()是如何與sysfs dir open ()接上頭的,那還得去看glibc的代碼。我就不想分析了...glibc可以從gnu的網站上自己下載源代碼,編譯。再用gdb調試,就可以看得跟清楚。
函數流程如下:
opendir("/sys/bus/") -> /*用戶空間*/
-> 系統調用->
sys_open() -> filp_open()-> dentry_open() -> sysfs_dir_open()/*內核空間*/

static int sysfs_dir_open(struct inode *inode, struct file *file)

{

struct dentry * dentry = file->f_dentry;

struct sysfs_dirent * parent_sd = dentry->d_fsdata;

down(&dentry->d_inode->i_sem);

file->private_data = sysfs_new_dirent(parent_sd, NULL);

up(&dentry->d_inode->i_sem);

return file->private_data ? 0 : -ENOMEM;

}
內核空間:新建一個dirent結構,連入父輩的dentry中,並將它地址保存在file->private_data中。這個dirent的具體作用待會會講。
用戶空間:新建了一個DIR結構,DIR結構如下。
#define __dirstream DIR
struct __dirstream
{
int fd; /* File descriptor. */
char *data; /* Directory block. */
size_t allocation; /* Space allocated for the block. */
size_t size; /* Total valid data in the block. */
size_t offset; /* Current offset into the block. */
off_t filepos; /* Position of next entry to read. */
__libc_lock_define (, lock) /* Mutex lock for this structure. */
};
(2)sysfs_readdir()
流程如下:
readdir(dir) -> getdents() ->/*用戶空間*/
-> 系統調用->
sys32 readdir() -> vfs readdir() -> sysfs readdir()/*內核空間*/
readdir(dir)這個函數有點復雜,雖然在main函數裡的while循環中,readdir被執行了多次,我們看看glibc裡面的代碼

readdir(dir){

......

if (dirp->offset >= dirp->size){

......

getdents()

......

}

......

}
實際上,getdents() -> ... -> sysfs_readdir()只被調用了兩次,getdents()一次就把所有的內容都讀完,存在DIR結構當中,readdir()只是從DIR結構當中每次取出一個。DIR(dirstream)結構就是一個流。而回調函數filldir的作用就是往這個流中填充數據。第二次調用getdents()是用戶把DIR裡面的內容讀完了,所以它又調用getdents()但是這次getdents()回返回NULL。

static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)

{

struct dentry *dentry = filp->f_dentry;

struct sysfs_dirent * parent_sd = dentry->d_fsdata;

struct sysfs_dirent *cursor = filp->private_data;

struct list_head *p, *q = &cursor->s_sibling;

ino_t ino;

int i = filp->f_pos;

switch (i) {

case 0:

ino = dentry->d_inode->i_ino;

if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)

break;

filp->f_pos++;

i++;

/* fallthrough */

case 1:

ino = parent_ino(dentry);

if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)

break;

filp->f_pos++;

i++;

/* fallthrough */

default:

if (filp->f_pos == 2) {

list_del(q);

list_add(q, &parent_sd->s_children);

}

for (p=q->next; p!= &parent_sd->s_children; p=p->next) {

struct sysfs_dirent *next;

const char * name;

int len;

next = list_entry(p, struct sysfs_dirent, s_sibling);

if (!next->s_element)

continue;

name = sysfs_get_name(next);

len = strlen(name);

if (next->s_dentry)

ino = next->s_dentry->d_inode->i_ino;

else

ino = iunique(sysfs_sb, 2);

if (filldir(dirent, name, len, filp->f_pos, ino,dt_type(next)) < 0)

return 0;

list_del(q);

list_add(q, p);

p = q;

filp->f_pos++;

}

}

return 0;

}
看sysfs_readdir()其實很簡單,它就是從我們調用sysfs_dir_open()時新建的一個sysfs_dirent結構開始,便利當前dentry->dirent下的所有子sysfs_dirent結構。讀出名字,再回調函數filldir()將文件名,文件類型等信息,按照一定的格式寫入某個緩沖區。
一個典型的filldir()就是filldir64(),它的作用的按一定格式向緩沖區寫數據,再把數據復制到用戶空間去。
Copyright © Linux教程網 All Rights Reserved