Li Zhijun의 "운영 체제" | 실험 9 - proc 파일 시스템 구현

목차

1. 실험 목적

2. 실험적인 내용

3. 실험 준비

1. procfs 소개

2. 기본 아이디어

4. 실험과정

1. 새로운 파일 형식 추가

2. mknod()가 새로운 파일 형식을 지원하도록 합니다.

(1) mknod 시스템 호출 수정

(2) procfs 초기화

3. proc 파일을 읽을 수 있게 만듭니다.

(1) proc_read 함수 외부 호출 추가

(2) proc 파일의 proc_read() 호출 추가

4. proc 파일의 처리 기능 실현

(1) proc.c 작성

(2) Makefile 수정

5. 컴파일 및 실행 


1. 실험 목적

1. 가상 파일 시스템의 구현 원리를 숙달하십시오.

2. 파일, 디렉토리 및 파일 시스템의 개념을 연습합니다.

2. 실험적인 내용

procfs( proc파일 시스템 )  내의 psinfo 접합은         Linux 0.11에서 구현됩니다 (구현될 hdinfo 수도 있음 inodeinfo). 이 노드의 내용을 읽을 때 시스템의 모든 현재 프로세스의 상태 정보를 얻을 수 있습니다.

예를 들어  cat 명령을 사용하여 /proc/psinfo 및  의 내용을 표시하려면 /proc/hdinfo 다음을 얻을 수 있습니다.

$ 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;
...
  • procfs 커널이 시작될 때 해당 노드가 자동으로 생성됩니다.
  • 관련 기능은  fs/proc.c 파일에 구현되어 있습니다.

3. 실험 준비

1. procfs 소개

       공식 리눅스 커널이 구현되어 있다  procfs. 보통 디렉토리에 마운트(마운트)되는 가상 파일 시스템(프로세스 파일 시스템)이다.  /proc 가상 파일과 가상 디렉토리를 통해 시스템 매개변수에 접근할 수 있는 기회를 제공하기 때문에 혹자는 " 시스템 정보를 볼 수 있는 창".

       이러한 가상 파일 및 디렉토리는 실제로 디스크에 존재하지 않지만 커널의 다양한 데이터를 시각적으로 표현한 것입니다. 가상이지만 모두 표준 시스템 호출(  open(), read() ... )을 통해 액세스할 수 있습니다.

예를 들어, /proc/meminfo 메모리 사용량 정보가 포함된 경우 cat 명령을 사용하여 내용을 표시할 수 있습니다.

       실제로 Linux의 많은 시스템 명령은  /proc 읽기로 구현됩니다. 예를 들어  uname -a 정보의 일부는 에서 이고  /proc/version정보  uptime 의 일부는  /proc/uptime 및  에서 입니다 /proc/loadavg .

procfsProcfs에 대한 자세한 내용 http://en.wikipedia.org/wiki/Procfs 를 방문하세요.

2. 기본 아이디어

       Linux는 파일 시스템 인터페이스를 통해 구현되며   시작 시 자동으로 디렉토리 procfs에 마운트됩니다  ./proc

       이 디렉터리의 모든 내용은 시스템의 동작에 따라 자동으로 생성, 삭제, 갱신되며 외부 저장 공간을 차지하지 않고 메모리에 완전히 존재합니다.

       Linux 0.11은 아직 가상 파일 시스템을 구현하지 않습니다. 즉, 새 파일 시스템에 대한 지원을 추가하기 위한 인터페이스를 아직 제공하지 않습니다. 따라서 이 실험은 기존 파일 시스템을 기반으로 패치를 적용하여 한 가지만 시뮬레이션할 수 있습니다  procfs .

       Linux 0.11은 일반적인 Linux 기반 파일 시스템인 Minix 파일 시스템을 사용하며  inode "Notes" 책에 자세히 설명되어 있습니다. 각 파일은 적어도 하나의 inode에 해당해야 하며 파일 유형을 포함하여 파일의 다양한 속성이 inode에 기록됩니다. 파일 유형에는 일반 파일, 디렉토리, 문자 장치 파일 및 블록 장치 파일이 포함됩니다. 커널에서 각 유형의 파일에는 그에 상응하는 다른 처리 기능이 있습니다. 새로운 파일 유형인  proc file을 추가하고 해당 처리 기능에서 실현할 기능을 실현할 수 있습니다 procfs

4. 실험과정

1. 새로운 파일 형식 추가

【힌트】

include/sys/stat.h 여러 파일 형식과 해당 테스트 매크로가 파일에 정의되어 있습니다

#define S_IFMT  00170000

// 普通文件
#define S_IFREG  0100000

// 块设备
#define S_IFBLK  0060000

// 目录
#define S_IFDIR  0040000

