哈工大OS实验九--proc文件系统的实现

proc文件系统的实现

procfs简介

正式的Linux内核实现了procfs,它是一个虚拟文件系统,通常被 mount(挂载) 到/proc目录上,通过虚拟文件和虚拟目录的方式提供访问系统参数的机会,所以有人称它为 “了解系统信息的一个窗口”。

这些虚拟的文件和目录并没有真实地存在在磁盘上,而是内核中各种数据的一种直观表示。虽然是虚拟的,但它们都可以通过标准的系统调用(open()、read()等)访问。

例如/proc/meminfo中包含内存使用的信息,可以用 cat 命令显示其内容:

实验内容:在 Linux 0.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
4    1    1    1    73
3    1    1    27    63
6    0    4    12    817

$ cat /proc/hdinfo
total_blocks:    62000;
free_blocks:    39037;
used_blocks:    22963;
...

相关功能的实现在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;

同时,在上面声明一下proc_read函数

extern int proc_read(int dev,char *buf,int count,unsigned long *pos);

第五步,实现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");

        //这里借鉴了,进程切换函数schedule()的代码,也就是遍历系统全部的进程。
        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;
}

第六步,修改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

最后一步,运行

YocPXD.png

猜你喜欢

转载自blog.csdn.net/jump_into_zehe/article/details/106231756