Linux编程系列——系统和进程信息

11 篇文章 0 订阅
10 篇文章 0 订阅

/proc文件系统

  • 获取与进程有关的信息: /proc/PID

在老版UNIX中允许特权级程序深入内核内存中的数据结构以获取内核信息, 如:
1. 系统中有多少进程在运行,其属主是谁?
2. 一个进程打开多少文件?
3. 目前锁定了什么文件,
4. 哪些进程持有这些锁?系统正在使用什么套接字?

为了提供更为简便的方法访问内核信息,许多现代UNIX实现提供了一个/proc虚拟文件系统
该文件系统驻留于/proc目录中,包含了各种用于展示内核信息的文件,并且允许进程通过常规文件I/O系统调用来方便的读取,有时还可以修改这些信息。
之所以称/proc文件系统为虚拟,因其包含的文件和子目录并未存储于磁盘上,而是由内核在进程访问此类信息时动态创建而成

  1. 获取与进程相关的信息: /proc/PID

    对于系统中的每个进程,内核都提供了相应的目录,命令为/proc/PID,其中PID为进程ID。此目录中的各种文件和子目录包含了进程的相关信息。

    1)./Proc/PID/status 结果参数说明

    每个/proc/PID目录中都存在一个命名为satus的文件,提供了进程的以系列信息,

    $ cat /proc/1/status
    Name:	systemd			 # name of command run by this process(进程名)	
    Umask:	0000
    State:	S (sleeping)	 # State of this process (进程状态)
    Tgid:	1				 # Thread group ID(traditional PID, getpid()) 线程组ID	
    Ngid:	0	
    Pid:	1				 # Actually,thread ID(gettid()) 进程ID(实际线程ID)
    PPid:	0				 # Parent process ID (父进程ID)
    TracerPid:	0 			 # PID of tracing process(0 if not traced)(接收跟踪该进程信息的进程的ID号)
    Uid:	0	0	0	0	 # Real, offective, saved set, and FS UIDS
    Gid:	0	0	0	0	 # Real, offective, saved set, and FS GIDS
    FDSize:	128				 # of file descriptor slots currently allocated(当前分配的文件描述符插槽最大个数)
    Groups:	 				 # supplementary group IDs(补充组ID)
    NStgid:	1	
    NSpid:	1
    NSpgid:	1
    NSsid:	1
    VmPeak:	  270708 kB		 # Peak virtual memory size(峰值虚拟内存大小)
    VmSize:	  205172 kB		 # Current virtual memory size(当前虚拟内存大小,其中total_vm为进程的地址空间的大小,reserved_vm:进程在预留或特殊的内存间的物理页 )
    VmLck:	       0 kB		 # Locked memory (锁定存储器, 任务已经锁住的物理内存的大小。锁住的物理内存不能交换到硬盘 (locked_vm) 
    VmPin:	       0 kB		 
    VmHWM:	    7440 kB		 # Peak resident set size (峰值驻留集大小)
    VmRSS:	    7440 kB		 # Current resident set size (当前峰值驻留集大小, 应用程序正在使用的物理内存的大小,就是用ps命令的参数rss的值 (rss) )
    RssAnon:	    2208 kB	 # 
    RssFile:	    5232 kB
    RssShmem:	       0 kB
    VmData:	   18556 kB		 # Data segment size (数据段大小)
    VmStk:	     132 kB		 # Stack size(栈大小)
    VmExe:	     948 kB		 # Text(executable code)size (代码段大小,程序所拥有的可执行虚拟内存的大小,代码段,不包括任务使用的库 (end_code-start_code) )
    VmLib:	    6816 kB		 # Shared library size (共享库大小,被映像到任务的虚拟内存空间的库的大小 (exec_lib)
    VmPTE:	     160 kB		 # Size of page table(since 2.6.10) (页表大小, 该进程的所有页表的大小,单位:kb )
    VmSwap:	       0 kB 	 # 
    HugetlbPages:	       0 kB
    CoreDumping:	0
    Threads:	1		  	 #  of threads in this thread's thread group (此线程的线程组中的线程数)
    SigQ:	0/31144			 # Current/max,queued signals(since 2.6.10) (当前/最大,信号队列(从2.6.12开始))
    SigPnd:	0000000000000000 # Signals pending for thread (线程的挂起信号, 屏蔽位,存储了该线程的待处理信号)
    ShdPnd:	0000000000000000 # Signals pending for process(since 2.6) (等待处理的信号, 屏蔽位,存储了该线程组的待处理信号 )
    SigBlk:	7be3c0fe28014a03 # Blocked signals(阻塞信号, 存放被阻塞的信号)
    SigIgn:	0000000000001000 # Ignored singals(忽略的信号, 存放被忽略的信号 )
    SigCgt:	00000001800004ec # Caught signals (捕捉到的信号, 存放被捕获到的信号)
    CapInh:	0000000000000000 # Inheritable capabilities (可继承能力, 能被当前进程执行的程序的继承的能力 )
    CapPrm:	0000003fffffffff # Permitted capabilities (进程能够使用的能力,可以包含CapEff中没有的能力,这些能力是被进程自己临时放弃的,CapEff是CapPrm的一个子集,进程放弃没有必要的能力有利于提高安全性)
    CapEff:	0000003fffffffff # Effective capabilities (进程的有效能力)
    CapBnd:	0000003fffffffff # Capability boundings set(since 2.6.26) (能力边界集)
    CapAmb:	0000000000000000
    NoNewPrivs:	0
    Seccomp:	0
    Speculation_Store_Bypass:	vulnerable
    Cpus_allowed:	f		 # CPUs allowed, mask	(since 2.6.24)
    Cpus_allowed_list:	0-3  # Same as above, list format(since 2.6.24) (与上面相同, 列表格式)
    
    # Mems_allowed:	 Memory nodes allowed, mask (允许内存节点,掩码) since 2.6.24
    Mems_allowed:	00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001	
    Mems_allowed_list:	0 # Same as above, list format
    voluntary_ctxt_switches:	1383	# Voluntary context switches (自动上下文切换) since 2.6.23
    nonvoluntary_ctxt_switches:	576 	# Involuntary context switches (非自愿上下文切换)since 2.6.23
    Statck usage: 8kb	# Stack usage high-water mark (堆栈使用的量最高水位线) since 2.6.23	
    

    该文件内容随着时间而改变,这一事实揭示出关于/proc 文件使用的要点所在。当这些文
    件由多个条目组成时,对其解析应当谨慎从事,在这种情况下,应查找包含特殊字符串(如,
    PPid)的匹配行记录,而非按照(逻辑)行号来处理文件

    2)./Proc/PID目录中其他内容
    /proc/PID目录下文件节选
    a. /proc/PID/fd 目录:

    /proc/PID/fd 目录为进程打开的每个文件描述符都包含了一个符号链接,每个符号链接的
    名称都与描述符的数值相匹配。例如,/proc/1968/1 是 ID 为 1968 的进程中指向标准输出的符
    号链接,更多信息参见 5.11 节。
    为方便起见,任何进程都可使用符号链接/proc/self 来访问其自己的/proc/PID 目录

    b. 线程:/proc/PID/task 目录:

    Linux 2.4 增加了线程组概念,正式支持 POSIX 线程模型。因为线程组中的一些属性对于
    线程而言是唯一的,所以 Linux 2.4 在/proc/PID 目录下增加了一个 task 子目录。针对进程中的
    每个线程,内核提供了以/proc/PID/task/TID 命名的子目录,其中 TID 是该线程的线程 ID。
    (此
    值等同于在线程中调用 gettid()函数的返回值。)
    每个/proc/PID/task/TID 子目录中都有一套类似于/proc/PID 目录内容的文件和目录。因为线
    程共享了多个属性,所以这些文件中的许多信息对进程中各个线程而言都是相同的。然而,这
    些文件也显示了每个线程的独特信息,故而是合理的。例如,在线程组的/proc/PID/task/TID/status
    文件中,存在那种对每个线程而言,内容都有可能不同的字段,State、Pid、SigPnd、SigBlk、
    CapInh、CapPrm、CapEff 和 CapBnd 就在此列。

  2. /proc目录下的系统信息

    • /proc目录下的各种文件和子目录访问的系统信息
      /proc目录下的各种文件和子目录访问的系统信息
    1. /proc子目录用途节选
       /proc子目录用途节选
  3. 访问/proc文件

