1. 前言
linux系統上的/proc目錄是一種文件系統,即proc文件系統。
與其它常見的文件系統不同的是,/proc是一種偽文件系統(也即虛擬文件系統),存儲的是當前內核運行狀態的一系列特殊文件,用戶可以通過這些文件查看有關系統硬件及當前正在運行進程的信息,甚至可以通過更改其中某些文件來改變內核的運行狀態。
當前的實驗平臺是嵌入式Linux開發板,根文件系統掛載成功后,進入命令就能看到proc目錄,這個目錄里正常情況下已經生成了很多文件。通過cat命令讀取這些文件,可以得到很多內核的信息。
比如:查看中斷有哪些注冊了,中斷從上電到現在響應了多少次,雜項設備注冊了哪些,幀緩沖節點有哪些,RTC時間查看,等等。
下面是proc目錄下文件的功能的詳細介紹(資源來源與網絡):
2.1、/proc/apm
高級電源管理(APM)版本信息及電池相關狀態信息,通常由apm命令使用;
2.2、/proc/buddyinfo
用于診斷內存碎片問題的相關信息文件;
2.3、/proc/cmdline

在啟動時傳遞至內核的相關參數信息,這些信息通常由lilo或grub等啟動管理工具進行傳遞;
2.4、/proc/cpuinfo
處理器的相關信息的文件;
2.5、/proc/crypto
系統上已安裝的內核使用的密碼算法及每個算法的詳細信息列表;
2.6、/proc/devices
系統已經加載的所有塊設備和字符設備的信息,包含主設備號和設備組(與主設備號對應的設備類型)名;
2.7、/proc/diskstats
每塊磁盤設備的磁盤I/O統計信息列表;(內核2.5.69以后的版本支持此功能)
2.8、/proc/dma
每個正在使用且注冊的ISA DMA通道的信息列表;
2.9、/proc/execdomains
內核當前支持的執行域(每種操作系統獨特“個性”)信息列表;
2.10、/proc/fb
幀緩沖設備列表文件,包含幀緩沖設備的設備號和相關驅動信息;
2.11、/proc/filesystems
當前被內核支持的文件系統類型列表文件,被標示為nodev的文件系統表示不需要塊設備的支持;通常mount一個設備時,如果沒有指定文件系統類型將通過此文件來決定其所需文件系統的類型;
2.12、/proc/interrupts
X86或X86_64體系架構系統上每個IRQ相關的中斷號列表;多路處理器平臺上每個CPU對于每個I/O設備均有自己的中斷號;
2.13、/proc/iomem
每個物理設備上的記憶體(RAM或者ROM)在系統內存中的映射信息;
2.14、/proc/ioports
當前正在使用且已經注冊過的與物理設備進行通訊的輸入-輸出端口范圍信息列表;如下面所示,第一列表示注冊的I/O端口范圍,其后表示相關的設備;
2.15、/proc/kallsyms
模塊管理工具用來動態鏈接或綁定可裝載模塊的符號定義,由內核輸出;(內核2.5.71以后的版本支持此功能);通常這個文件中的信息量相當大;
2.16、/proc/kcore
系統使用的物理內存,以ELF核心文件(core file)格式存儲,其文件大小為已使用的物理內存(RAM)加上4KB;這個文件用來檢查內核數據結構的當前狀態,因此,通常由GBD通常調試工具使用,但不能使用文件查看命令打開此文件;
2.17、/proc/kmsg
此文件用來保存由內核輸出的信息,通常由/sbin/klogd或/bin/dmsg等程序使用,不要試圖使用查看命令打開此文件;
2.18、/proc/loadavg
保存關于CPU和磁盤I/O的負載平均值,其前三列分別表示每1秒鐘、每5秒鐘及每15秒的負載平均值,類似于uptime命令輸出的相關信息;第四列是由斜線隔開的兩個數值,前者表示當前正由內核調度的實體(進程和線程)的數目,后者表示系統當前存活的內核調度實體的數目;第五列表示此文件被查看前最近一個由內核創建的進程的PID;
2.19、/proc/locks
保存當前由內核鎖定的文件的相關信息,包含內核內部的調試數據;每個鎖定占據一行,且具有一個惟一的編號;如下輸出信息中每行的第二列表示當前鎖定使用的鎖定類別,POSIX表示目前較新類型的文件鎖,由lockf系統調用產生,FLOCK是傳統的UNIX文件鎖,由flock系統調用產生;第三列也通常由兩種類型,ADVISORY表示不允許其他用戶鎖定此文件,但允許讀取,MANDATORY表示此文件鎖定期間不允許其他用戶任何形式的訪問;
2.20、/proc/mdstat
保存RAID相關的多塊磁盤的當前狀態信息,在沒有使用RAID機器上,其顯示為如下狀態:
2.21、/proc/meminfo
系統中關于當前內存的利用狀況等的信息,常由free命令使用;可以使用文件查看命令直接讀取此文件,其內容顯示為兩列,前者為統計屬性,后者為對應的值;
2.22、/proc/mounts
在內核2.4.29版本以前,此文件的內容為系統當前掛載的所有文件系統,在2.4.19以后的內核中引進了每個進程使用獨立掛載名稱空間的方式,此文件則隨之變成了指向/proc/self/mounts(每個進程自身掛載名稱空間中的所有掛載點列表)文件的符號鏈接;/proc/self是一個獨特的目錄,后文中會對此目錄進行介紹;
2.23、/proc/modules
當前裝入內核的所有模塊名稱列表,可以由lsmod命令使用,也可以直接查看;如下所示,其中第一列表示模塊名,第二列表示此模塊占用內存空間大小,第三列表示此模塊有多少實例被裝入,第四列表示此模塊依賴于其它哪些模塊,第五列表示此模塊的裝載狀態(Live:已經裝入;Loading:正在裝入;Unloading:正在卸載),第六列表示此模塊在內核內存(kernel memory)中的偏移量;
2.24、/proc/partitions
塊設備每個分區的主設備號(major)和次設備號(minor)等信息,同時包括每個分區所包含的塊(block)數目(如下面輸出中第三列所示);
2.25、/proc/pci
內核初始化時發現的所有PCI設備及其配置信息列表,其配置信息多為某PCI設備相關IRQ信息,可讀性不高,可以用“/sbin/lspci –vb”命令獲得較易理解的相關信息;在2.6內核以后,此文件已為/proc/bus/pci目錄及其下的文件代替;
2.26、/proc/slabinfo
在內核中頻繁使用的對象(如inode、dentry等)都有自己的cache,即slab pool,而/proc/slabinfo文件列出了這些對象相關slap的信息;詳情可以參見內核文檔中slapinfo的手冊頁;
2.27、/proc/stat
實時追蹤自系統上次啟動以來的多種統計信息;如下所示,其中,
“cpu”行后的八個值分別表示以1/100(jiffies)秒為單位的統計值(包括系統運行于用戶模式、低優先級用戶模式,運系統模式、空閑模式、I/O等待模式的時間等);
“intr”行給出中斷的信息,第一個為自系統啟動以來,發生的所有的中斷的次數;然后每個數對應一個特定的中斷自系統啟動以來所發生的次數;
“ctxt”給出了自系統啟動以來CPU發生的上下文交換的次數。
“btime”給出了從系統啟動到現在為止的時間,單位為秒;
“processes (total_forks) 自系統啟動以來所創建的任務的個數目;
“procs_running”:當前運行隊列的任務的數目;
“procs_blocked”:當前被阻塞的任務的數目;
2.29、/proc/uptime
系統上次啟動以來的運行時間,如下所示,其第一個數字表示系統運行時間,第二個數字表示系統空閑時間,單位是秒;
2.32、/proc/zoneinfo
內存區域(zone)的詳細信息列表,信息量較大
2. 獲取CPU使用率
下面這份代碼是利用/proc/stat文件獲取當前CPU的占用率詳細信息,通過C語言代碼讀取數據后,進行分析,處理。
#include #include #include typedef struct cpu_occupy_ //定義一個cpu occupy的結構體 { char name[20]; //定義一個char類型的數組名name有20個元素 unsigned int user; //定義一個無符號的int類型的user unsigned int nice; //定義一個無符號的int類型的nice unsigned int system; //定義一個無符號的int類型的system unsigned int idle; //定義一個無符號的int類型的idle unsigned int iowait; unsigned int irq; unsigned int softirq; }cpu_occupy_t; double cal_cpuoccupy (cpu_occupy_t *o, cpu_occupy_t *n) { double od, nd; double id, sd; double cpu_use ; od = (double) (o->user + o->nice + o->system +o->idle+o->softirq+o->iowait+o->irq);//第一次(用戶+優先級+系統+空閑)的時間再賦給od nd = (double) (n->user + n->nice + n->system +n->idle+n->softirq+n->iowait+n->irq);//第二次(用戶+優先級+系統+空閑)的時間再賦給od id = (double) (n->idle); //用戶第一次和第二次的時間之差再賦給id sd = (double) (o->idle) ; //系統第一次和第二次的時間之差再賦給sd if((nd-od) != 0) cpu_use =100.0 - ((id-sd))/(nd-od)*100.00; //((用戶+系統)乖100)除(第一次和第二次的時間差)再賦給g_cpu_used else cpu_use = 0; return cpu_use; } void get_cpuoccupy (cpu_occupy_t *cpust) { FILE *fd; int n; char buff[256]; cpu_occupy_t *cpu_occupy; cpu_occupy=cpust; fd = fopen ("/proc/stat", "r"); if(fd == NULL) { perror("fopen:"); exit (0); } fgets (buff, sizeof(buff), fd); sscanf (buff, "%s %u %u %u %u %u %u %u", cpu_occupy->name, &cpu_occupy->user, &cpu_occupy->nice,&cpu_occupy->system, &cpu_occupy->idle ,&cpu_occupy->iowait,&cpu_occupy->irq,&cpu_occupy->softirq); fclose(fd); } double get_sysCpuUsage() { cpu_occupy_t cpu_stat1; cpu_occupy_t cpu_stat2; double cpu; get_cpuoccupy((cpu_occupy_t *)&cpu_stat1); sleep(1); //第二次獲取cpu使用情況 get_cpuoccupy((cpu_occupy_t *)&cpu_stat2); //計算cpu使用率 cpu = cal_cpuoccupy ((cpu_occupy_t *)&cpu_stat1, (cpu_occupy_t *)&cpu_stat2); return cpu; } int main(int argc,char **argv) { while(1) { printf("CPU占用率:%f\n",get_sysCpuUsage()); } return 0; }
3. proc驅動相關接口
Proc文件接口,主要用于驅動代碼調試,獲取內核信息,可以直接使用cat命令訪問proc目錄下的對應文件接口即可。
需要使用的頭文件:
#include #include
下面介紹內核里proc接口實現的相關函數接口:
1. 在proc目錄下創建子目錄函數 static inline struct proc_dir_entry *proc_mkdir(const char *name,struct proc_dir_entry *parent) 示例: //注意只能創建單層目錄 //在proc目錄下創建aaa文件夾 proc_mkdir("aaa",NULL); 2. 在proc目錄下創建文件 static inline struct proc_dir_entry *proc_create(const char *name, //文件名稱 umode_t mode, //模式,默認為0 struct proc_dir_entry *parent, //父目錄結構 const struct file_operations *proc_fops) //文件集合 示例: //在proc目錄下創建一個文件 proc_create("aaa/tiny4412_proc_test", 0, NULL, &fops_proc); 3. 刪除proc目錄下之前創建的文件或者目錄 void remove_proc_entry(const char *name, //文件的路徑 struct proc_dir_entry *parent //父目錄結構 ) 示例: remove_proc_entry("aaa/tiny4412_proc_test", NULL); 注意: 如果是刪除目錄,需要先把目錄下的文件刪除掉,每次刪除必須保證目錄是空的。
4. 編寫proc接口測試驅動
4.1 案例1
下面驅動代碼注冊之后,會在proc目錄下創建一個tiny4412_proc文件,通過cat讀取這個文件,可以打印驅動代碼里設置好的信息。驅動卸載時會刪除這個tiny4412_proc文件。
4.2 案例2
下面這份代碼是在字符設備框架代碼里增加了proc接口,驅動安裝之后,會在proc目錄下創建tiny4412_proc文件,通過cat命令讀取tiny4412_proc文件,可以打印出當前主設備號下所有的子設備信息。
#include #include #include #include #include #include #include #include #include #include #include #include #include static struct class *tiny4412_beep_class; static unsigned int major=0; //主設備號 static LIST_HEAD(tiny4412_beep_list); //鏈表頭 static DEFINE_MUTEX(tiny4412_beep_mtx); //互斥鎖 #define DYNAMIC_MINORS 64 /* like dynamic majors */ static DECLARE_BITMAP(beep_minors, DYNAMIC_MINORS); struct tiny4412_beep_device { int minor; /*次設備號*/ const char *name; /*設備節點的名稱*/ const struct file_operations *fops; /*文件操作集合*/ struct list_head list; //鏈表 }; int tiny4412_beep_register(struct tiny4412_beep_device *beep_dev) { struct tiny4412_beep_device *c; dev_t dev; INIT_LIST_HEAD(&beep_dev->list); mutex_lock(&tiny4412_beep_mtx); //查找傳入的次設備號是否沖突 list_for_each_entry(c, &tiny4412_beep_list, list) { if(c->minor == beep_dev->minor) { mutex_unlock(&tiny4412_beep_mtx); return -EBUSY; } } //自動分配 if(beep_dev->minor == MISC_DYNAMIC_MINOR) { int i = find_first_zero_bit(beep_minors,DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS) { mutex_unlock(&tiny4412_beep_mtx); return -EBUSY; } beep_dev->minor = DYNAMIC_MINORS - i - 1; set_bit(i,beep_minors); } //合成設備號 dev = MKDEV(major, beep_dev->minor); //創建設備節點 device_create(tiny4412_beep_class,NULL,dev,NULL,"%s", beep_dev->name); list_add(&beep_dev->list,&tiny4412_beep_list); //解鎖 mutex_unlock(&tiny4412_beep_mtx); return 0; } int tiny4412_beep_deregister(struct tiny4412_beep_device *beep_dev) { int i = DYNAMIC_MINORS - beep_dev->minor - 1; mutex_lock(&tiny4412_beep_mtx); list_del(&beep_dev->list); //將dev目錄下的文件刪除掉 device_destroy(tiny4412_beep_class, MKDEV(major, beep_dev->minor)); if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, beep_minors); mutex_unlock(&tiny4412_beep_mtx); return 0; } EXPORT_SYMBOL_GPL(tiny4412_beep_register); EXPORT_SYMBOL_GPL(tiny4412_beep_deregister); //底層open函數 static int tiny4412_beep_open(struct inode * inode, struct file * file) { //得到次設備號 int minor = iminor(inode); struct tiny4412_beep_device *c; struct file_operations *new_fops,*old_fops; mutex_lock(&tiny4412_beep_mtx); //遍歷鏈表--找到鏈表里相同的次設備號 list_for_each_entry(c,&tiny4412_beep_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); //得到47次設備號對應的結構體地址 break; } } file->f_op = new_fops; //改變指向--文件操作集合的指向 if(file->f_op->open) { file->f_op->open(inode,file); } fops_put(old_fops); mutex_unlock(&tiny4412_beep_mtx); return 0; } static const struct file_operations tiny4412_beep_fops = { .owner = THIS_MODULE, .open = tiny4412_beep_open, }; static ssize_t tiny4412_read(struct file *file, char __user *buf, size_t cnt, loff_t *loff) { struct tiny4412_beep_device *c; //遍歷鏈表--找到鏈表里相同的次設備號 list_for_each_entry(c,&tiny4412_beep_list, list) { printk("%d %s\n",c->minor,c->name); } return 0; } static struct file_operations tiny4412_fops= { .read=tiny4412_read, }; static int __init tiny4412_beep_class_init(void) { /*1. 創建設備類*/ tiny4412_beep_class=class_create(THIS_MODULE,"tiny4412_beep"); /*2. 注冊字符設備*/ major=register_chrdev(0,"tiny4412_beep",&tiny4412_beep_fops); proc_mkdir("wbyq",0); /*創建內核接口: proc 存放內核信息*/ proc_create("wbyq/tiny4412_proc",0, NULL, &tiny4412_fops); return 0; } static void __exit tiny4412_beep_class_cleanup(void) { remove_proc_entry("wbyq/tiny4412_proc", NULL); remove_proc_entry("wbyq", NULL); //注銷設備類 class_destroy(tiny4412_beep_class); //注銷字符設備 unregister_chrdev(major,"tiny4412_beep"); } module_init(tiny4412_beep_class_init); module_exit(tiny4412_beep_class_cleanup); MODULE_LICENSE("GPL"); MODULE_AUTHOR("wbyq");
Linux 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。