實(shí)驗(yàn)內(nèi)容在Linux0.11上實(shí)現(xiàn)procfs(proc文件系統(tǒng))內(nèi)的psinfo節(jié)點(diǎn),當(dāng)讀取此節(jié)點(diǎn)的內(nèi)容的時(shí)候,可得到系統(tǒng)當(dāng)前所有進(jìn)程的狀態(tài)信息,例如,用cat命令顯示/proc/procfo的內(nèi)容,可得到: # 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及其節(jié)點(diǎn)要在內(nèi)核啟動(dòng)時(shí)自動(dòng)創(chuàng)建,相關(guān)功能的實(shí)現(xiàn)放在fs/proc.c文件。
實(shí)驗(yàn)過(guò)程//已有的宏定義
#define S_IFMT 00170000 //文件類(lèi)型(都是8進(jìn)制表示)
#define S_IFREG 0100000 //普通文件
#define S_IFCHAR 0020000 //字符設(shè)備文件
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) //測(cè)試m是否是普通文件
#define S_ISCHAR(m) (((m) & S_IFMT) == S_IFCHAR) //測(cè)試m是否是字符設(shè)備文件
//proc文件的宏定義/宏函數(shù)
#define S_IFPROC 0030000
#define S_ISPROC(m) (((m) & S_IFMT) == S_IFPROC) //測(cè)試m是否是proc文件

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

void init(void)
{
setup((void *) &drive_info);
}
顯然在執(zhí)行setup((void *) &drive_info)的時(shí)候,也就是根文件系統(tǒng)掛載以后就可以創(chuàng)建proc文件了,首先建立/proc目錄,然后再建立該目錄下的各個(gè)proc文件節(jié)點(diǎn),建立目錄用mkdir(),建立文件用mknod()。 現(xiàn)在可以調(diào)用mkdir()來(lái)創(chuàng)建proc目錄,調(diào)用mknod()來(lái)創(chuàng)建proc目錄下的各個(gè)proc文件節(jié)點(diǎn)了。 內(nèi)核初始化的全部工作是在main()中完成,而/init/main()在最后從內(nèi)核態(tài)切換到用戶態(tài),并調(diào)用init()。init()做的第一件事情就是掛載根文件系統(tǒng):setup((void *) &drive_info); procfs的初始化工作應(yīng)該在根文件系統(tǒng)掛載之后開(kāi)始。它包括兩個(gè)步驟:
建立目錄和結(jié)點(diǎn)分別需要調(diào)用mkdir()和mknod()系統(tǒng)調(diào)用。因?yàn)槌跏蓟瘯r(shí)已經(jīng)在用戶態(tài),所以不能直接調(diào)用sys_mkdir()和sys_mknod()。必須在初始化代碼所在文件中實(shí)現(xiàn)這兩個(gè)系統(tǒng)調(diào)用的用戶態(tài)接口,即API:
_syscall2(int,mkdir,const char*,name,mode_t,mode)
_syscall3(int,mknod,const char *,filename,mode_t,mode,dev_t,dev)


mkdir()時(shí)mode參數(shù)的值可以是“0755”(rwxr-xr-x),表示只允許root用戶改寫(xiě)此目錄,其它人只能進(jìn)入和讀取此目錄。 procfs是一個(gè)只讀文件系統(tǒng),所以用mknod()建立psinfo結(jié)點(diǎn)時(shí),必須通過(guò)mode參數(shù)將其設(shè)為只讀。建議使用“S_IFPROC|0444”做為mode值,表示這是一個(gè)proc文件,權(quán)限為0444(r--r--r--),對(duì)所有用戶只讀。
mknod()的第三個(gè)參數(shù)dev用來(lái)說(shuō)明結(jié)點(diǎn)所代表的設(shè)備編號(hào)。對(duì)于procfs來(lái)說(shuō),此編號(hào)可以完全自定義。proc文件的處理函數(shù)將通過(guò)這個(gè)編號(hào)決定對(duì)應(yīng)文件包含的信息是什么。例如,可以把0對(duì)應(yīng)psinfo,1對(duì)應(yīng)hdinfo,2對(duì)應(yīng)inodeinfo。 上述步驟完成以后,就可以使用make all編譯內(nèi)核,然后./run運(yùn)行內(nèi)核,使用ll /proc可以看到:

inode->i_mode就是通過(guò)mknod()設(shè)置的mode。信息中的XXX和你設(shè)置的S_IFPROC有關(guān)。通過(guò)此值可以了解mknod()工作是否正常。這些信息說(shuō)明內(nèi)核在對(duì)psinfo進(jìn)行讀操作時(shí)不能正確處理,向cat返回了EINVAL錯(cuò)誤。因?yàn)檫€沒(méi)有實(shí)現(xiàn)處理函數(shù),所以這是很正常的。
注意:博主在此沒(méi)截使用cat命令的圖片,但是就是這個(gè)么道理,嘻嘻。
這些信息至少說(shuō)明,psinfo被正確open()了。所以我們不需要對(duì)sys_open()動(dòng)任何手腳,唯一要打補(bǔ)丁的,是sys_read()。 在.c文件中要引入另一個(gè)文件,而且是另一個(gè).c文件的全局變量或者函數(shù),就需要用extern來(lái)說(shuō)明一下。
 然后仿照其他if語(yǔ)句,添加proc文件的proc_read()調(diào)用。

#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沒(méi)有sprintf(),該函數(shù)是用于輸出結(jié)果到字符串中的,所以就實(shí)現(xiàn)一個(gè),這里是通過(guò)vsprintf()實(shí)現(xiàn)的。
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;
//硬盤(pán)總共有多少塊(空閑 非空閑),有多少inode索引節(jié)點(diǎn)等信息都放在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: 對(duì)應(yīng)的就是輸出系統(tǒng)此時(shí)的全部進(jìn)程的狀態(tài)信息
if(dev==0)
{
proc_buf=(char *)malloc(sizeof(char *)*1024);
file_size=sprintf(proc_buf,"pid\tstate\tfather\tcounter\tstart_time\n");
//這里借鑒了,進(jìn)程切換函數(shù)schedule()的代碼,也就是遍歷系統(tǒng)全部的進(jì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: 打印出硬盤(pán)的一些信息,
//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緩沖區(qū)的內(nèi)容放入文件
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;
}
由于添加了一個(gè)文件proc.c,所以需要改下fs/Makefile,

 上述的代碼,是用的這位同學(xué)的,在此表示感謝! (但是我搞不清為什么是proc_dev,添加的文件不是proc.c嗎,如果有人可以解答,可以評(píng)論下。)
然后make all編譯內(nèi)核,./run運(yùn)行內(nèi)核,輸出cat命令,即可查看psinfo(當(dāng)前系統(tǒng)進(jìn)程狀態(tài)信息)和hdinfo(硬盤(pán)信息)的信息。

實(shí)驗(yàn)問(wèn)題我會(huì)實(shí)現(xiàn)meminfo、cpuinfo這些節(jié)點(diǎn),分別對(duì)應(yīng)的信息是系統(tǒng)內(nèi)存信息和cpu的信息,原因是我只知道這兩個(gè)名詞了
我認(rèn)為后幾次read()傳遞給用戶的數(shù)據(jù)應(yīng)該是變化前的,因?yàn)樽x完一部分?jǐn)?shù)據(jù)之后,之前讀取的進(jìn)程狀態(tài)信息可能已經(jīng)發(fā)生了改變了,那么讀給proc_buf緩沖區(qū)的內(nèi)容還是讀之前的,所以可能會(huì)導(dǎo)致讀到的數(shù)據(jù)出現(xiàn)混亂(數(shù)據(jù)不正確?),要使得數(shù)據(jù)讀的正確、不混亂,就要在讀的時(shí)候讓想要變化的進(jìn)程先等待,等讀完了數(shù)據(jù)放到了文件中以后,再喚醒要更新的進(jìn)程。
注:我的答案很可能不準(zhǔn)確,請(qǐng)別被我誤導(dǎo)了。
HIT-OS-LAB參考資料: 1.《操作系統(tǒng)原理、實(shí)現(xiàn)與實(shí)踐》-李治軍、劉宏偉 編著 2.《Linux內(nèi)核完全注釋》 3.兩個(gè)哈工大同學(xué)的實(shí)驗(yàn)源碼 4.Linux-0.11源代碼 (上述資料,如果有需要的話,請(qǐng)主動(dòng)聯(lián)系我))
|