// 字符设备
#define S_IFCHR  0020000
#define S_IFIFO  0010000
//……

// 测试 m 是否是普通文件
#define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)

// 测试 m 是否是目录
#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)

// 测试 m 是否是字符设备
#define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)

// 测试 m 是否是块设备
#define S_ISBLK(m)      (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)

새 파일 유형을 추가하는 두 단계가 있습니다.

  •  값이 0010000에서 0100000 사이여야 하지만 8진수의 마지막 4자리가 0이어야 하는  유형 매크로를 정의합니다  (이것이 제한  사항이며 이유는 테스트 매크로를 분석하여 알 수 있음).  기존의 것.S_IFPROCS_IFMTS_IFXXX 
  •  다른 형식으로  테스트 매크로를 정의합니다  .S_ISPROC(m)S_ISXXX(m) 

C 언어에서 "0"이 있는 숫자에 직접 연결된 상수는 8진수입니다.

추가 시작:

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

2.  mknod()가 새로운 파일 형식을 지원하도록 합니다.

(1) mknod 시스템

psinfo 그리고 hdinfo노드는  mknod() 시스템 호출을 통해 생성되므로 새로운 파일 형식을 지원하도록 합니다 proc .

fs/namei.c 다음과 같이 파일의 함수에서 코드 라인을 수정  하고  if 판단에서 파일 시스템에 대한 판단을 추가합니다. sys_mknod()proc

if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISPROC(mode))
     inode->i_zone[0] = dev;
// 文件系统初始化
inode->i_mtime = inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
bh = add_entry(dir,basename,namelen,&de);

(2) procfs 초기화

【힌트】

커널 초기화의 모든 작업은  main() 에서 완료되며  main() 마지막에는 커널 모드에서 사용자 모드로 전환하고 를 호출합니다  init() .

init() 가장 먼저 할 일은 루트 파일 시스템을 마운트하는 것입니다.

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

procfs 초기화 작업은 루트 파일 시스템이 마운트된 후 시작해야 하며 다음 두 단계를 포함합니다.

  • 디렉터리를 만들고 디렉터리 아래에 각 노드를 만듭니다 . /proc  /proc 
  • 디렉토리와 노드를 생성하려면 각각 호출   과   시스템 호출이 필요합니다. 초기화할 때 이미 사용자 모드에 있기 때문에  직접  호출할 수 없습니다 . 이 두 시스템 호출의 사용자 모드 인터페이스는 초기화 코드가 있는 파일, 즉 API에서 구현되어야 합니다.mkdir()mknod()sys_mkdir() sys_mknod() 
#ifndef __LIBRARY__
#define __LIBRARY__
#endif

_syscall2(int,mkdir,const char*,name,mode_t,mode)
_syscall3(int,mknod,const char*,filename,mode_t,mode,dev_t,dev)

디렉토리 및 노드의 해당 매개변수를 작성하십시오.

디렉토리를 생성할모드 매개변수의 값은 "0755"(rwxr-xr-x 에 해당  )일 수 있습니다. 즉, 루트 사용자만 이 디렉토리를 다시 쓸 수 있으며 다른 사람은 이 디렉토리에 들어가고 읽을 수 있습니다. . mkdir()  /proc 

procfs 는 읽기 전용 파일 시스템이므로   노드 mknod() 생성에 사용할 때 mode 매개 변수를 통해 읽기 전용으로 설정해야 합니다.  모든 사용자에게 읽기 전용인 0444(r--r--r--) 권한이 있는 proc 파일임을 나타내는 모드 값으로 사용하는 것이 좋습니다 psinfoS_IFPROC|0444

mknod()  세 번째 매개 변수 dev는 노드가 나타내는 장치 번호를 설명하는 데 사용됩니다. procfs의 경우 이 숫자는 완전히 사용자 정의할 수 있습니다. proc 파일의 처리 기능은 이 번호를 사용하여 해당 파일에 포함된 정보를 결정합니다. 예를 들어 psinfo에 0, hdinfo에 1, inodeinfo에 2를 입력할 수 있습니다.

추가 시작:

init/main.c 추가 mkdir()mknod()API:

init/main.c 다음  init 기능에서 초기화  procfs:

mkdir("/proc",0755);
mknod("/proc/psinfo",S_IFPROC|0444,0);
mknod("/proc/hdinfo",S_IFPROC|0444,1);
mknod("/proc/inodeinfo",S_IFPROC|0444,2);

【테스트 초기화 효과】 

이 작업이 아무 문제 없이 완료되면 0.11 커널을 컴파일하고 실행한 후 다음을  ll /proc 볼 수 있습니다.

cat을 통해 이 파일을 읽을 수도 있습니다.