使用常规I/O系统调用来访问/proc目录下的文件,但在访问时有如下限制:
1. /proc目录下的一些文件是只读的,即这些文件仅用于显示内核信息,但无法对其修改。/proc/PID目录下的大多数文件就属于此类型

2. /proc目录下的一些文件仅能有文件拥有者(或特权级进程)读取。例如,/proc/PID/目录下的所有文件都属于拥有相应进程的用户,而且即使是对文件的属主,其中的部分文件(如:/proc/PID/environ文件)也仅仅授予读权限

3. 除了/proc/PID子目录中的文件,/proc目录的其他大多属于root用户,并且也仅有root用户能够修改那写可修改的文件

示例程序: 修改/proc/sys/kernel/pid_max

#define MAX_LINE 100

int main(int argc, char **argv)
{
    int fd;
    char line[MAX_LINE];
    ssize_t n;

    fd = open("/proc/sys/kernel/pid_max",
            (argc > 1) ? O_RDWR : O_RDONLY);

    if (fd == -1) {
        perror("open");
        exit(1);
    }

    n = read(fd, line, MAX_LINE);
    if (n == -1) {
        perror("read");
        exit(1);
    }

    if (argc > 1) {
        printf("Old value: ");
        printf("%.*s", (int)n, line);
    }

    if (argc > 1) {
        if (lseek(fd, 0, SEEK_SET) == -1) {
            perror("lseek");
        }
        if ( write(fd, argv[1], strlen(argv[1])) != strlen(argv[1]) ) {
            perror("write()");
        }

        system("echo /proc/sys/kernel/pid_max now_contains "
               " `cat /proc/sys/kernel/pid_max`");
    }

    exit(EXIT_SUCCESS);
    return 0;
}

