特殊文件--proc文件系统

来源:互联网 发布:qq游戏大厅hd网络异常 编辑:程序博客网 时间:2024/04/27 20:40

1.proc文件系统

proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。用户和应用程序可以通过 proc得到系统的信息,并可以改变内核的某些参数。由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是 动态从系统内核读出所需信息并提交的。

作为一种特殊的文件,程序可以使用open,read,write等函数对其进行操纵,已取得相应的信息。

 

2.常用的内核信息

/proc目录下有两种子目录,其中一部分是完全以数字为名的子目录,这个数字就是相关进程的进程ID。这些目录中包含着记录运行中进程的信息。

剩下的不以数字为名的子目录是内核的数据信息。具体含义如下:

 

目录名称 目录内容

apm 高级电源管理信息

cmdline 内核命令行

Cpuinfo 关于Cpu信息

Devices 可以用到的设备(块设备/字符设备)

Dma 使用的DMA通道

Filesystems 支持的文件系统

Interrupts 中断的使用

Ioports I/O端口的使用

Kcore 内核核心印象

Kmsg 内核消息

Ksyms 内核符号表

Loadavg 负载均衡

Locks 内核锁

Meminfo 内存信息

Misc 杂项

Modules 加载模块列表

Mounts 加载的文件系统

Partitions 系统识别的分区表

Rtc 实时时钟

Slabinfo Slab池信息

Stat 全面统计状态表

Swaps 对换空间的利用情况

Version 内核版本

Uptime 系统正常运行时间

 

 

而实际上 /proc 文件系统通过 /proc 中可读写的文件提供了对内核的交互机制。写这些文件可以改变内核 的状态,因而要慎重改动这些文件。/proc/sys 目录存放所有可读写的文件 的目录,可以被用于改变内核行为。

/proc/sys/kernel - 这个目录包含反通用内核行为的信息。 /proc/sys/kernel/{domainname, hostname} 存放着机器/网络的域名和主机名。 这些文件可以用于修改这些名字。

$ hostnamemachinename.domainname.com$ cat /proc/sys/kernel/domainnamedomainname.com$ cat /proc/sys/kernel/hostnamemachinename$ echo "new-machinename"  > /proc/sys/kernel/hostname$ hostnamenew-machinename.domainname.com

这样,通过修改 /proc 文件系统中的文件,我们可以修改主机名。很多其 他可配置的文件存在于 /proc/sys/kernel/。这里不可能列出所有这些文件, 读者可以自己去这个目录查看以得到更多细节信息。
另一个可配置的目录是 /proc/sys/net 。这个目录中的文件可以 用于修改机器/网络的网络属性。比如,简单修改一个文件,你可以在网络 上瘾藏匿的计算机。

$ echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

这将在网络上瘾藏你的机器,因为它不响应 icmp_echo。主机将不会响应其 他主机发出的 ping 查询。

$ ping machinename.domainname.comno answer from machinename.domainname.com

要改回缺省设置,只要

$ echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all

/proc/sys 下还有许多其它可以用于改变内核属性。

 

 

3.读取内核信息

 

//getinfo.c

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/time.h>

#include <fcntl.h>

#include <unistd.h>

 

#define MAX  1024

#define STANDARD  0

#define SHORT 1

#define LONG 2

 

int get_load_avg()

{

char line_buf[MAX];

int fd;

int n;

int res=-1;

 

if((fd=open("/proc/loadavg",O_RDONLY))==-1){//打开存储系统平均负载的proc文件

perror("fail to loadavg");

exit(-1);

}

 

if((n=read(fd,line_buf,MAX))==-1){//读取系统的平均负载的内容

perror("fail to read");

goto err;

}

 

line_buf[n]='/0';

 

printf("average load : %s/n",line_buf);

 

res=0;

 

err:

close(fd);

 

return res;

 

 

}

 

/*在指定的proc文件中查找所需要的内核信息,并将找到的字符串后面的信息输出

* pah:指定的proc文件的路径

* name:所要查找的内核信息

*/

int search(char *  path,char * name)

