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)
在fs/namei.c文件中
//在sys_mkmod()函数中
if(S_ISBLK(mode) || S_ISCHR(mode) || S_ISPROC(mode))
inode->i_zone[0] = dev;
因为创建目录时会使用到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。
在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);
#include
#include
#include
#include
#include
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;is_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;
}
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