Implementation of operating system experiment nine proc file system (Li Zhijun, Harbin Institute of Technology)

Implementation of Experiment 9 proc file system

Purpose

  • Master the realization principle of virtual file system;
  • Practice the concepts of files, directories, filesystems, etc.

Experimental content

Implementation of psinfo nodes within procfs (proc file system) on Linux 0.11. When reading the content of this node, you can get the status information of all current processes in the system. For example, using the cat command to /proc/psinfodisplay the content, you can get:

$ 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;
...

procfsand its nodes are automatically created when the kernel starts.

Related functions are implemented in fs/proc.cthe file .

Introduction to procfs

The official Linux kernel is implemented procfs. It is a virtual file system, which is usually mounted (mounted) to /procthe directory , and provides opportunities to access system parameters through virtual files and virtual directories, so some people call it "understanding system information. a window".

These virtual files and directories do not actually exist on the disk, but a visual representation of various data in the kernel. Although virtual, they are all accessible through standard system calls ( open(), , read()etc.).

For example, /proc/meminfocontains memory usage information, you can use the cat command to display its contents:

image-20220101134655104

Linux is implemented through the file system interface procfsand automatically mounts it to /procthe directory .

All the contents in this directory are automatically created, deleted and updated along with the operation of the system, and they exist completely in the memory without occupying any external storage space.

Linux 0.11 does not yet implement virtual filesystems, that is, does not yet provide an interface for adding support for new filesystems. Therefore, this experiment can only simulate one by patching on the basis of the existing file system procfs.

Linux 0.11 uses the Minix file system, which is a typical file system inodebased , which is described in detail in the book "Notes". Each of its files must correspond to at least one inode, and various attributes of the file are recorded in the inode, including the file type. File types include ordinary files, directories, character device files, and block device files. In the kernel, each type of file has a different processing function corresponding to it. We can add a new file type - proc file, and realize the functions to be realized by procfs in the corresponding processing function.

Add new file types

File location:include/sys/stat.h

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

image-20220101135740255

Let mknod()support new file types

The psinfo node is created through mknod()system calls, so it needs to support new file types.

if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISPROC(mode))
     inode->i_zone[0] = dev;

image-20220101140917424

procfsinitialization work

All the work of kernel initialization is done main()in , and main()at the end switch from kernel mode to user mode and call init().

init()The first thing to do is mount the root filesystem:

void init(void)
{
    
    
//    ……
    setup((void *) &drive_info);
//    ……
}

procfsThe initialization work should start after the root file system is mounted. It consists of two steps:

  • (1) Create /proca directory ; create /proceach node under the directory. This experiment was built only /proc/psinfo.

  • (2) To create a directory and a node, you need to call mkdir()and mknod()system call respectively. Because it is already in the user mode when it is initialized, it cannot be sys_mkdir()called sys_mknod(). The user mode interface of these two system calls must be implemented in the file where the initialization code is located, that is, the API:

    static inline _syscall0(int,fork)
    static inline _syscall0(int,pause)
    static inline _syscall1(int,setup,void *,BIOS)
    static inline _syscall0(int,sync)
    /*新增mkdir和mknode系统调用*/
    _syscall2(int,mkdir,const char*,name,mode_t,mode)
    _syscall3(int,mknod,const char *,filename,mode_t,mode,dev_t,dev)
        
    //.......   
        
    	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/hdinfo",S_IFPROC|0444,1);
    	mknod("/proc/inodeinfo",S_IFPROC|0444,2);
    

File Directory:init/main.c

image-20220101142324146

image-20220101143650297

mkdir()The value of the mode parameter can be "0755" (corresponding rwxr-xr-x), which means that only the root user is allowed to rewrite this directory, and other people can only enter and read this directory.

procfs is a read-only file system, so when mknod()creating a psinfo node with , it must be set as read-only through the mode parameter. It is recommended to use S_IFPROC|0444as the mode value, indicating that this is a proc file with permission 0444 (r–r–r–), read-only for all users.

mknod()The third parameter dev is used to describe the device number represented by the node. For procfs, this number is fully customizable. The processing function of the proc file will use this number to determine what information the corresponding file contains. For example, you can put 0 for psinfo, 1 for meminfo, and 2 for cpuinfo.

If the work is completed without any problem, after compiling and running the 0.11 kernel, you ll /proccan see:

image-20220101144222230

inode->i_modeIt is the mode mknod()set . The XXX in the information S_IFPROCis related to . Use this value to know whether mknod()it is working properly. These messages indicate that the hdinfokernel failed to process correctly when reading from , and returned an EINVAL error to cat. This is normal since the handler function has not been implemented yet.

This information at least shows hdinfothat it open()is . So we don't need to sys_open()touch anything, the only thing that needs to be patched is sys_read().

make procthe file readable

File location:fs/read_write.c

Add extern, indicating that the proc_read function is called from the outside

/*新增proc_read函数外部调用*/
extern int proc_read(int dev,char* buf,int count,unsigned long *pos);

image-20220101145341739

Then in the sys_read function, follow other if statements, add the branch of S_IFPROC(), and add the proc_read() call of the proc file:

	if (inode->i_pipe)
		return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;
	/*新增proc_read调用*/
	if (S_ISPROC(inode->i_mode))
		return proc_read(inode->i_zone[0],&file->f_pos,buf,count);
	if (S_ISCHR(inode->i_mode))
		return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);

