实验名称:proc文件系统的实现 实验日期:2021/7/7
班级:物联网193 姓名:沈俊杰 学号:1930110716 二、实验环境: linux操作系统
三、实验内容: 在 Linux 0.11 上实现 procfs(proc 文件系统)内的 psinfo 结点。当读取此结点的内容时,可得到系统当前所有进程的状态信息。例如,用 cat 命令显示 /proc/psinfo 的内容,可得到:
cat /proc/hdinfo total_blocks: 62000; free_blocks: 39037; used_blocks: 22963; ... copy procfs 及其结点要在内核启动时自动创建。
相关功能实现在 fs/proc.c 文件内。
四、实验过程及数据记录: 第一步,新建一个文件类型 在include/sys/stat.h文件中
第二步,修改namei.c文件 在fs/namei.c文件中
//在sys_mkmod()函数中 if(S_ISBLK(mode) || S_ISCHR(mode) || S_ISPROC(mode)) inode->i_zone[0] = dev; 第三步,修改main.c文件 因为创建目录时会使用到mkdir,mknod这两个函数,而main.c处于用户态,因此我们需要在main.c添加系统调用,系统调用详细可以看这篇文章操作系统接口
_syscall2(int,mkdir,const char*,name,mode_t,mode) _syscall3(int,mknod,const char*,filename,mode_t,mode,dev_t,dev)
//在init()函数中 mkdir("/proc",0755); mknod("/proc/psinfo",S_IFPROC|0444,0); mknod("/proc/hdinfo",S_IFPROC|0444,1); mkdir() 时 mode 参数的值可以是 “0755”(对应 rwxr-xr-x),表示只允许 root 用户改写此目录,其它人只能进入和读取此目录。
procfs 是一个只读文件系统,所以用 mknod() 建立 psinfo 结点时,必须通过 mode 参数将其设为只读。建议使用 S_IFPROC|0444 做为 mode 值,表示这是一个 proc 文件,权限为 0444(r–r--r–),对所有用户只读。
mknod() 的第三个参数 dev 用来说明结点所代表的设备编号。对于 procfs 来说,此编号可以完全自定义。proc 文件的处理函数将通过这个编号决定对应文件包含的信息是什么。例如,可以把 0 对应 psinfo,1 对应 meminfo,2 对应 cpuinfo。
第四步,修改sys_read函数 在fs/read_write.c文件中
//添加一个分支,实现proc文件系统 if(S_ISPROC(inode->i_mode)) return proc_read(inode->i_zone[0],buf,count,&file->f_pos); printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode); return -EINVAL; 1 2 3 4 5 同时,在上面声明一下proc_read函数
extern int proc_read(int dev,char *buf,int count,unsigned long *pos); 1 第五步,实现proc.c文件 #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);
//Linux0.11没有sprintf(),该函数是用于输出结果到字符串中的,所以就实现一个,这里是通过vsprintf()实现的。 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 proc_read(int dev, char * buf, int count, unsigned long * pos) { struct task_struct ** p; int output_count=0; char * proc_buf=NULL; int file_size=0; int offset=*pos;
struct super_block * sb;
struct buffer_head * bh;
int total_blocks, total_inodes;
int used_blocks=0, free_blocks=0;
int i,j,k;
char * db=NULL;
unsigned short s_imap_blocks;
unsigned short s_zmap_blocks;
//硬盘总共有多少块(空闲 + 非空闲),有多少inode索引节点等信息都放在super块中。
sb=get_super(current->root->i_dev);
total_blocks = sb->s_nzones;
total_inodes=sb->s_ninodes;
s_imap_blocks = sb->s_imap_blocks;
s_zmap_blocks = sb->s_zmap_blocks;
//psinfo: 对应的就是输出系统此时的全部进程的状态信息
if(dev==0)
{
proc_buf=(char *)malloc(sizeof(char *)*1024);
file_size=sprintf(proc_buf,"pid\tstate\tfather\tcounter\tstart_time\n");
for(p = &LAST_TASK ; p >= &FIRST_TASK ; --p)
if(*p)
file_size+=sprintf(proc_buf+file_size,"%d\t%d\t%d\t%d\t%d\n",(*p)->pid,(*p)->state,(*p)->father,(*p)->counter,(*p)->start_time);
*(proc_buf+file_size)='\0';
}
//hdinfo: 打印出硬盘的一些信息,
//s_imap_blocks、ns_zmap_blocks、
//total_blocks、free_blocks、used_blocks、total_inodes
if(dev==1)
{
for(i=0;i<sb->s_zmap_blocks;i++)
{
bh=sb->s_zmap[i];
db=(char*)bh->b_data;
for(j=0;j<1024;j++){
for(k=1;k<=8;k++){
if((used_blocks+free_blocks)>=total_blocks)
break;
if( *(db+j) & k)
used_blocks++;
else
free_blocks++;
}
}
}
proc_buf=(char*)malloc(sizeof(char*)*512);
file_size=sprintf(proc_buf,"s_imap_blocks:%d\ns_zmap_blocks:%d\n",s_imap_blocks,s_zmap_blocks);
file_size+=sprintf(proc_buf+file_size,"total_blocks:%d\nfree_blcoks:%d\nused_blocks:%d\ntotal_indoes:%d\n",total_blocks,free_blocks,used_blocks,total_inodes);
}
//将proc_buf缓冲区的内容放入文件
while(count>0)
{
if(offset>file_size)
break;
put_fs_byte(*(proc_buf+offset),buf++);
offset++;
output_count++;
count--;
}
//重置文件的pos位置,也就是指向文件末尾的指针
(*pos)+=output_count;
free(proc_buf);
return output_count;
copy
}
第六步,修改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.o
proc.o : proc.c ../include/linux/kernel.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/asm/segment.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/asm/segment.h
最后一步,运行
五、实验结果分析: 如果要求你在psinfo之外再实现另一个结点,具体内容自选,那么你会实现一个给出什么信息的结点?为什么?
如果再实现另一个结点,我想会选内存使用的信息情况,因为内存是整个系统中最重要的资源,系统的运行要靠内存的支持,能及时了解内存的使用情况是有意义的。
一次read()未必能读出所有的数据,需要继续read(),直到把数据读空为止。而数次read()之间,进程的状态可能会发生变化。你认为后几次read()传给用户的数据,应该是变化后的,还是变化前的? 如果是变化后的,那么用户得到的数据衔接部分是否会有混乱?如何防止混乱? 如果是变化前的,那么该在什么样的情况下更新psinfo的内容?
按照目前的实现方法,如果在几次read之间进程状态发生变化,后几次传递给用户的是变化后的信息,那么可能会导致信息混乱。如果要防止混乱发生,可以考虑将取到的进程信息保存在硬盘的一个文件中,读取时只要该文件存在,就从文件中直接读取,而不是每次read都重新生成进程信息,而一次读动作完成后(返回0)就将硬盘上的文件删除。这样下次再时再次生成文件。这样可以保证传递的数据是一致的。
六、实验心得: 此次实验难度并不算高,只要了解了文件视图的工作流程和实现原理,完成此次实验并不困难。在实验过程中,遇到的问题主要在于对硬盘信息的读取上。硬盘信息在系统mount_root时已经进行了挂载,所以该硬盘的super_block已经保存在系统中,直接调用就可以了。但这里一定要清楚知道文件系统的实现方式,比如minix1.0文件系统由如下信息组成:
|启动区|超级块|inode节点位图|zblock节点位图|inode节点数据区|文件数据区|
而系统中super_block结构中已经记录了相应的信息,同时也保存着缓冲区对应的指针信息。如znone位图的缓冲区指针以及inode位图的缓冲区指针,通过对这些指针的操作就可以取得相应的硬盘信息。
学习时间 58分钟
操作时间 54分钟
按键次数 1604次
实验次数 3次
报告字数 6122字
是否完成 完成