proc/PID 目录内容变化不定。每个目录随着含有相应进程 ID 的进程创建而生,又随进程的终止而灭。这意味着要确定特定/proc/PID 目录的存在,就需要干净利落地处理如下可能性:当打开此目录下的文件时,进程已经终止,并且也已经删除了相应的/proc/PID 目录

系统标识: uname()

uname()系统调用返回了一系列关于主机系统的标示信息,存储于utsbuf所指向的结构中

  • utsname结构
    utsbuf参数是一个指向utsname结构的指针,其定义如下
#define _UTSNAME_LENGTH 65
struct utsname {
    char sysname[_UTSNAME_LENGTH];  /* Implementation name 实现名称*/
    char nodename[_UTSNAME_LENGTH]; /* Node name on network 网络上的节点名 */
    char release[_UTSNAME_LENGTH];  /* Implementation release 实施发布级别 */
    char version[_UTSNAME_LENGTH];  /* Release version level 版本级别 */
    char machine[_UTSNAME_LENGTH];  /* Hardware on which system
                                        is running  运行系统的硬件 */
#ifdef _GNU_SOURCE                  /* Following is Linux-specific */
    char domainname[_UTSNAME_LENGTH];   /* NIS domain name of host 主机域名 */
#endif
};

/* SUSv3规范了uname(),但对utsname结构中各种字段的长度未加定义,
仅要求字符串以空子节终止。在Linux中,这些字段长度均为65个字节,
其中包括了空字节终止符所占用的空间。而在一些UNIX实现中,这些长度更短,
但在其他操作系统(如Solaris)中,这些字段的长度长达257个字节
*/
/* utsname结构中的sysname、release、version、和machine字段
由内核自动设置 */

在 Linux 中, /proc/sys/kernel 目录下的 3 个文件提供了与 utsname 结构的 sysname、和 version 字段返回值相同的信息,这些只读文件分别为ostypeosreleaseversion。另外一个文件/proc/version,也包含了这些信息,并且还包含了有关内核编译的步骤信息(即执行编译的用户名、用于编译的主机名,以及使用的 gcc 版本)。

  • uname相关系统调用
    1.sethostname()

    设置utsname结构中nodename字段值,(详情请参考此系统调用的手册页)。通常,该值类似于系统 DNS 域名中的前缀主机名。

  1. gethostname()

    gethostname()系统调用(是 sethostname()函数的反向操作)用于获取系统主机名,也可利用 hostname(1)命令和 Linux 特有的/proc/hostname 文件来查看和设置系统主机名。

  2. getdomainame() & setdomainname()

    getdomainname()系统调用(setdomainname()函数的反向操作)用于获取 NIS 域名,也
    可利用 domainname(1)命令和 Linux 特有的/proc/domainname 文件来查看和设置 NIS 域名

sethostname()和 setdomainname()系统调用在应用程序中鲜有使用。通常,会在系统启动
时运行启动脚本来确立主机名和 NIS 域名。

  • 使用uname

#include <sys/utsname.h>
int uname(struct utsname *utsbuf);
                                  Returns 0 on sucess, or -1 on error

int main()
{
    struct utsname uts;

    if (uname(&uts) == -1) {
        perror("uname");
        exit(EXIT_FAILURE);
    }

    printf("Node name: %s\n", uts.nodename);
    printf("System name: %s\n", uts.sysname);
    printf("Release: %s\n", uts.release);
    printf("Version: %s\n", uts.version);
    printf("Machine: %s\n", uts.machine);
#ifdef _GNU_SOURCE
    printf("Domain name: %s\n", uts.domainname);
#endif
    exit(EXIT_SUCCESS);
}
  • 总结

/proc文件系统向应用程序暴露了一系列内核信息。每个/proc/PID子目录都包含有许多文件和子目录,是进程ID为PID的进程提供的相关信息。/proc目录下的其他许多文件和目录,则暴露了应用程序可以读取,有时还可修改的系统级信息。

使用uname系统调用,可以获取UNIX的实现信息以及应用程序所运行的机器类型

进阶: 关于/proc 文件系统的深入信息可见诸于 proc(5)手册页、内核源文件 Documentation/
filesystems/proc.txt 以及 Documentation/sysctl 目录下的各种文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值