上回我們說到,如何創建文件夾和文件。我們發現,在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(),它的作用的按一定格式向緩沖區寫數據,再把數據復制到用戶空間去。