尝试[Linux] go读取进程信息并处理成结构体和json

156 阅读5分钟

在Linux中proc文件夹记录了系统当前运行的系统状态和进程状态。在每个进程文件中有一个status文件,该文件描述了当前进程的各种信息,如下图所示

Name:   cat                                       进程名称。
Umask:  0022                                      文件创建掩码
State:  R (running)                               进程状态
Tgid:   1578975                                   线程组ID
Ngid:   0                                         Nice组ID
Pid:    1578975                                   进程ID
PPid:   1425046                                   父进程ID
TracerPid:      0                                 跟踪进程ID
Uid:    1001    1001    1001    1001              用户组ID
Gid:    1001    1001    1001    1001              组ID
FDSize: 128                                       文件描述符大小
Groups: 965 1001                                  进程所属组
NStgid: 1578975                                   新线程组ID
NSpid:  1578975                                   新进程组ID
NSpgid: 1578975                                   新线程组ID
NSsid:  1425046                                   新会话ID
Kthread:        0                                 内核线程
VmPeak:     8700 kB                               进程使用的虚拟内存峰值
VmSize:     8700 kB                               进程使用的虚拟内存大小
VmLck:         0 kB                               锁定的虚拟内存大小
VmPin:         0 kB                               钉住的虚拟内存大小
VmHWM:      1764 kB                               进程使用的虚拟内存高水位标记
VmRSS:      1764 kB                               进程使用的驻留集大小
RssAnon:               0 kB                       匿名驻留集大小
RssFile:            1764 kB                       文件驻留集大小
RssShmem:              0 kB                       共享内存驻留集大小
VmData:      340 kB                               数据段大小
VmStk:       136 kB                               堆栈段大小
VmExe:        16 kB                               可执行段大小
VmLib:      1544 kB                               库段大小
VmPTE:        52 kB                               页表条目大小
VmSwap:        0 kB                               交换空间大小
HugetlbPages:          0 kB                       大页面数量
CoreDumping:    0                                 核心转储状态
THP_enabled:    1                                 透明大页面启用状态
untag_mask:     0xffffffffffffffff                进程未标记的内存页面的范围
Threads:        1                                 线程数量
SigQ:   1/63004                                   信号队列
SigPnd: 0000000000000000                          待处理信号数量
ShdPnd: 0000000000000000                          待处理共享内存信号数量
SigBlk: 0000000000000000                          被阻塞的信号数量
SigIgn: 0000000000000000                          被忽略的信号数量
SigCgt: 0000000000000000                          被捕获的信号数量
CapInh: 0000000000000000                          被禁止的能力
CapPrm: 0000000000000000                          被允许的能力
CapEff: 0000000000000000                          有效的能力
CapBnd: 000001ffffffffff                          绑定的能力
CapAmb: 0000000000000000                          未决的能力
NoNewPrivs:     0                                 禁止新特权
Seccomp:        0                                 安全计算模式
Seccomp_filters:        0                         安全计算过滤器
Speculation_Store_Bypass:      thread vulnerable  推测存储绕过
SpeculationIndirectBranch:    conditional enabled 推测间接分支
Cpus_allowed:   ff                                允许使用的 CPU
Cpus_allowed_list:      0-7
Mems_allowed:   00000001
Mems_allowed_list:      0
voluntary_ctxt_switches:        0
nonvoluntary_ctxt_switches:     0

1. 首先定义需要处理的结构体,这里直接定义所有类型为string

type Process struct {
	Name                       string
	Umask                      string
	State                      string
	Tgid                       string
	Ngid                       string
	Pid                        string
	PPid                       string
	TracerPid                  string
	Uid                        string
	Gid                        string
	FDSize                     string
	Groups                     string
	NStgid                     string
	NSpid                      string
	NSpgid                     string
	NSsid                      string
	Kthread                    string
	VmPeak                     string
	VmSize                     string
	VmLck                      string
	VmPin                      string
	VmHWM                      string
	VmRSS                      string
	RssAnon                    string
	RssFile                    string
	RssShmem                   string
	VmData                     string
	VmStk                      string
	VmExe                      string
	VmLib                      string
	VmPTE                      string
	VmSwap                     string
	HugetlbPages               string
	CoreDumping                string
	THP_enabled                string
	untag_mask                 string
	Threads                    string
	SigQ                       string
	SigPnd                     string
	ShdPnd                     string
	SigBlk                     string
	SigIgn                     string
	SigCgt                     string
	CapInh                     string
	CapPrm                     string
	CapEff                     string
	CapBnd                     string
	CapAmb                     string
	NoNewPrivs                 string
	Seccomp                    string
	Seccomp_filters            string
	Speculation_Store_Bypass   string
	SpeculationIndirectBranch  string
	Cpus_allowed               string
	Cpus_allowed_list          string
	Mems_allowed               string
	Mems_allowed_list          string
	voluntary_ctxt_switches    string
	nonvoluntary_ctxt_switches string
}

