第十二章
12-1 编写一个程序,以用户名作为命令行参数,列表显示该用户下所有正在运行的进程ID和命令名。通过分析系统中/proc/PID/status文件的name和uid行,可以实现此功能。遍历系统所有的/proc/PID目录需要使用readdir(3)函数,相关信息在18.8节。进程需要处理如下可能性:在确定目录存在与程序尝试打开相应的/proc/PID/status文件之间,/proc/PID目录消失了。
在确定文件目录之后,打开status文件之前如果status文件消失,则无法打开这个文件。所以我们在这里判断一下,如果打开文件失败,则返回,不进行处理。
下面是一个简单的实现。里面用到了c++ 11的特性regex正则匹配。
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <string.h>
#include <regex>
#include <iostream>
#include <fstream>
using namespace std;
vector<string> getAllFd(){
vector<string> ret;
DIR * dir;
struct dirent * ptr;
int i;
dir = opendir("/proc/");
regex e ("[0-9]+");
while((ptr = readdir(dir)) != NULL)
{
string name(ptr->d_name);
if(regex_match(name, e)){
ret.push_back("/proc/" + name + "/status");
}
}
closedir(dir);
return ret;
}
void get_name_UID(string path){
ifstream in;
in.open(path);
char buff[100];
if(!in.is_open()){
cout << "Error opening file" << endl;
return;
}
regex a ("^(Name:\\s+).*");
regex b ("^(Uid:\\s+).*");
while(!in.eof()){
string line;
getline(in, line);
smatch m;
if(regex_search(line, m, a)){
cout << m.str() << endl;
}
else if(regex_search(line, m, b)){
cout << m.str() << endl;
break;
}
}
in.close();
}
int main()
{
vector<string> statusList = getAllFd();
for(auto s:statusList){
get_name_UID(s);
}
return 0;
}
12-2 编写一个程序绘制树状结构,展示系统中所有进程的父子关系,根节点为init进程。对每个进程而言,程序应该显示进程ID和所执行的行命令。程序输出类似pstree(1),但是也无需那样复杂。每个进程的父进程可通过对/proc/PID/status进行扫描,分析PPid:行获得信息。
在命令行运行pstree命令,会有如下输出
systemd─┬─ModemManager───2*[{ModemManager}]
├─NetworkManager─┬─dhclient
│ └─2*[{NetworkManager}]
├─accounts-daemon───2*[{accounts-daemon}]
├─avahi-daemon───avahi-daemon
├─bamfdaemon-dbus───bamfdaemon───2*[{bamfdaemon}]
├─bluetoothd
├─cron
├─cupsd
├─3*[dbus-daemon]
├─dbus-launch
├─dconf-service───2*[{dconf-service}]
├─dde-control-cen───7*[{dde-control-cen}]
├─dde-file-manage───{dde-file-manage}
├─dde-launcher───6*[{dde-launcher}]
├─dde-lockservice───3*[{dde-lockservice}]
├─dde-system-daem───11*[{dde-system-daem}]
├─deepin-notifica───4*[{deepin-notifica}]
├─deepin-terminal─┬─bash───pstree
│ └─2*[{deepin-terminal}]
├─dockerd─┬─docker-containe───9*[{docker-containe}]
│ └─10*[{dockerd}]
├─fcitx─┬─3*[sh]
│ └─{fcitx}
├─fcitx-dbus-watc
├─gconfd-2
├─gnome-keyring-d───3*[{gnome-keyring-d}]
├─gvfs-afc-volume───3*[{gvfs-afc-volume}]
├─gvfs-goa-volume───2*[{gvfs-goa-volume}]
├─gvfs-gphoto2-vo───2*[{gvfs-gphoto2-vo}]
├─gvfs-mtp-volume───2*[{gvfs-mtp-volume}]
├─gvfs-udisks2-vo───2*[{gvfs-udisks2-vo}]
├─gvfsd───2*[{gvfsd}]
├─gvfsd-fuse───5*[{gvfsd-fuse}]
├─gvfsd-metadata───2*[{gvfsd-metadata}]
├─irqbalance
├─langselector───5*[{langselector}]
├─lastore-daemon───10*[{lastore-daemon}]
├─lightdm─┬─Xorg───{Xorg}
│ ├─lightdm─┬─startdde─┬─applet.py───{applet.py}
│ │ │ ├─dde-desktop───7*[{dde-desktop}]
│ │ │ ├─dde-dock───7*[{dde-dock}]
│ │ │ ├─2*[dde-file-manage───6*[{dde-file-manage}]]
│ │ │ ├─dde-osd───4*[{dde-osd}]
│ │ │ ├─dde-polkit-agen───4*[{dde-polkit-agen}]
│ │ │ ├─dde-session-dae───28*[{dde-session-dae}]
│ │ │ ├─dde-session-ini─┬─chrome─┬─2*[cat]
│ │ │ │ │ ├─chrome─┬─chrome
│ │ │ │ │ │ └─3*[{chrome}]
│ │ │ │ │ ├─chrome-sandbox───chrome─┬─c+
│ │ │ │ │ │ └─c+
│ │ │ │ │ └─45*[{chrome}]
│ │ │ │ └─18*[{dde-session-ini}]
│ │ │ ├─deepin-cloud-pr───4*[{deepin-cloud-pr}]
│ │ │ ├─deepin-menu───4*[{deepin-menu}]
│ │ │ ├─deepin-wm-switc─┬─deepin-wm───3*[{deepin-wm}]
│ │ │ │ └─2*[{deepin-wm-switc}]
│ │ │ ├─fcitx
│ │ │ ├─lastore-session───4*[{lastore-session}]
│ │ │ ├─ssh-agent
│ │ │ ├─synergy───6*[{synergy}]
│ │ │ └─28*[{startdde}]
│ │ └─2*[{lightdm}]
│ └─2*[{lightdm}]
├─lvmetad
├─mount.ntfs
├─mousearea───5*[{mousearea}]
├─nmbd
├─packagekitd───2*[{packagekitd}]
├─polkitd───2*[{polkitd}]
├─pulseaudio───2*[{pulseaudio}]
├─python
├─smbd─┬─cleanupd
│ ├─lpqd
│ └─smbd-notifyd
├─sogou-qimpanel───10*[{sogou-qimpanel}]
├─sogou-qimpanel-
├─sshd
├─starter───charon───16*[{charon}]
├─systemd───(sd-pam)
├─systemd-journal
├─systemd-logind
├─systemd-timesyn───{systemd-timesyn}
├─systemd-udevd
├─udisksd───4*[{udisksd}]
├─upowerd───2*[{upowerd}]
└─wpa_supplicant
这个题目稍微涉及到了一点点数据结构的知识。为了将树全部打印出来,我们使用map容器和set容器做一个树形的结构。通过这个容器,存储每一个pid下的children。然后,仍然使用map容器,做一个pid到name的映射。通过这样的方式,我们实现了一个树形结构的打印。下面是具体的实现方法:
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <string.h>
#include <regex>
#include <iostream>
#include <fstream>
#include <map>
#include <set>
using namespace std;
static map<string, set<string>> pids;
static map<string, string> pidname;
vector<string> getAllFd()
{
vector<string> ret;
DIR *dir;
struct dirent *ptr;
int i;
dir = opendir("/proc/");
regex e("[0-9]+");
while ((ptr = readdir(dir)) != NULL)
{
string name(ptr->d_name);
if (regex_match(name, e))
{
ret.push_back("/proc/" + name + "/status");
}
}
closedir(dir);
return ret;
}
void get_name_UID(string path)
{
ifstream in;
in.open(path);
char buff[100];
if (!in.is_open())
{
cout << "Error opening file" << endl;
return;
}
regex re_name("^(Name:\\s+).*");
regex re_ppid("^(PPid:\\s+).*");
regex re_pid("^(Pid:\\s+).*");
regex re_num("[0-9]+");
string name;
string ppid;
string pid;
while (!in.eof())
{
string line;
getline(in, line);
smatch m;
if (regex_search(line, m, re_name))
{
name = m.str().substr(6);
cout << m.str() << endl;
}
else if (regex_search(line, m, re_pid))
{
regex_search(line, m, re_num);
pid = m.str();
cout << "pid: " << m.str() << endl;
}
else if (regex_search(line, m, re_ppid))
{
regex_search(line, m, re_num);
ppid = m.str();
cout << "ppid: " << m.str() << endl;
pidname[pid] = name;
pids[ppid].insert(pid);
break;
}
}
in.close();
}
void printSubTree(string element, int count)
{
if (pids.count(element) != 0)
{
int first = 0;
for (auto child : pids[element])
{
if (first > 0)
{
cout << endl;
for (int i = 0; i < count; i++)
{
if (i != count - 1)
cout << " ";
else
cout << "|";
}
}
else if (count > 0)
cout << "---|";
cout << pidname[child];
printSubTree(child, count + 4 + pidname[child].size());
first++;
}
}
}
int main()
{
vector<string> statusList = getAllFd();
for (auto s : statusList)
{
get_name_UID(s);
}
printSubTree("0", 0);
return 0;
}
编译并运行上面的程序,获得的结果如下:
systemd---|sogou-qimpanel-
|upowerd
|dconf-service
|sogou-qimpanel
|dde-control-cen
|bamfdaemon-dbus---|bamfdaemon
|gvfs-udisks2-vo
|udisksd
|gvfs-mtp-volume
|gvfs-gphoto2-vo
|gvfs-goa-volume
|gvfs-afc-volume
|mount.ntfs
|gvfsd-metadata
|dde-file-manage
|dde-launcher
|deepin-terminal---|bash
|python
|mousearea
|packagekitd
|systemd-journal
|systemd-udevd
|lvmetad
|systemd-timesyn
|systemd-logind
|cupsd
|bluetoothd
|dbus-daemon
|irqbalance
|cron
|NetworkManager---|dhclient
|accounts-daemon
|ModemManager
|avahi-daemon---|avahi-daemon
|polkitd
|lastore-daemon
|sshd
|lightdm---|Xorg
|lightdm---|startdde---|deepin-wm-switc---|deepin-wm
|dde-session-ini---|chrome---|cat
|cat
|chrome-sandbox---|chrome---|chrome-sandbox---|nacl_helper
|chrome---|chrome
|chrome
|chrome
|chrome
|chrome
|chrome
|chrome
|chrome
|chrome---|chrome
|dde-desktop
|dde-dock
|dde-session-dae
|dde-file-manage
|synergy
|deepin-menu
|deepin-cloud-pr
|dde-file-manage
|lastore-session
|dde-polkit-agen
|dde-osd
|applet.py
|code---|code---|code
|code---|code---|bash---|a.out
|code---|code
|Microsoft.VSCod---|Microsoft.VSCod
|Microsoft.VSCod
|code
|code
|code
|code
|ssh-agent
|fcitx
|wpa_supplicant
|starter---|charon
|dockerd---|docker-containe
|nmbd
|smbd---|smbd-notifyd
|cleanupd
|lpqd
|dde-lockservice
|dde-system-daem
|systemd---|(sd-pam)
|gnome-keyring-d
|dbus-launch
|dbus-daemon
|fcitx---|sh
|sh
|sh
|dbus-daemon
|fcitx-dbus-watc
|deepin-notifica
|pulseaudio
|gvfsd
|gvfsd-fuse
|gconfd-2
kthreadd---|migration/0
|acpi_thermal_pm
|ata_sff
|krfcommd
|lru-add-drain
|watchdog/0
|i915/signal:0
|cpuhp/0
|i915/signal:1
|i915/signal:2
|i915/signal:4
|scsi_eh_0
|scsi_tmf_0
|scsi_eh_1
|scsi_tmf_1
|scsi_eh_2
|scsi_tmf_2
|cpuhp/1
|scsi_eh_3
|scsi_tmf_3
|scsi_eh_4
|scsi_tmf_4
|scsi_eh_5
|scsi_tmf_5
|watchdog/1
|bioset
|bioset
|migration/1
|kworker/0:1H
|md
|ksoftirqd/1
|raid5wq
|kworker/1:0H
|cpuhp/2
|bioset
|watchdog/2
|migration/2
|ksoftirqd/2
|jbd2/sda2-8
|ext4-rsv-conver
|kworker/2:0H
|cpuhp/3
|watchdog/3
|kworker/2:1H
|kworker/3:1H
|kworker/1:1H
|migration/3
|kauditd
|ksoftirqd/3
|ksoftirqd/0
|kworker/3:0H
|kdevtmpfs
|netns
|khungtaskd
|oom_reaper
|writeback
|kcompactd0
|ksmd
|irq/124-mei_me
|khugepaged
|crypto
|kintegrityd
|bioset
|kblockd
|kworker/u9:0
|hci0
|hci0
|kworker/u9:1
|kworker/u9:2
|cfg80211
|kworker/1:3
|devfreq_wq
|watchdogd
|kworker/0:0H
|kswapd0
|vmstat
|kthrotld
|ipv6_addrconf
|rcu_preempt
|kworker/0:0
|kworker/2:1
|kworker/u8:1
|kworker/3:0
|kworker/u8:0
|kworker/3:2
|kworker/2:2
|kworker/1:1
|kworker/0:1
|rcu_sched
|kworker/3:1
|kworker/2:0
|kworker/u8:2
|kworker/1:0
|kworker/0:2
|rcu_bh
12-3 编写一个程序,列表展示打开同一个特定文件的所有进程。通过分析/proc/PID/fd/*目录下的符号链接来判断是否打开了该文件。符号链接使用readlink函数来进行分析。
思路和上面的代码类似,只是需要使用readdir函数来遍历目录下的文件,用readlink来分析符号链接的对象。多了一些API的调用而已。
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <string.h>
#include <regex>
#include <iostream>
#include <fstream>
using namespace std;
vector<string> getAllFd(){
vector<string> ret;
DIR * dir;
struct dirent * ptr;
int i;
dir = opendir("/proc/");
regex e ("[0-9]+");
while((ptr = readdir(dir)) != NULL)
{
string name(ptr->d_name);
if(regex_match(name, e)){
ret.push_back("/proc/" + name +"/");
}
}
closedir(dir);
return ret;
}
string get_readlink(string path){
char realpath[1024] = {0};
int size = readlink(path.c_str(), realpath, 1024);
if(size > 0){
realpath[size] = '\0';
}
string ret(realpath);
return ret;
}
int main(int argc, char * argv[])
{
vector<string> statusList = getAllFd();
if(argc !=2 ) return -1;
string target(argv[1]);
cout << target << endl;
for(auto s:statusList){
DIR *dir;
struct dirent * ptr;
dir = opendir((s+"fd/").c_str());
while((ptr = readdir(dir)) != NULL)
{
string name(ptr->d_name);
string readlink = get_readlink(s+"fd/"+name);
if(readlink == target or (target == "test" and readlink != "")){
ifstream in(s+"status");
string pidname;
getline(in, pidname);
cout << pidname << "\tfd:" << s+"fd/"+name+" -> " + readlink << endl;
in.close();
}
}
}
return 0;
}
运行下面代码进行编译、执行:
g++ 12-3.cpp && sudo ./a.out /opt/google/chrome/resources.pak
需要root权限才能读取proc下面的某些文件夹。在我的电脑上出现了如下的输出:
/opt/google/chrome/resources.pak
Name: chrome fd:/proc/8964/fd/41 -> /opt/google/chrome/resources.pak
Name: chrome fd:/proc/8973/fd/13 -> /opt/google/chrome/resources.pak
Name: chrome fd:/proc/8978/fd/13 -> /opt/google/chrome/resources.pak
Name: chrome fd:/proc/9119/fd/13 -> /opt/google/chrome/resources.pak
Name: chrome fd:/proc/9196/fd/13 -> /opt/google/chrome/resources.pak
Name: chrome fd:/proc/9224/fd/13 -> /opt/google/chrome/resources.pak
Name: chrome fd:/proc/9318/fd/13 -> /opt/google/chrome/resources.pak
这说明有7个进程使用了/opt/google/chrome/resources.pak文件,他们的名字都是chrome。