{

int fd;

char char_all[MAX]="/0";

char line_buf[MAX]="/0";

char *p;

char * s;

int n;

int res=-1;

 

if((fd=open(path,O_RDONLY))==-1){//打开指定的proc文件

perror("fail to open");

exit(1);

}

 

if((n=read(fd,char_all,MAX))==-1){//将文件的内容读入缓冲区

perror("fail to read");

goto err;

}

 

char_all[n]='/0';

 

p=strstr(char_all,name);

s=strstr(p,"/n");//这一行就是所需要的内核信息

n=s-p+1;

 

strncpy(line_buf,p,n);

printf("%s/n",line_buf);

 

res=0;

 

err:

close(fd);

 

return res;

}

 

int main(int argc,char * argv[])

{

char c1,c2;

int interval;

int duration;

int intervation=0;

int reportType=0;

char repTypeName[16];

struct timeval now;

 

reportType=STANDARD;//默认standard

strcpy(repTypeName,"Standard");

 

if(argc>1){

sscanf(argv[1],"%c%c",&c1,&c2);

 

if(c1 != '-'){

printf(wrong command/n");

exit(1);

}

 

if(c2=='s'){

reportType=SHORT;

strcpy(repTypeName,"Short");

}else if(c2=='l'){

reportType=LONG;

strcpy(repTypeName,"Long");

 

interval=atoi(argv[2]);

duration=atoi(argv[3]);

}

 

}

 

gettimeofday(&now,NULL);

 

printf("status report:    %s/nat the time of : %s/n",repTypeName,ctime(&(now.tv_sec)));

 

/*输出主机名,该信息在/proc/sys/kernel/hostname文件中*/

printf("the hostname is :");

search("/proc/sys/kernel/hostname","");

 

switch(reportType){

case 0:

search("/proc/cpuinfo","model name");//cpu的类型型号

search("/proc/version","");//Linux内核版本

break;

case 1:

search("/proc/stat","cpu");//总共有多少cpu时间执行在用户态,系统态和空闲态

search("/proc/stat","intr");//磁盘读写请求数

search("/proc/stat","ctxt");//内核进行了多少次进程上下文切换

search("/proc/stat","btime");//系统最后的启动时间

search("/proc/stat","processes");//从启动以来创建的进程数

break;

case 2:

search("/proc/meminfo","MemTotal");//配置内存的大小

search("/proc/meminfo","MemFree");//当前可用内存数

while(intervation<duration){//取得平均系统负荷

sleep(interval);

if(get_load_avg()==-1)

exit(1);

 

intervation+=interval;

}

break;

default:

printf("should not bu here/n");

}

return 0;

}

 

运行

./getinfo

./getinfo -s (short 版本)

./getinfo -l interval duration (long版本)

 

 

4.进程状态信息

在/proc目录中,包含了一些以数字为名的目录,这些目录就是当前系统运行进程的proc抽象。在相应的目录下,有一些文件,显示进程相关的信息。/proc/self表示当前进程的proc抽象。如打印当前进程的命令行参数:

cat /proc/self/cmdline

 

每一个目录下有如下内容:

Cmdline 命令行参数

Environ 环境变量值

Fd 一个包含所有文件描述符的目录

Mem 进程的内存被利用情况

Stat 进程状态

Status 进程当前状态,以可读的方式显示出来

Cwd 当前工作目录的链接

Exe 指向该进程的执行命令文件

Maps 内存映象

Statm 进程内存状态信息

Root 链接此进程的root目录

 

 

5.读取进程状态

由于proc文件系统下有一个self的子目录,该目录中存储着当前在执行进程的状态信息。这些状态信息存储在内核的数据结构中。proc文件系统提供了一个和这些信息交互的接口。访问该目录显得/proc/self/task/"进程号"/status文件就可以得到这些信息。

 

下例输出当前执行的进程的状态。

 

//cur_status.c

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <string.h> 
 
#define MAX 1024 
#define PID 6 
#define PATH_SIZE 128 
 
int main(void) 

    FILE *fp; 
    pid_t pid; 
    char pid_str[PID]; 
    char path[PATH_SIZE]; 
    char buf[MAX]; 
 
    pid = getpid(); /* 得到进程ID */ 
    sprintf(pid_str, "%d", pid); /* 将进程ID由数字转换为字符串 */ 
    strcpy(path, "/proc/self/task/"); /* 拼接路径,打开"/proc/self/task/进程ID"目录下的status文件 */ 
    strcat(path, pid_str); 
    strcat(path, "/status"); 
 
    fp = fopen(path, "r"); /* 打开该文件,以只读的方式打开 */ 
    if(fp == NULL){ 
        perror("fail to open"); 
        exit(1); 
    } 
 
    while(fgets(buf, MAX, fp) != NULL) /* 顺序读取每一行,并且打印 */ 
        printf("%s", buf); 
 
    fclose(fp); /* 关闭文件 */ 
 
    return 0; 
}

 

