第十二章

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。

results matching ""

    No results matching ""