第12章 系统和进程信息

在本章中,将介绍访问各种系统和进程信息的方法。本章的主要重点是讨论 /proc 文件系统。还将阐述uname()系统调用,用于获取各种系统标识符。

12.1 The /proc 文件系统

在旧的UNIX实现中,一般没有简单的方法通过查看分析(或者改变)内核的属性来回答以下问题:

  • 系统中有多少个正在运行的进程,这些进程的所属者是谁?
  • 进程打开了哪些文件?
  • 哪些文件当前是锁住的,哪些进程持有这些锁?
  • 系统中使用了哪些socket?

一些旧的UNIX实现通过特权程序分析内核内存的数据结构来解决这些问题。然而这种途径存在各种问题。尤其是,需要对内核数据结构具备专业的知识,而这些结构可能随着内核版本的不同而不同,编写的程序需要根据这些而特意编写。
为了能够更简单地访问内核信息,许多现代UNIX实现提供了 /proc 虚拟文件系统。在后续章节中,我们会介绍特定的 /proc 文件,因为它与这些章节的主题有关。尽管很多UNIX实现提供了 /proc 文件系统,但是SUSv3中并未对此文件系统进行定义。本书中介绍的详情是Linux特有的。

12.1.1 Obtaining Information About a Process: /proc/PID

对于系统中的每个进程,内核提供了一个相应的目录,名为 /proc/PID,PID是进程的ID。此目录中的各种文件和子目录都是跟该进程的信息有关的。例如,可以通过查看目录 /proc/1 中的文件来获取 init 进程(通常进程ID为1)的信息:
在这里插入图片描述
在这里插入图片描述

上面的输出信息是内核2.6.32的,该文件的内容随着内核版本的变化而变化。所以当解析该文件的条目时,需要根据具体的字符串(例如PPid)来解析,而不是根据文件的行号。
Table 12-1列出了/proc/PID目录下的其他文件:

文件说明(进程属性)
cmdline由\0分隔的命令行参数
cwd指向当前工作目录的符号链接
environ由\0分隔的环境列表 Name=value 对
exe指向正在执行的文件的符号链接
fd该目录中存放指向进程打开文件的符号链接
maps内存映射
mem进程虚拟内存(在I/O之前必须lseek()到有效偏移量)
mounts进程的挂载点(mount points)
root指向root目录的符号链接
status各种信息(例如进程ID、凭证、内存使用情况和信号)
task该目录下存放每个线程相关信息的子目录(Linux 2.6)
The /proc/PID/fd directory

/proc/PID/fd 目录包含进程打开的每个文件描述符的符号链接。每个符号链接的名称都是对应描述符的数值(descriptor number)。例如 /proc/2057/fd/1 是一个符号链接,指向进程2057的标准输出。详情参考5.11节
为方便起见,每个进程都可以通过使用 /proc/self 来访问自己的 /proc/PID 目录。
在这里插入图片描述

Threads: the /proc/PID/task directory

Linux 2.4 增加了线程组的概念用于对POSIX线程模型进程一定的支持。因为一个线程组中线程的某些属性是不同的,从Linux2.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这些字段对于每个线程都可能是不同的。
在这里插入图片描述

12.1.2 System Information Under /proc

/proc下的各种文件和子目录提供了系统级(system-wide)的信息访问。Figure 12-1中列出了其中的部分。Table 12-2概括了Figure12-1中/proc 子目录的作用。

在这里插入图片描述

Table 12-2:/proc下子目录的作用

目录该目录下文件展示的信息
/proc各种系统信息
/proc/net网络和sockets相关的状态信息
/proc/sys/fs文件系统相关的设置
/proc/sys/kernel各种通用的内核设置
/proc/sys/net网络和sockets设置
/proc/sys/vm内存管理设置
/proc/sysvipcSystem V IPC对象信息

12.1.3 Accessing /proc Files

通常使用shell脚本来访问/proc下的文件(使用脚本语言如Python或Perl可以轻易地解析大部分/proc文件下的多数值)。例如可以使用以下的shell命令来修改和查看/proc下的文件:
在这里插入图片描述