image-20220101154426345

The parameters that need to be passed to the handler function include:

  • inode->i_zone[0], which is what was specified mknod()when dev- the device number
  • buf, pointing to user space, is the second parameter read()of , used to receive data
  • count, which is the third parameter read()of , indicating bufthe size of the buffer pointed to by
  • &file->f_pos, f_posis the point of the "file position pointer" at the end of the last read file. A pointer must be passed here, because the processing function needs to modify the value of according to the amount of data passed bufto f_pos.

implement proc_readfunction

The function of the processing function of the proc file is to write different contents to buf in the user space according to the device number. The data to be written should start from the position f_pospointed to , write at most count bytes each time, and adjust f_posthe value according to the actual number of bytes written, and finally return the actual number of bytes written. When the device number indicates that the content of psinfo is to be read, the data should be organized in the form of psinfo.

The following functions may be used to implement this function:

  • malloc() function
  • free() function

linux/kernel.hOnce the header file is included , malloc()the and free()functions are available. They can be called by core state code, and the only limitation is that the memory size requested at a time cannot exceed one page.

File location:fs/proc.c

The code comes from a big guy's blog --- reference code

#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/segment.h>
#include <linux/fs.h>
#include <stdarg.h>
#include <unistd.h>

#define set_bit(bitnr,addr) ({
      
       \
register int __res ; \
__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \
__res; })

char proc_buf[4096] ={
    
    '\0'};

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 get_psinfo()
{
    
    
	int read = 0;
	read += sprintf(proc_buf+read,"%s","pid\tstate\tfather\tcounter\tstart_time\n");
	struct task_struct **p;
	for(p = &FIRST_TASK ; p <= &LAST_TASK ; ++p)
 	if (*p != NULL)
 	{
    
    
 		read += sprintf(proc_buf+read,"%d\t",(*p)->pid);
 		read += sprintf(proc_buf+read,"%d\t",(*p)->state);
 		read += sprintf(proc_buf+read,"%d\t",(*p)->father);
 		read += sprintf(proc_buf+read,"%d\t",(*p)->counter);
 		read += sprintf(proc_buf+read,"%d\n",(*p)->start_time);
 	}
 	return read;
}

/*
*  参考fs/super.c mount_root()函数
*/
int get_hdinfo()
{
    
    
	int read = 0;
	int i,used;
	struct super_block * sb;
	sb=get_super(0x301);  /*磁盘设备号 3*256+1*/
	/*Blocks信息*/
	read += sprintf(proc_buf+read,"Total blocks:%d\n",sb->s_nzones);
	used = 0;
	i=sb->s_nzones;
	while(--i >= 0)
	{
    
    
		if(set_bit(i&8191,sb->s_zmap[i>>13]->b_data))
			used++;
	}
	read += sprintf(proc_buf+read,"Used blocks:%d\n",used);
	read += sprintf(proc_buf+read,"Free blocks:%d\n",sb->s_nzones-used);
	/*Inodes 信息*/
	read += sprintf(proc_buf+read,"Total inodes:%d\n",sb->s_ninodes);
	used = 0;
	i=sb->s_ninodes+1;
	while(--i >= 0)
	{
    
    
		if(set_bit(i&8191,sb->s_imap[i>>13]->b_data))
			used++;
	}
	read += sprintf(proc_buf+read,"Used inodes:%d\n",used);
	read += sprintf(proc_buf+read,"Free inodes:%d\n",sb->s_ninodes-used);
 	return read;
}

int get_inodeinfo()
{
    
    
	int read = 0;
	int i;
	struct super_block * sb;
	struct m_inode *mi;
	sb=get_super(0x301);  /*磁盘设备号 3*256+1*/
	i=sb->s_ninodes+1;
	i=0;
	while(++i < sb->s_ninodes+1)
	{
    
    
		if(set_bit(i&8191,sb->s_imap[i>>13]->b_data))
		{
    
    
			mi = iget(0x301,i);
			read += sprintf(proc_buf+read,"inr:%d;zone[0]:%d\n",mi->i_num,mi->i_zone[0]);
			iput(mi);
		}
		if(read >= 4000) 
		{
    
    
			break;
		}
	}
 	return read;
}

int proc_read(int dev, unsigned long * pos, char * buf, int count)
{
    
    
	
 	int i;
	if(*pos % 1024 == 0)
	{
    
    
		if(dev == 0)
			get_psinfo();
		if(dev == 1)
			get_hdinfo();
		if(dev == 2)
			get_inodeinfo();
	}
 	for(i=0;i<count;i++)
 	{
    
    
 		if(proc_buf[i+ *pos ] == '\0')  
          break; 
 		put_fs_byte(proc_buf[i+ *pos],buf + i+ *pos);
 	}
 	*pos += i;
 	return i;
}

Also modify fs/Makefilethe file:

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
//......
### Dependencies:
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

image-20220101153300252

image-20220101153343127

Compile and run

Recompile the kernel again make all, then run the kernel, and check the information of psinfo(current system process status information) and hdinfo(hard disk information):

image-20220101154642892

God rewards work

bye

Guess you like

Origin blog.csdn.net/leoabcd12/article/details/122268183