위  inode->i_mode 는  mknod() 모드 설정이고 이전 030은  S_IFPROC 우리가 설정한 것과 관련이 있습니다. mknod() 이 값을 사용하여 제대로 작동하는지 확인하십시오  . 위의 정보는 커널이  psinfo、hdinfo、inodeinfo 읽기 작업을 올바르게 처리할 수 없으며 cat에 EINVAL 오류를 반환함을 보여줍니다. 핸들러 기능이 아직 구현되지 않았기 때문에 이는 정상입니다.

위의 정보는 적어도 psinfo、hdinfo、inodeinfo 그것이 정확하다는 것을  보여줍니다 open() . 따라서 우리는 아무것도 할 필요가 없습니다  sys_open() . 패치가 필요한 유일한 것은  sys_read() .

3. proc 파일을 읽을 수 있게 만듭니다.

【힌트】

첫 번째 구문 분석  sys_read( 파일에서  fs/read_write.c ):

int sys_read(unsigned int fd,char * buf,int count)
{
    struct file * file;
    struct m_inode * inode;
//    ……
    inode = file->f_inode;
    if (inode->i_pipe)
        return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;
    if (S_ISCHR(inode->i_mode))
        return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);
    if (S_ISBLK(inode->i_mode))
        return block_read(inode->i_zone[0],&file->f_pos,buf,count);
    if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
        if (count+file->f_pos > inode->i_size)
            count = inode->i_size - file->f_pos;
        if (count<=0)
            return 0;
        return file_read(inode,file,buf,count);
    }

    printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);    //这条信息很面善吧?
    return -EINVAL;
}

ifs here 그룹 비교에서  S_IFPROC() proc 파일의 처리 기능에 분기를 추가하기 위해 처리 기능에 전달해야 하는 매개변수는 다음과 같습니다.

  • inode->i_zone[0], 이것은  mknod() 당시에 지정된  dev 장치 번호입니다.
  • buf사용자 공간을 가리키는 는  read() 데이터를 수신하는 데 사용되는 의 두 번째 매개변수입니다.
  • count의  read() 세 번째 매개변수로  buf 가리키는 버퍼의 크기를 나타냅니다.
  • &file->f_pos, f_pos 마지막으로 읽은 파일의 끝에 있는 "파일 위치 포인터"의 지점입니다. buf 처리 함수는 전달된 데이터의 양에 따라 값을 수정  해야 하므로 포인터를 여기에 전달해야 합니다  f_pos .

(1) proc_read 함수 외부 호출 추가

proc_read 함수가 외부에서 호출되었음을 나타내기 위해 extern proc_read를 추가합니다  .fs/read_write.c 

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

(2) proc 파일의 proc_read() 호출 추가

함수 에서 sys_read다른 if 문을 따르고 S_IFPROC()분기를 추가하고 proc 파일에 proc_read() 호출 처리를 추가합니다.

/* 新增proc_read调用 */
if (S_ISPROC(inode->i_mode))
    return proc_read(inode->i_zone[0],&file->f_pos,buf,count);

4. proc 파일의 처리 기능 실현

【힌트】

proc 파일의 처리 기능의 기능은 장치 번호에 따라 사용자 공간에 다른 내용을 쓰는 것입니다 buf. 기록된 데이터는  지정된 위치에서 시작하여 한 번에 최대 바이트까지  f_pos 기록 하고  기록된 실제 바이트 수에 따라 값을 조정하고 최종적으로 기록된 실제 바이트 수를 반환해야 합니다. 장치 번호가 psinfo의 내용을 읽어야 함을 나타내면 데이터를 psinfo 형식으로 구성해야 합니다.countf_pos

이 기능을 구현하기 위해 다음 기능을 사용할 수 있습니다.

  • 맬록()
  • 무료()

헤더 파일이 포함되면   및  기능을 linux/kernel.h 사용할 수 있습니다   . 핵심 상태 코드에 의해 호출될 수 있으며 한 번에 적용되는 메모리 크기가 한 페이지를 초과할 수 없다는 유일한 제한이 있습니다.malloc()free()

(1) proc.c 작성

디렉터리 에 새 파일을 fs추가합니다 proc.c.

코드 소스

#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;
}

(2) Makefile 수정

코드 트리에는 다양한 모듈의 컴파일을 담당하는 많은 Makefile이 있습니다. 위의 내용은  디렉토리 proc.c에 추가되었으므로 디렉토리의 Makefile도 그에 맞게 수정해야 합니다.fsfs

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

5. 컴파일 및 실행 

(현재 시스템 프로세스 상태 정보) 및  (하드 디스크 정보) 정보를 cat proc/xxx확인 하여 Linux 0.11을 다시 컴파일하고 실행합니다 .psinfohdinfo

추천

출처blog.csdn.net/Amentos/article/details/131469815