您的位置:首页 > 编程语言

五。内核代码调试方法——proc文件系统、seqfile文件系统

2013-11-05 22:32 281 查看
如有错误大家一定告诉我,一起学习了;

############## proc 文件系统 ##################

1、 mount -t proc none /proc

ps ---> /proc/(每个结构体(每个进程)在这创建一个目录。以pid名字命名,进程的状态,关系等属性都是内核写在相应的目录下)

内核:找的是proc文件系统,我们只是通过上面的命令挂载到/proc下,找的不是proc目录,但是用户态的ps命令只会在proc目录下找,所以习惯放在proc目录

task_struct(每个进程都对应这么一个结构体) ---> task_struct的list(链表) --->

创建一个新的进程内核会自动分配一个这样的结构体(task_struct),再分配一个pid,然后把父进程的页表拷贝过来,所以父子进程共享前半段,当子进程进行不同的操作时会重新写页表

这些创建都是在磁盘上创建,proc文件系统上的文件在硬盘是不存在的,是存在于内存的;所以/proc里的文件只有内核代码能够创建,我们需要写模块来执行这些操作

不能这么创建,在/proc 下进行下列操作是错误的

mkdir /proc/test X(错)

touch /proc/test/hello X(错)

2、功能:

1>.调试:

a.printk

b.创建/proc/test/hello 打印到这个文件中。代替printk

2>.给内核传数据:

echo abc > /proc/test/hello

hello文件不能容纳数据,只是一个标识,是内核接口;abc的内容是传到内核的。

3、proc文件系统的头文件,包含各种函数的实现

<linux>/include/linux/proc_fs.h

struct proc_dir_entry{ //proc目录每个文件都对应这么一个结构体

void *data;

//这个值是写驱动的人提前写好的,内核不会操作这个值;在调用函数时会把这个值传给读写函数,无用就是NULL

//这是一个指针

read_proc_t *read_proc; //proc下文件的读函数指针

write_proc_t *write_proc; //proc下文件的写函数指针

loff_t size;//偏移

const struct inode_operations *proc_iops;//该文件的一组操作

... //还包含其它的很多

};

当执行 cat /proc/test/hello 时,调用的就是read_proc

当执行 echo abc > /proc/test/hello 时,调用的就是write_proc

create_proc_entry 创建一个proc文件

remove_proc_entry 删除一个proc文件

proc_mkdir 创建一个目录

proc_symlink 创建一个符号链接

create_proc_read_entry 创建一个只读的proc文件

proc_create_data 创建seqfile文件,proc存在问题,seqfile是用来解决proc函数一次只能读一页(4K)数据的问题;seqfile文件系统是自动检测分配内存的,不够时会自动给添加一页

4、为什么在写内核模块函数的时候加上 static?

答:内核模块函数默认是 static 的,但是内核默认是 extern,加上 static 就是 静态的;

又因为最后内核模块的函数最后都会被写到内核中,为了以后方便,也是一个习惯,就都加上

