摘要:/proc文件系統(tǒng)是一個(gè)特殊的、由軟件創(chuàng)建的文件系統(tǒng),內(nèi)核使用此文件系統(tǒng)可以向外部輸出信息。在進(jìn)行Linux和實(shí)時(shí)應(yīng)用程序開發(fā)時(shí),用戶經(jīng)常會(huì)使用內(nèi)核變量,獲取內(nèi)核信息。文章分析了利用/proe文件系統(tǒng)獲取內(nèi)核信息的方法以及/proc文件系統(tǒng)在實(shí)時(shí)系統(tǒng)RTAI中的應(yīng)用。
關(guān)鍵詞:/proc文件系統(tǒng);Linux;內(nèi)核信息;RTAI
0 引言
/proc文件系統(tǒng)是Linux向用戶提供系統(tǒng)內(nèi)部參數(shù)的一個(gè)偽文件系統(tǒng),它包含許多文件,可用于監(jiān)視、調(diào)試或更改運(yùn)行中內(nèi)核的參數(shù)。對(duì)于內(nèi)核信息的獲取,一般僅使用其中的一部分文件。對(duì)這些文件執(zhí)行的讀操作,會(huì)被,proc文件系統(tǒng)定位到一個(gè)內(nèi)核函數(shù)。本文通過對(duì)Linux系統(tǒng)內(nèi)核現(xiàn)有通信機(jī)制的研究,介紹利用/proc文件系統(tǒng)輸出內(nèi)核信息的方法以及在實(shí)時(shí)系統(tǒng)RTAI中利用/proc文件系統(tǒng)控制實(shí)時(shí)任務(wù)的方法。
1/proc文件系統(tǒng)概述
Linux系統(tǒng)中的/proc文件系統(tǒng)是進(jìn)程文件系統(tǒng)和內(nèi)核文件系統(tǒng)組成的復(fù)合體,它將內(nèi)核數(shù)據(jù)對(duì)象化為文件形式進(jìn)行存取,通過文件系統(tǒng)接口實(shí)現(xiàn),用于輸出系統(tǒng)運(yùn)行狀態(tài);以文件系統(tǒng)的形式為操作系統(tǒng)和應(yīng)用進(jìn)程之間的通信提供了一個(gè)界面,使應(yīng)用程序能夠安全、方便地獲得系統(tǒng)當(dāng)前的運(yùn)行狀態(tài)和內(nèi)核的數(shù)據(jù)信息,并且可以修改某些系統(tǒng)的配置信息。
/proc文件系統(tǒng)主要分成兩個(gè)部分:一部分是和進(jìn)程相關(guān)的目錄部分,在實(shí)現(xiàn)時(shí)將這部分稱為base部分;另一部分是把/proc根下面的其他目錄和文件,又分為兩部分,一是/proc下的子目錄,另一是/proc下的文件,如cpuinfo等。這三部分是通過不同的初始化函數(shù)完成初始化的。/proe目錄下的每一個(gè)文件都與內(nèi)核函數(shù)緊密相連,當(dāng)文件被讀取時(shí)內(nèi)核函數(shù)生成文件的“內(nèi)容”。/proe文件系統(tǒng)是動(dòng)態(tài)的,可以在其中創(chuàng)建自己的/proc文件,獲取所需內(nèi)核信息。
Linux系統(tǒng)啟動(dòng)后,創(chuàng)建了由proc_dir_entry{}結(jié)構(gòu)形成的文件系統(tǒng)樹。在/proc文件系統(tǒng)中,代表各個(gè)文件節(jié)點(diǎn)的結(jié)構(gòu)是proc_dir_entry{}結(jié)構(gòu),它描述一個(gè)/proc文件系統(tǒng)中目錄結(jié)構(gòu)節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)在整個(gè)目錄結(jié)構(gòu)中或是一個(gè)文件,或是一個(gè)目錄,通過指針將大量的proc_dir_entry{}節(jié)點(diǎn)組成樹狀結(jié)構(gòu)。和一般文件系統(tǒng)不同的是,它修改的并不是硬盤上的文件,而是在系統(tǒng)啟動(dòng)之后內(nèi)存中由內(nèi)核動(dòng)態(tài)創(chuàng)建的文件。因此在系統(tǒng)關(guān)閉之后,/proc文件系統(tǒng)中的文件就不存在了。proc_dir_entry{}結(jié)構(gòu)提供了對(duì)文件內(nèi)容的讀寫所需要的函數(shù)指針,即管理著從操作系統(tǒng)的用戶空間到核心空間對(duì)文件讀寫的驅(qū)動(dòng)。每當(dāng)從用戶空間讀取,proc目錄下的文件時(shí),內(nèi)核根據(jù)讀取的文件映射到對(duì)應(yīng)的驅(qū)動(dòng)函數(shù),動(dòng)態(tài)地獲取內(nèi)核數(shù)據(jù)。除了提供讀的功能,/proe文件系統(tǒng)的部分文件還提供寫的功能,在Linux2.4系統(tǒng)中主要是針對(duì)/proc/sys目錄而做的。對(duì)/proc文件系統(tǒng)的寫操作并不是寫硬盤等硬件設(shè)備,而是動(dòng)態(tài)更改內(nèi)核中的數(shù)據(jù),達(dá)到監(jiān)視內(nèi)核運(yùn)行狀態(tài)的目的。
2 /proc目錄下文件節(jié)點(diǎn)的讀寫操作流程(以讀為例)
讀/proc文件系統(tǒng)中的文件,是通過層層調(diào)用,最終操作內(nèi)核變量的讀函數(shù),將內(nèi)核信息收集到字符型指針?biāo)赶虻木彌_區(qū)中。也就是說,用戶進(jìn)程通過系統(tǒng)調(diào)用read(讀文件)進(jìn)入系統(tǒng)態(tài),執(zhí)行sys_read內(nèi)核函數(shù);
asmtinkage ssize_t sys_read(unsigned int fd,char*buf,
size_t count)
{…
ssize_t ret;
struct file*file;
file=get(fd);
ret=file->read(file,buf,count,file->f_pos);
}
可見,文件讀操作進(jìn)入到核心態(tài)后,執(zhí)行的是file結(jié)構(gòu)中函數(shù)跳轉(zhuǎn)表f_op中的read指針?biāo)傅暮瘮?shù)。
file->f_op函數(shù)跳轉(zhuǎn)表值來源于inode結(jié)構(gòu)中的i_fop;
在訪問文件的過程中,dentry_open函數(shù)將文件索引節(jié)點(diǎn)中函數(shù)跳轉(zhuǎn)表i_fop值賦給file結(jié)構(gòu)中的f_op。
struct file *dentry_open(struct dentry*dentry,
struct vfsmount *mnt,int flags)
{struct file *f;
struct inode *inode;
inode=dentry->d_inode;
f->f_op=fops_get(jnode->ffop);
return f;
}
inode結(jié)構(gòu)中的i_fop值來源于proc_dir_entry中的proc_fops;
在創(chuàng)建/proc文件系統(tǒng)中文件的inode節(jié)點(diǎn)時(shí),函數(shù)proc_get_inode將文件對(duì)應(yīng)的proc_dir_entry中的proc_fops值賦給inode結(jié)構(gòu)中的i_fop。
struct inode *proc_get_inode(struct super_block *sb,int ino,
struct proc_dir_entry*de)
{struct inode *inode;
inode=iget(sb,ino):
inode->u.generic_ip=(void*)de;
if(de->proc_fops)
inode->i fop=de->proc_fops;
return inode;
}
proc_dir_entry結(jié)構(gòu)中的proc_fops是proc_file_operations;
在文件./linux/fs/proc/generic.c,proc_file_operations賦值為:
struct file_operations proc_file_operations=
{Ilseek:proc_file_lseek,
read::proc_file_read,
write:proc file write,
};
在創(chuàng)建文件的proc_dir_entry結(jié)構(gòu)時(shí),函數(shù)proc_register將proc_file_operations值賦給proc_dir_entry結(jié)構(gòu)中的proc_fops。
static int proc_register(struct proc_dir_entry*dir,
struct proc_dir_entry*dp)
{…
if(dp->proc_fops==NULL)
dp->proc_fops=proc__file_operations;
}
proc_file_read函數(shù)執(zhí)行的是proc_dir_entry結(jié)構(gòu)中函數(shù)指針?biāo)傅暮瘮?shù)。
static ssize_t proc_file_read(struct file*file,
const char*buffer,size_t count,Ioff_t *ppos)
{struct inode *inode=file->f_dentry->d_inode;
struct proc dir entry *dp;
dp=(struct proc_dir_entry*)->inode->u.generic_ip;
if(!dp->write_proc)
return EIO;
return dp->read_proc(file,buffer,count,dp->data);
}
proc_dir_entry結(jié)構(gòu)中proc_read函數(shù)指針賦值是在創(chuàng)建文件的proc_dir_entry結(jié)構(gòu)時(shí)進(jìn)行的。
文件./include/linux/proc_fs.h
static inline struct proc_dir_entry *create_proc_read_entry(const
char*name,mode_t mode,struct proc_dir_entry*base,
read_proc_t*read_proc,void*data)
{struct proc_dir_entry*res=create_proc_entry(name,mode,base);
if(res)
{res->read_proc=read_proc;
res->data=data;
}
return res;
}
函數(shù)create_proc_read_entry參數(shù)表中的參數(shù)read_proc是對(duì)應(yīng)內(nèi)核變量的讀函數(shù)。
3 使用/proc文件系統(tǒng)獲取內(nèi)核信息的方法
3.1修改/proc文件系統(tǒng)中原有的文件
/proc文件系統(tǒng)中,用戶進(jìn)程是直接打開/proc文件來實(shí)現(xiàn)自動(dòng)調(diào)用相應(yīng)內(nèi)核函數(shù)的。當(dāng)查看/proc目錄下的文件時(shí),這些文件會(huì)顯示出Linux系統(tǒng)內(nèi)部的一些信息。其實(shí)對(duì)它們進(jìn)行讀操作是調(diào)用了操作系統(tǒng)內(nèi)核中的一些對(duì)應(yīng)的函數(shù),這些函數(shù)及時(shí)將所取的信息反饋給用戶或應(yīng)用程序。因此在內(nèi)核中改造讀文件函數(shù),在讀/proc目錄下的對(duì)應(yīng)文件時(shí),可以得到所需的數(shù)據(jù)。
例如對(duì)于/proc/loadavg文件來說,讀文件指針指向loadavg_read_proc()函數(shù)。該函數(shù)的作用就是當(dāng)用戶讀/proc/loadavg文件時(shí),將數(shù)組avenrum[]中積累的,即在過去1分鐘、5分鐘以及15分鐘的系統(tǒng)平均CPU負(fù)荷等統(tǒng)計(jì)信息通過spfintf()“打印”到緩沖區(qū)頁面中。我們可以把代碼加到諸如此類的文件中,由這些文件幫我們把所需的數(shù)據(jù)輸出來。修改完代碼后,重新編譯內(nèi)核即可。在這里,內(nèi)核到用戶的信息通過/proc文件系統(tǒng)來傳遞,但沒有在/proc目錄下創(chuàng)建新文件,而是借用Linux內(nèi)核代碼,從而得到所需的數(shù)據(jù)。這樣做的好處是減少對(duì)內(nèi)核的修改量,缺點(diǎn)是需重新編譯內(nèi)核。
3.2在/proc目錄下新建文件
3.2.1創(chuàng)建新/proc文件,利用read_proc操作
新創(chuàng)建的/proc文件要想被訪問到,首先要在,proc文件系統(tǒng)中創(chuàng)建一個(gè)入口,這可以通過函數(shù)creat_proc_read_entry()來實(shí)現(xiàn)。
從上面分析可以看出,/proc目錄下的文件只提供了讀寫操作,即只實(shí)現(xiàn)了proc dir entry{}結(jié)構(gòu)中對(duì)應(yīng)的read_proc操作,而沒有提供該結(jié)構(gòu)中的文件操作函數(shù)集,此時(shí)使用的是缺省的文件操作函數(shù)集。文件操作函數(shù)集struct file_operationsproc_file_operations中read對(duì)應(yīng)函數(shù)proc_file_read(),該函數(shù)的實(shí)現(xiàn)依賴于proc_dir_entry{}結(jié)構(gòu)中的read_proc函數(shù)。因此若注冊自己的/proc文件,在沒有設(shè)置proc_fops文件操作函數(shù)集時(shí),需實(shí)現(xiàn)read_proc(),否則缺省的proc file read()函數(shù)將做不了任何工作。所以,當(dāng)創(chuàng)建只讀的/proc文件時(shí),程序中必須實(shí)現(xiàn)proc_dir_entry{}結(jié)構(gòu)中的read_proc()函數(shù)。
read_proc()函數(shù)的特點(diǎn)是:/proc文件系統(tǒng)首先通過sprintf()函數(shù)將要獲取的參數(shù)轉(zhuǎn)換成字符串寫入內(nèi)核中臨時(shí)分配的一個(gè)空頁,然后再將該頁用copy_to_user()送到用戶空間,最后釋放該頁。實(shí)際上,大部分內(nèi)核參數(shù)都是整型或長整型的。/proc文件系統(tǒng)將內(nèi)核數(shù)據(jù)轉(zhuǎn)換成字符串是為了方便用戶閱讀。
3.2.2創(chuàng)建新/proc文件,使用文件操作函數(shù)集
創(chuàng)建/proc文件的方法與創(chuàng)建字符設(shè)備文件的方法非常相似。首先創(chuàng)建一個(gè)proc_dir_entry{}結(jié)構(gòu),該結(jié)構(gòu)包含了/proc文件需要的所有信息。然后通過create_proc_entry()函數(shù)向內(nèi)核注冊這個(gè)結(jié)構(gòu),而remove_proc_entry()函數(shù)將取消它的注冊。proc_dir_entry{)結(jié)構(gòu)中描述了/proc文件的全部信息。這里最重要的是該結(jié)構(gòu)中的proc_fops項(xiàng),這是一個(gè)指向file_operations結(jié)構(gòu)的指針,可以把對(duì)/proc文件的讀寫等操作放在這個(gè)結(jié)構(gòu)中,用以實(shí)現(xiàn)對(duì)/proc文件的讀寫等功能。代碼如下:
int ssize_t procread(struct file*file,char*buf,aize_t len,loff_t*offset); static ssize_t procwrite(struct file*file,char*buf,aize_t Ien,
loff_t *offset);
static struct file_operations procfop=
{read:procread,
write:procwrite,
};
static int init_routine(void)
{struct proc_dir_entry *entry;
entry=create_proc_entry(\"test\",S_IRUSRIS_IwUSR,NULL);
entry->proc_fops=procfop;
rutern;
}
4 /proc文件系統(tǒng)在RTAI中的應(yīng)用
RTAI是實(shí)時(shí)應(yīng)用接口Real-Time Application Interface的縮寫,提供了一個(gè)基于Linux的實(shí)時(shí)方案。RTAI利用Linux提供的可加載內(nèi)核模塊機(jī)制來提供服務(wù),完成實(shí)時(shí)功能。模塊經(jīng)過加載,成為內(nèi)核的一部分,但是并沒有被編譯到內(nèi)核里面去。它們被分別編譯并連接成一組目標(biāo)文件,這些文件能被插入到正在運(yùn)行的內(nèi)核,或從正在運(yùn)行的內(nèi)核中移走。RTAI提供的主要模塊有RTAI主模塊、RTAI調(diào)度器模塊、RTAI命名管道fifo模塊及RTAI共享內(nèi)存模塊等。
4.1利用/proc文件系統(tǒng)用戶獲取實(shí)時(shí)內(nèi)核的信息
RTAI主模塊是RTAI的核心模塊,它是其他模塊的基礎(chǔ)。要加載其他模塊,首先必須加載這個(gè)模塊,沒有它,任何實(shí)時(shí)任務(wù)都不能完成。當(dāng)這個(gè)模塊被加載后,在/proc目錄下會(huì)自動(dòng)生成一個(gè)rtai子目錄,在其他模塊如rtai_sched、rtai_fifos等模塊加載后,rtai子目錄下又有rtai、scheduler、fifos等文件??梢酝ㄟ^命令cat讀取實(shí)時(shí)系統(tǒng)RTAI的有關(guān)信息,主要由…/rtai-x.x.x/arch/i386/rtai.c文件中的函數(shù)實(shí)現(xiàn)其功能:
struct proc dir entry *rtai_proc_root=NULL;
static int rtai_proc_register(void)
{…
rtai_proc_root=create_proc_entry(“rtai”,S_IFDIR,0);
ent=create_proc_entry(“rtai”,S_IFREGIS_IRUGOIS_IwUSR,
rtai_proc_root);
ent->read proc=rtai_read_rtai;
}
4.2 利用/proc文件系統(tǒng)控制實(shí)時(shí)任務(wù)
利用/proc文件系統(tǒng)write_proc函數(shù)可以修改內(nèi)核數(shù)據(jù),控制實(shí)時(shí)任務(wù)。首先使用create_proc_entry函數(shù)在/proc文件系統(tǒng)中創(chuàng)建一個(gè)虛擬文件,這個(gè)函數(shù)可以接收一個(gè)文件名、一組權(quán)限和這個(gè)文件在,proc文件系統(tǒng)中出現(xiàn)的位置。create_proc_entry的返回值是一個(gè)proc_dir_entry指針(或者為NULL,說明在create時(shí)發(fā)生了錯(cuò)誤)。然后就可以使用這個(gè)返回的指針來配置這個(gè)虛擬文件的其他參數(shù),例如在對(duì)該虛擬文件執(zhí)行寫操作時(shí)應(yīng)該調(diào)用的write_proc函數(shù)。通過/proc文件的write_proc函數(shù),修改內(nèi)核中可以控制實(shí)時(shí)任務(wù)執(zhí)行的變量,從而達(dá)到控制實(shí)時(shí)任務(wù)的目的。
5 結(jié)束語
Linux、RTAI都是開放源代碼的,我們可以采取任何能想到的方法對(duì)它進(jìn)行分析和改進(jìn),這是開放源代碼提供給我們研究和學(xué)習(xí)的便利。但也應(yīng)注意避免向內(nèi)核中帶入一些難以發(fā)現(xiàn)的錯(cuò)誤。本文所提出的利用/proc文件實(shí)現(xiàn)實(shí)時(shí)系統(tǒng)監(jiān)控和實(shí)時(shí)任務(wù)控制的方法,簡單易行,用于開發(fā)應(yīng)用程序非常方便。
注:本文中所涉及到的圖表、注解、公式等內(nèi)容請以PDF格式閱讀原文。