程序中可以使用文件I/O系统调用来访问/proc文件。当访问这些文件时有以下约束:

  • 有些/proc文件是只读的;也就是说,它们只用于展示内核信息,而不能用于修改信息。/proc?PID目录下的大部分文件都是这样的。
  • 一些/proc文件只能被文件拥有者(或者特权进程)所读取。例如,/proc/PID下的所有文件只能被拥有该进程的用户所访问。其中一些文件的读取权限(例如 /proc/PID/environ)只能被授予文件拥有者。
  • 除了/proc/PID子目录下的文件,大部分/proc下的文件的拥有者是root,并且这些文件只能被root所修改。
Accessing files in /proc/PID

/proc/PID目录是易变的(volatile),该目录随着进程及相应进程ID的创建而创建,随着进程的终止而消失。

Example program

Listing 12-1 展示了如何读取和修改/proc文件。该程序将读取和显示/proc/sys/kernel/pid_max文件的内容。如果提供了命令行参数,程序会使用这个值更新文件。该文件指定了进程ID的上限值(6.2节)。以下是程序使用的例子:
在这里插入图片描述

// Listing 12-1: Accessing /proc/sys/kernel/pid_max
// sysinfo/procfs_pidmax.c
#include <fcntl.h>
#include "tlpi_hdr.h"
#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)
 		errExit("open");
 	 
 	n = read(fd, line, MAX_LINE);
 	if (n == -1)
 		errExit("read");
 	
 	if (argc > 1)
 		printf("Old value: ");
 	printf("%.*s", (int) n, line);

	 if (argc > 1) {
 	 	if (write(fd, argv[1], strlen(argv[1])) != strlen(argv[1]))
 			fatal("write() failed");
 		
 		system("echo /proc/sys/kernel/pid_max now contains "
				 "`cat /proc/sys/kernel/pid_max`");
 		}
	exit(EXIT_SUCCESS);
}

12.2 System Identification: uname()

uname() 系统调用返回由 utsbuf 结构体指向的一系列关于该主机系统的识别信息。

#include <sys/utsname.h>
// 成功时返回0,失败时返回-1
int uname(struct utsname *utsbuf);

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 level */
	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() , 但是并未对ustname结构体中各字段的长度进行规定。只规定字符串使用null字节结尾。在Linux中,每个字段的长度是65字节,包含终止的null字符。在某些UNIX实现中,这些字段更短,而有些UNIX实现可以达到257个字节。
utsname结构体中的sysname、release、version和machine字节会由内核自动设置。

在Linux中,/proc/sys/kernel中有三个只读文件,用于提供与utsname结构体的sysname、release和version字段,这三个文件分别是ostype、osrelease和version。另一个文件==/proc/version== 中包含了这些文件中的信息,并且还包含关于内核编译步骤的相关信息(即执行编译的用户名称、执行编译的主机名称、使用的gcc版本)
在这里插入图片描述

nodename 字段返回的值是通过使用 sethostname() 系统调用设置的。通常,这个名称有点像系统DNS域名的主机名前缀。domainname 字段返回的值是通过使用 setdomainname() 系统调用设置的。这是主机的网络信息服务域名(Network Information Service,NIS)。

gethostname()系统调用与sethostname()相反,用于获取系统主机名(hostname)。系统主机名可以通过hostname 命令和 /proc/sys/kernel/hostname 文件来查看和设置。
getdomainname()系统调用与setdomainname()相反,用于获取NIS域名。NIS域名可以通过使用 domainname 命令和 /proc/sys/kernel/domainname 文件来查看和设置。
在应用程序中很少使用sethostname()和setdomainname()系统调用。通常主机名和NIS域名在启动时(at boot time)由启动脚本(startup scripts)来设置

Listing 12-2中的程序展示了uname()返回的信息。以下是运行该程序时可能看到的输出信息。:

在这里插入图片描述

Listing 12-2: Using uname()

// sysinfo/t_uname.c
#define _GNU_SOURCE
#include <sys/utsname.h>
#include "tlpi_hdr.h"

int main(int argc, char *argv[])
{
	struct utsname uts;
	if (uname(&uts) == -1)
		errExit("uname");
	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);

12.3 Summary

/proc文件系统存储了应用程序的一系列内核信息。每个/proc/PID子目录中的文件和子目录包含了进程ID为PID的进程的相关信息。/proc目录下的其他文件和目录存储了系统级的信息,这些信息可以读取,在某些情况下可以修改。
可以通过uname()系统调用了解UNIX实现和应用运行的机子相关信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值