實(shí)驗內(nèi)容在Linux0.11上實(shí)現(xiàn)procfs(proc文件系統(tǒng))內(nèi)的psinfo節(jié)點(diǎn),當(dāng)讀取此節(jié)點(diǎn)的內(nèi)容的時候,可得到系統(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)核啟動時自動創(chuàng)建,相關(guān)功能的實(shí)現(xiàn)放在fs/proc.c 文件。
實(shí)驗過程//已有的宏定義
#define S_IFMT 00170000 //文件類型(都是8進(jìn)制表示)
#define S_IFREG 0100000 //普通文件
#define S_IFCHAR 0020000 //字符設(shè)備文件
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) //測試m是否是普通文件
#define S_ISCHAR(m) (((m) & S_IFMT) == S_IFCHAR) //測試m是否是字符設(shè)備文件
//proc文件的宏定義/宏函數(shù)
#define S_IFPROC 0030000
#define S_ISPROC(m) (((m) & S_IFMT) == S_IFPROC) //測試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) 的時候,也就是根文件系統(tǒng)掛載以后就可以創(chuàng)建proc 文件了,首先建立/proc 目錄,然后再建立該目錄下的各個proc 文件節(jié)點(diǎn),建立目錄用mkdir() ,建立文件用mknod() 。 現(xiàn)在可以調(diào)用mkdir() 來創(chuàng)建proc 目錄,調(diào)用mknod() 來創(chuàng)建proc 目錄下的各個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)掛載之后開始。它包括兩個步驟:
建立目錄和結(jié)點(diǎn)分別需要調(diào)用mkdir()和mknod()系統(tǒng)調(diào)用。因為初始化時已經(jīng)在用戶態(tài),所以不能直接調(diào)用sys_mkdir()和sys_mknod()。必須在初始化代碼所在文件中實(shí)現(xiàn)這兩個系統(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() 時mode 參數(shù)的值可以是“0755”(rwxr-xr-x) ,表示只允許root 用戶改寫此目錄,其它人只能進(jìn)入和讀取此目錄。 procfs是一個只讀文件系統(tǒng),所以用mknod() 建立psinfo 結(jié)點(diǎn)時,必須通過mode 參數(shù)將其設(shè)為只讀。建議使用“S_IFPROC|0444” 做為mode 值,表示這是一個proc 文件,權(quán)限為0444(r--r--r--) ,對所有用戶只讀。
mknod() 的第三個參數(shù)dev 用來說明結(jié)點(diǎn)所代表的設(shè)備編號。對于procfs 來說,此編號可以完全自定義。proc 文件的處理函數(shù)將通過這個編號決定對應(yīng)文件包含的信息是什么。例如,可以把0 對應(yīng)psinfo ,1 對應(yīng)hdinfo ,2對應(yīng)inodeinfo 。 上述步驟完成以后,就可以使用make all 編譯內(nèi)核,然后./run 運(yùn)行內(nèi)核,使用ll /proc 可以看到:

inode->i_mode 就是通過mknod() 設(shè)置的mode 。信息中的XXX 和你設(shè)置的S_IFPROC 有關(guān)。通過此值可以了解mknod() 工作是否正常。這些信息說明內(nèi)核在對psinfo 進(jìn)行讀操作時不能正確處理,向cat 返回了EINVAL 錯誤。因為還沒有實(shí)現(xiàn)處理函數(shù),所以這是很正常的。
注意:博主在此沒截使用cat命令的圖片,但是就是這個么道理,嘻嘻。
這些信息至少說明,psinfo 被正確open() 了。所以我們不需要對sys_open() 動任何手腳,唯一要打補(bǔ)丁的,是sys_read() 。 在.c文件中要引入另一個文件,而且是另一個.c文件的全局變量或者函數(shù),就需要用extern來說明一下。
 然后仿照其他if 語句,添加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沒有sprintf(),該函數(shù)是用于輸出結(jié)果到字符串中的,所以就實(shí)現(xiàn)一個,這里是通過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;
//硬盤總共有多少塊(空閑 非空閑),有多少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: 對應(yīng)的就是輸出系統(tǒng)此時的全部進(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: 打印出硬盤的一些信息,
//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;
}
由于添加了一個文件proc.c ,所以需要改下fs/Makefile ,

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

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