2. 获取原始文件

首先定义一个常量PROC_PATH用于保存proc文件系统路径

const PROC_PATH = "/proc"

3. 读取文件

使用os.ReadDir读取该文件夹下所有的目录

processList, err := os.ReadDir(PROC_PATH)

4. 正则匹配进程目录

在Linux中,进程存储在/proc/pid文件夹内,每个进程都是一个文件夹,而进程号则是以数字命名

validate := regexp.MustCompile("^[0-9]+$")

for _, info := range processList {
  var process Process
  if info.IsDir() && validate.MatchString(info.Name()) {
    ...
  }
}

5. 将文件byte[]处理为string

首先将文件buffer转为string字符串

file := string(filebuffer)

6. 将字符处理为key[value]数据结构

每个进程状态描述文件的内容都是以key: value的形式组织,但是中间的间隔符有的是\t有的是\s需要统一并分割成key[value]形式,最终返回一个map数字

var regM = regexp.MustCompile(":{1}([\t| ])*")
var regV = regexp.MustCompile("(\t| )+")

func getStatusFileToObj(str string) map[string]string {
    infoMap := make(map[string]string)
    infoLineList := strings.Split(str, "\n")
    for _, line := range infoLineList {
        line1 := regM.ReplaceAllString(line, "___")
        info := strings.Split(line1, "___")
        if len(info) == 2 {
            infoMap[info[0]] = regV.ReplaceAllString(info[1], ",")
        }
    }
    return infoMap
}

7. 使用json.Marshal将字符转换为json,再利用json.Unmarshal将json转换为Process结构

j, _ := json.Marshal(getStatusFileToObj(file))
json.Unmarshal(j, &process)
processObjList = append(processObjList, process)

最终效果如下

{
    "Name": "systemd", 
    "Umask": "0000", 
    "State": "S (sleeping)", 
    "Tgid": "1", 
    "Ngid": "0", 
    "Pid": "1", 
    "PPid": "0", 
    "TracerPid": "0", 
    "Uid": "0 0 0 0", 
    "Gid": "0 0 0 0", 
    "FDSize": "512", 
    "Groups": "", 
    "NStgid": "1", 
    "NSpid": "1", 
    "NSpgid": "1", 
    "NSsid": "1", 
    "Kthread": "0", 
    "VmPeak": "30704 kB", 
    "VmSize": "23104 kB", 
    "VmLck": "0 kB", 
    "VmPin": "0 kB", 
    "VmHWM": "20316 kB", 
    "VmRSS": "8928 kB", 
    "RssAnon": "2532 kB", 
    "RssFile": "6396 kB", 
    "RssShmem": "0 kB", 
    "VmData": "3016 kB", 
    "VmStk": "1036 kB", 
    "VmExe": "40 kB", 
    "VmLib": "11936 kB", 
    "VmPTE": "84 kB", 
    "VmSwap": "1920 kB", 
    "HugetlbPages": "0 kB", 
    "CoreDumping": "0", 
    "THP_enabled": "1", 
    "Threads": "1", 
    "SigQ": "1/63004", 
    "SigPnd": "0000000000000000", 
    "ShdPnd": "0000000000000000", 
    "SigBlk": "7fefc1fe28014a03", 
    "SigIgn": "0000000000001000", 
    "SigCgt": "00000000000004ec", 
    "CapInh": "0000000000000000", 
    "CapPrm": "000001ffffffffff", 
    "CapEff": "000001ffffffffff", 
    "CapBnd": "000001ffffffffff", 
    "CapAmb": "0000000000000000", 
    "NoNewPrivs": "0", 
    "Seccomp": "0", 
    "Seccomp_filters": "0", 
    "Speculation_Store_Bypass": "thread vulnerable", 
    "SpeculationIndirectBranch": "conditional enabled", 
    "Cpus_allowed": "ff", 
    "Cpus_allowed_list": "0-7", 
    "Mems_allowed": "00000001", 
    "Mems_allowed_list": "0"
}