实验目的
- 掌握虚拟文件系统的实现原理
- 实践文件、目录、文件系统等概念
实验内容
在Linux0.11 上实现procfs(proc文件系统)内的psinfo 结点。读取此结点内容时,可得到系统当前所有进程的状态信息。例如用cat命令显示/proc/psinfo 的内容:
cat /proc/psinfo
pid state father counter start_time
0 1 -1 0 0
1 1 0 28 1
2 1 1 1 73
4 1 1 27 63
6 0 4 12 817
procfs及其结点要在内核启动时创建。相关功能实现在fs/proc.c文件当中。
实验过程
-
在include/sys/stat.h 中增加一种文件类型,实验手册里面有写。 .....
#define S_IFPROC 0060000
.....
#define S_ISPROC(m) (((m)&S_IFMT) == S_IFPROC)
....
-
psinfo结点通过fs/namei.c 文件中的mknod() 系统调用(用户态调用)建立,由于我们添加了新的文件类型,需要在该文件下增加对新文件类型的支持使得在第三步能通过mknod() 函数创建psinfo 文件,我们可以直接修改该系统调用的内核实现sys_mknod() 。 ....
inode->i_mode = mode;
if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISPROC(mode))
inode->i_zone[0] = dev;
.....
-
接下来就是建立/proc 目录以及在该目录下面 创建psinfo 文件。
创建目录我们在终端一般使用mkdir 命令, 在内核态使用sys_mkdir 函数。
创建文件我们在第二步已经在mknod() 函数添加了对新文件类型的支持,在内核态使用sys_mknod() 函数
我们可以在系统启动时就建立那些proc 文件系统(注意这些文件系统是挂载在根文件系统 下面的,我们自定义的proc 文件系统在系统中的位置是根文件系统/proc )Linux对文件系统的初始化是在根文件系统挂载之后才开始的。对根文件系统是在init/main.c 文件下面的init() 函数中完成的。 void init(void)
{
int pid,i;
setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0);
....
}
现在只要在挂载根文件系统之后就可以创建我们的目录以及文件了,但是有个地方需要注意:此时我们处在用户态,因此我们不能直接使用内核态才能使用的sys_mkdir() 以及sys_mknod() ,如果我们需要调用内核态的函数,我们需要先实现用户态的API接口然后通过中断进入内核态调用相应的函数(这是我们在实现操作系统实验二----系统调用中已经学到的知识)。 static inline _syscall0(int,fork)
static inline _syscall0(int,pause)
static inline _syscall1(int,setup,void *,BIOS)
static inline _syscall0(int,sync)
_syscall2(int,mkdir,const char*,name,mode_t,mode)
_syscall3(int,mknod,const char*,filename,mode_t,mode,dev_t,dev)
void init(void)
{
.....
setup((void *) &drive_info);
(void) open("/dev/tty0",O_RDWR,0);
(void) dup(0);
(void) dup(0);
mkdir("/proc",0755);
mknod("/proc/psinfo",S_IFPROC|0444,0);
mknod("/proc/meminfo",S_IFPROC|0444,1);
mknod("/proc/hdinfo",S_IFPROC|0444,2);
printf("%d buffers = %d bytes buffer space\n\r",NR_BUFFERS,
NR_BUFFERS*BLOCK_SIZE);
.....
}
-
此时进入Linux 0.11 编译以下,并进入模拟器运行以下指令: ll /proc
显示我们刚刚创建的三个结点,就说明以上步骤无误,也就是说我们能够通过sys_open() 能正常打开根目录下的刚刚创建的新文件系统proc . 但是如果我们用cat 指令去读取proc 目录下刚刚创建的psinfo 结点时出错(返回EINVAL ),说明内核对psinfo 文件类型的读取存在问题,返回的EINVAL 说明还没有实现处理函数,因此我们需要去修改sys_read() 。 -
为了让proc目录下面的文件可读,我们需要添加一个处理函数proc_read
if (inode->i_pipe)
return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;
if (S_ISCHR(inode->i_mode))
return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode))
return block_read(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
if (count+file->f_pos > inode->i_size)
count = inode->i_size - file->f_pos;
if (count<=0)
return 0;
return file_read(inode,file,buf,count);
}
printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
显然,我们需要在这一群if 中加上S_IFPROC 的分支来决定是否进入对proc 文件的处理函数。在此之前我们先分析以下需要传给处理函数的参数: inode->i_zone[0];
buf;
count;
&file->f_pos;
由于我们处理函数是在单独的文件里面实现,在.c文件中引入另外一个文件的全局变量或者函数就需要用extern 来声明以下。在read_write.c 文件开始的地方添加以下声明: extern int rw_char(int rw,int dev, char * buf, int count, off_t * pos);
extern int read_pipe(struct m_inode * inode, char * buf, int count);
extern int write_pipe(struct m_inode * inode, char * buf, int count);
extern int proc_read(int dev,char *buf,int count, unsigned long *pos);
extern int block_read(int dev, off_t * pos, char * buf, int count);
然后再添加分支 if (inode->i_pipe)
return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;
if (S_ISCHR(inode->i_mode))
return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode))
return block_read(inode->i_zone[0],&file->f_pos,buf,count);
if(S_ISPROC(inode->i_mode))
return proc_read(inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
-
接下来当然要去实现pro_read 函数。 在linux/fs/ 目录下创建一个新文件proc.c 。在实现该程序之前我们先弄清楚几个概念,这样就会更好理解代码。先上硬盘设备上的分区以及文件系统图表。
-
逻辑块与磁盘块:逻辑块与磁盘块大小一样(1KB)。 -
s_nzones: 表示设备上以逻辑块为单位的总逻辑块数。 -
s_ninodes :表示设备上的i节点总数。 -
s_imap_blocks: i节点位图所占用的磁盘块数。 -
s_zmap_blocks: 逻辑块位图所占用的磁盘块数。
#include<linux/sched.h>
#include<linux/kernel.h>
#include<asm/segment.h>
#include<stdarg.h>
#include<stddef.h>
extern int vsprintf(char *buf, const char *fmt,va_list args);
int sprintf(char *buf, const char *fmt,...)
{
va_list args;
int i;
va_start(args,fmt);
i=vsprintf(buf,fmt,args);
va_end(args);
return i;
}
int psinfo(unsigned long * pos, char * buf, int count)
{
struct task_struct ** p;
int output_count=0;
char * psbuf=NULL;
int chars=0;
int offset=*pos;
if((psbuf=(char *)malloc(sizeof(char *)*1024))==NULL)
{
printk("psinfo - malloc error!\n");
}
chars=sprintf(psbuf,"pid\tstate\tfather\tcounter\tstart_time\n");
for(p = &LAST_TASK ; p >= &FIRST_TASK ; --p)
{
if(*p)
{
chars+=sprintf(psbuf+chars,"%d\t%d\t%d\t%d\t%d\n",(*p)->pid,(*p)->state,(*p)->father,(*p)->counter,(*p)->start_time);
}
}
*(psbuf+chars)='\0';
while(count>0)
{
if(offset>chars)
{
break;
}
put_fs_byte(*(psbuf+offset),buf++);
offset++;
output_count++;
count--;
}
(*pos)+=output_count;
free(psbuf);
return output_count;
}
int hdinfo(unsigned long * pos, char * buf, int count)
{
struct super_block * sb;
struct buffer_head * bh;
int total_blocks;
int total_inodes;
int used_blocks=0;
int free_blocks=0;
int i,j,z;
char * p=NULL;
int chars=0;
int offset=*pos;
int output_count=0;
char * hdbuf=NULL;
sb=get_super(current->root->i_dev);
total_blocks = sb->s_nzones;
total_inodes=sb->s_ninodes;
for(i=0;i<sb->s_zmap_blocks;i++)
{
bh=sb->s_zmap[i];
p=(char*)bh->b_data;
for(j=0;j<1024;j++)
{
for(z=1;z<=8;z++)
{
if((used_blocks+free_blocks)>=total_blocks)
break;
if( *(p+j) & z)
used_blocks++;
else
free_blocks++;
}
}
}
hdbuf=(char*)malloc(sizeof(char*)*512);
chars=sprintf(hdbuf,"s_imap_blocks:%d\ns_zmap_blocks:%d\n",sb->s_imap_blocks,sb->s_zmap_blocks);
chars+=sprintf(hdbuf+chars,"total_blocks:%d\nfree_blcoks:%d\nused_blocks:%d\ntotal_indoes:%d\n",total_blocks,free_blocks,used_blocks,total_inodes);
while(count>0)
{
if(offset>chars)
break;
put_fs_byte(*(hdbuf+offset),buf++);
offset++;
output_count++;
count--;
}
(*pos)+=output_count;
free(hdbuf);
return output_count;
}
int proc_read(int dev,char *buf,int count, unsigned long *pos)
{
if(dev==0)
{
return psinfo(pos,buf,count);
}
if(dev==1)
{
return hdinfo(pos,buf,count);
}
return 0;
}
-
修改fs/ 目录下面的Makefile OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \
block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \
bitmap.o fcntl.o ioctl.o truncate.o proc_dev.o
### Dependencies:
bitmap.o: bitmap.c ../include/string.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h
block_dev.o: block_dev.c ../include/errno.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/segment.h ../include/asm/system.h
proc_dev.o: proc_dev.c ../include/errno.h ../include/linux/sched.h \
../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
../include/linux/mm.h ../include/signal.h ../include/linux/kernel.h \
../include/asm/segment.h ../include/asm/system.h ../include/stdarg.h
|