执行:

alei@alei-desktop:~/linux/code/20$ gcc cur_stat.c -o cur_stat
alei@alei-desktop:~/linux/code/20$ ./cur_stat
Name:    cur_stat
State:    R (running)
Tgid:    4443
Pid:    4443
PPid:    4413
TracerPid:    0
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
FDSize:    256
Groups:    4 20 24 46 106 121 122 1000 
VmPeak:        1788 kB
VmSize:        1788 kB
VmLck:           0 kB
VmHWM:         436 kB
VmRSS:         436 kB
VmData:         164 kB
VmStk:          84 kB
VmExe:           4 kB
VmLib:        1504 kB
VmPTE:          20 kB
Threads:    1
SigQ:    0/16382
SigPnd:    0000000000000000
ShdPnd:    0000000000000000
SigBlk:    0000000000000000
SigIgn:    0000000000000000
SigCgt:    0000000000000000
CapInh:    0000000000000000
CapPrm:    0000000000000000
CapEff:    0000000000000000
CapBnd:    ffffffffffffffff
Cpus_allowed:    00000000,00000003
Cpus_allowed_list:    0-1
Mems_allowed:    1
Mems_allowed_list:    0
voluntary_ctxt_switches:    1
nonvoluntary_ctxt_switches:    2

 

 

6.实现自己的ps命令

 

//my_ps.c

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h>   
#include <sys/stat.h>   
#include <unistd.h>   
#include <dirent.h>   
#include <fcntl.h> 
 
#define MAX 1024 
#define PATH_SIZE 128 
 
int main(void) 
{   
    DIR *dir; 
        struct dirent    *entry;  
        FILE *fp; 
    char path[PATH_SIZE]; 
    char buf[MAX]; 
 
    printf("NAME/t/tPID/n"); /* 输出表头 */ 
 
        if((dir = opendir( "/proc" ) ) == NULL ) { /* 打开/proc目录 */ 
            perror("fail to open dir"); 
            return -1;  
        }   
 
    while((entry = readdir( dir ) ) != NULL){  
                 
            if(entry->d_name[0] == '.') /* 跳过当前目录,proc目录没有父目录 */  
            continue; 
        /* 跳过系统信息目录,所有进程的目录全都是数字,而系统信息目录全都不是数字 */ 
        if( '0' > entry->d_name[0] || entry->d_name[0] >'9') 
            continue; 
             
        /* 使用sprintf完成并接路径,其中两个%s会由entry->d_name表示的进程ID替代 */ 
        sprintf(path, "/proc/%s/task/%s/status", entry->d_name, entry->d_name);
           
         
        fp = fopen(path, "r"); /* 打开文件 */ 
        if(fp == NULL){ 
            perror("fail to open"); 
            exit(1); 
        } 
 
        while(fgets(buf, MAX, fp) != NULL){ /* 读取每一行 */ 
            if(buf[0] == 'N' 
             && buf[1] == 'a' 
            && buf[2] == 'm' 
            && buf[3] == 'e') 
                printf("%s/t/t", &buf[5]); /* 跳过'/t',输出状态信息 */ 
             
            if(buf[0] == 'P' 
             && buf[1] == 'i' 
            && buf[2] == 'd'){ 
                printf("%s/n", &buf[4]); /* 输出PID后就结束循环 */ 
                break; 
            } 
        } 
 
        fclose(fp); /* 关闭stattus文件 */ 
    } 
         
    closedir( dir ); /* 关闭目录 */ 
     
    return 0;   
}

0 0
原创粉丝点击