在Linux上要擷取進程的資訊,需要讀取/proc/進程id/中的檔案,如果只是讀一個進程還好,如果要讀取很多進程,或者說像進程管理器一樣要擷取所有進程的資訊就有點麻煩了。總不能按1-1000一個一個的遍曆,效率太低。此時可以使用Linux中對目錄進行操作的函數opendir(),readdir()。
轉自http://www.liweifan.com/2012/05/13/linux-system-function-files-operation/
首先說說DIR這一結構體,以下為DIR結構體的定義:
struct __dirstream { void *__fd; char *__data; int __entry_data; char *__ptr; int __entry_ptr; size_t __allocation; size_t __size; __libc_lock_define (, __lock) }; typedef struct __dirstream DIR;
DIR結構體類似於FILE,是一個內部結構,以下幾個函數用這個內部結構儲存當前正在被讀取的目錄的有關資訊(摘自《UNIX環境進階編程(第二版)》)。函數 DIR *opendir(const char *pathname),即開啟檔案目錄,返回的就是指向DIR結構體的指標,而該指標由以下幾個函數使用:
struct dirent *readdir(DIR *dp); void rewinddir(DIR *dp); int closedir(DIR *dp); long telldir(DIR *dp); void seekdir(DIR *dp,long loc);
關於DIR結構,我們知道這麼多就可以了,沒必要去再去研究他的結構成員。
接著是dirent結構體,首先我們要弄清楚目錄檔案(directory file)的概念:這種檔案包含了其他檔案的名字以及指向與這些檔案有關的資訊的指標(摘自《UNIX環境進階編程(第二版)》)。從定義能夠看出,dirent不僅僅指向目錄,還指向目錄中的具體檔案,readdir函數同樣也讀取目錄下的檔案,這就是證據。以下為dirent結構體的定義:
struct dirent { long d_ino; /* inode number 索引節點號 */ off_t d_off; /* offset to this dirent 在目錄檔案中的位移 */ unsigned short d_reclen; /* length of this d_name 檔案名稱長 */ unsigned char d_type; /* the type of d_name 檔案類型 */ char d_name [NAME_MAX+1]; /* file name (null-terminated) 檔案名稱,最長255字元 */ }
然後是怎麼使用它讀取進程資訊。可以用這些函數來讀取/proc下的檔案夾,然後做一個判斷,只要檔案夾的名字開頭是1-9的,就進入目錄讀取其中的status檔案,然後輸出資訊。
代碼
#include <stdio.h>#include <dirent.h>#include <unistd.h>#include <stdlib.h>typedef struct{ pid_t pid; char name[256];//進程名稱 int vmsize;//虛擬記憶體資訊}proc_info_st;//儲存讀取的進程資訊#define PROC_NAME_LINE 1//名稱所在行#define PROC_PID_LINE 4//pid所在行#define PROC_VMSIZE_LINE 12//虛擬記憶體所在行#define BUFF_LEN 1024 //行緩衝區的長度#ifndef TRUE# define TRUE 1#endif#ifndef FALSE# define FALSE 0#endifvoid read_proc(proc_info_st* info,const char* c_pid);//讀取進程資訊int read_line(FILE* fp,char* buff,int b_l,int l);//讀取一行int main(){ //開啟目錄 DIR *dir; struct dirent *ptr; if (!(dir = opendir("/proc"))) return 0; //讀取目錄 while (ptr = readdir(dir)) {//迴圈讀取出所有的進程檔案 if (ptr->d_name[0] > '0' && ptr->d_name[0] <= '9') { //擷取進程資訊 proc_info_st info; read_proc(&info,ptr->d_name);//讀取資訊 printf("pid:%d\npname:%s\nvmsize:%d\n",info.pid,info.name,info.vmsize); printf("\n\n");//再空兩行 } }}/************************************************** **說明:根據進程pid擷取進程資訊,存放在proc_info_st結構體中 ** **輸入: ** /proc_info_st* info 返回進程資訊 ** /char* c_pid 進程pid的字串形式 ** ** ** *************************************************/void read_proc(proc_info_st* info,const char* c_pid){ FILE* fp = NULL; char file[512] = {0}; char line_buff[BUFF_LEN] = {0};//讀取行的緩衝區 sprintf(file,"/proc/%s/status",c_pid);//讀取status檔案 if (!(fp = fopen(file,"r"))) { printf("read %s file fail!\n",file); return; } char name[32]; //先讀取進程名稱 if (read_line(fp,line_buff,BUFF_LEN,PROC_NAME_LINE)) { sscanf(line_buff,"%s %s",name,(info->name)); } fseek(fp,0,SEEK_SET);//回到檔案頭部 //讀取進程pid if (read_line(fp,line_buff,BUFF_LEN,PROC_PID_LINE)) { sscanf(line_buff,"%s %d",name,&(info->pid)); } fseek(fp,0,SEEK_SET);//回到檔案頭部 //讀取進程vmsize if (read_line(fp,line_buff,BUFF_LEN,PROC_VMSIZE_LINE)) { sscanf(line_buff,"%s %d",name,&(info->vmsize)); } fclose(fp); }/************************************************** **說明:讀取檔案的一行到buff ** **輸入: ** /FILE* fp 檔案指標 ** /char* buff 緩衝區 ** /int b_l 緩衝區的長度 ** /l 指定行 ** **輸出: ** /true 讀取成功 ** /false 讀取失敗 *************************************************/int read_line(FILE* fp,char* buff,int b_l,int l){ if (!fp) return FALSE; char line_buff[b_l]; int i; //讀取指定行的前l-1行,轉到指定行 for (i = 0; i < l-1; i++) { if (!fgets (line_buff, sizeof(line_buff), fp)) { return FALSE; } } //讀取指定行 if (!fgets (line_buff, sizeof(line_buff), fp)) { return FALSE; } memcpy(buff,line_buff,b_l); return TRUE;}