5、参考代码 <path>/02proc/*

6、在内核环境中,一定要注意出错处理,出错要将之前占用的全部资源释放

######################################

# 错误码的出处:

# include/asm-generic/errno.h

# include/asm-generic/errno-base.h

######################################

7、proc文件系统读函数

//先打开一个文件 fd = open("/proc/test_dir/test_att", xx(权限))

//用户态读 read(fd, buf, len)

//内核分配一页内存(4K)(page)

//len ---> count 读取长度

//*eof = 1;代表文件读取结束

//读取的时候,将读到的东西放到page内存中,结束时内核会自动将page内的东西放到buf(用户态传进来的)中

//读取大小受page大小限制,最大读4K;seqfile文件系统解决这个问题

int (read_proc_t)(char *page, char **start, off_t off, int count, int *eof, void *data)

8、proc文件系统写函数

//ret = write(fd, buf, len)

//struct file *file ---> 就是用户态的 FILE *,fopen 打开的文件流;封装后在用户态无法看到这个结构体

//__user 代表是用户态分配的缓存;[4G - 1, 3G]内核缓存,[3G - 1,0]用户态缓存;两种缓存的使用方式是不一样

//buf ---> buffer

//len ---> count

//data ---> 初始化时的100,随便传的, 写内核的人自己定义的

int (write_proc_t)(struct file *file, const char __user *buffer, unsigned long count, void *data)

9、proc文件系统中从用户态拷贝

#include <asm/uaccess.h> //包含在这个头文件

//用户态 buffer ---> 内核buf

//user ---> kernel

//使用 memcpy()从内核把东西拷贝到用户态,不是一定出错,是可能出错,内核不容许隐患

因为拷贝内核的东西时会有权限问题,造成拷贝的数据不完全

//内核提供的宏定义

// min(count, 1024) //1024 是定义的 buf 的大小

// max(count, 1024)

//copy_from_user 是内核实现的函数

copy_from_user(buf, buffer, count)

############### seqfile 文件系统 ##############

1、对 proc 文件的操作只有简单的读写;对 seqfile 文件有一个操作集 ops,是一个结构体:

struct file_operations ops = {

.owner = THIS_MODULE,

.open = test_seq_open, //open,用户态,自己实现

.release = seq_release, //close ,用户态,内核实现

.llseek = seq_lseek, //lseek,用户态,内核实现

.read = seq_read, //read,用户态,内核实现

.write = test_seq_write, //write,用户态,自己实现

};

================================================

= 此结构体包含在头文件 <path>/include/linux/fs.h

= 这组操作于用户态的 I/O 操作对应

================================================

==========================================================================

= test_seq_open 自己实现的原因:因为类型不匹配,需要把 seq_open 函数封装一下

= test_seq_write 自己实现原因:因为需要我们自己决定要以什么样的方式写

=================================================================

2、seqfile 文件系统打开函数

int test_seq_open(struct inode *no, struct file *fp)

{

return seq_open(fp, &seq_ops);

}

//open--->vfs-inode file->sys_open-->test_seq_open

调用用户态的open函数,经过VFS(虚拟文件系统),进入系统调用的函数,再到test_seq_open

struct inode *nu 是在执行时内核创建的,

//seq_open 函数的 seq_ops 结构体是让 seq_read 用的

3、seq_read 函数操作:

struct seq_operations seq_ops = {

.start = seq_start,

.stop = seq_stop,

.next = seq_next,

.show = seq_show,

};

//seq_read 函数的执行过程

//read-->vfs-->sys_read-->seq_read-->start-show-next-show-next-show-......-stop

4.seq_read 的过程函数

void *seq_start(struct seq_file *m, loff_t *pos)

{

//返回第*pos个结点的指针

}

void seq_stop(struct seq_file *m, void *v)

{

//seq_start的反操作

}

//如果该函数返回一个NULL,则seq_read结束

void *seq_next(struct seq_file *m, void *v, loff_t *pos)

{

//++*pos

//返回第*pos个结点的指针

}

//v是seq_start和seq_next的返回值

int seq_show(struct seq_file *m, void *v)

{

struct item_st *tmp = v;

//ret += sprintf(page + ret, .....);

//sprintf(m->buf, "No:%d Len:%d Content:%s", tmp->no, tmp->len, tmp->content);

//在seq_read结束之后,内核会用copy_to_user(buffer, m->buf, xxxxxx)

return seq_printf(m, "No:%d Len:%d Content:%s", tmp->no, tmp->len, tmp->content);

}

5、简述 seqfile 过程:

创建seq_file

创建的时候绑定了一组操作ops(open,close,read,write,...)

这组操作用于:用户态--->VFS---->sys_open系列函数--->test_seq_open-->seq_open-->...

write里也要实现链表(自己选择的数据存储方式),把数据存住(临时)

read:start - show - next - show - next - show - stop

每一个 seq_file 都对应自己的结构体,必须在 seq_open 之前把函数保存进去,然后在read的时候才能找到这些函数

next返回个NULL,sig_read就结束了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