“操作系统实验”实验报告

proc文件系统的实现

实验名称:proc文件系统的实现 实验日期:2021/7/7

班级:物联网193 姓名:沈俊杰 学号:1930110716 二、实验环境: linux操作系统

三、实验内容: 在 Linux 0.11 上实现 procfs(proc 文件系统)内的 psinfo 结点。当读取此结点的内容时,可得到系统当前所有进程的状态信息。例如,用 cat 命令显示 /proc/psinfo 的内容,可得到:

cat/proc/psinfopidstatefathercounterstarttime01100110281411173311276360412817copy cat /proc/psinfo pid state father counter start_time 0 1 -1 0 0 1 1 0 28 1 4 1 1 1 73 3 1 1 27 63 6 0 4 12 817 copy cat /proc/hdinfo total_blocks: 62000; free_blocks: 39037; used_blocks: 22963; ... copy procfs 及其结点要在内核启动时自动创建。

相关功能实现在 fs/proc.c 文件内。

四、实验过程及数据记录: 第一步,新建一个文件类型 在include/sys/stat.h文件中

define S_IFPROC 0070000

define S_ISPROC(m) (((m) & S_IFMT) == S_IFPROC)

第二步,修改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位图的缓冲区指针,通过对这些指针的操作就可以取得相应的硬盘信息。

最新评论
暂无评论~