16.4 特殊文件与程序

我们在第六章曾经谈到特殊权限的 SUID/SGID/SBIT ,虽然第六章已经将这三种特殊权限作了详细的解释,不过,我们依旧要来探讨的是,那么到底这些权限对于你的“程序”是如何影响的? 此外,程序可能会使用到系统资源,举例来说,磁盘就是其中一项资源。哪天你在 umount 磁盘时,系统老是出现“ device is busy ”的字样~到底是怎么回事啊?我们下面就来谈一谈这些和程序有关系的细节部分:

16.4.1 具有 SUID/SGID 权限的指令执行状态

SUID 的权限其实与程序的相关性非常的大!为什么呢?先来看看 SUID 的程序是如何被一般使用者执行,且具有什么特色呢?

  • SUID 权限仅对二进制程序(binary program)有效;
  • 执行者对于该程序需要具有 x 的可执行权限;
  • 本权限仅在执行该程序的过程中有效 (run-time);
  • 执行者将具有该程序拥有者 (owner) 的权限。

所以说,整个 SUID 的权限会生效是由于“具有该权限的程序被触发”,而我们知道一个程序被触发会变成程序, 所以啰,执行者可以具有程序拥有者的权限就是在该程序变成程序的那个时候啦!第六章我们还没谈到程序的概念, 所以你或许那时候会觉得很奇怪,为啥执行了 passwd 后你就具有 root 的权限呢?不都是一般使用者执行的吗? 这是因为你在触发 passwd 后,会取得一个新的程序与 PID,该 PID 产生时通过 SUID 来给予该 PID 特殊的权限设置啦! 我们使用 dmtsai 登陆系统且执行 passwd 后,通过工作控制来理解一下!

[dmtsai@study ~]$ passwd
Changing password for user dmtsai.
Changing password for dmtsai
(current) UNIX password: <==这里按下 [ctrl]-z 并且按下 [enter]
[1]+  Stopped                 passwd

[dmtsai@study ~]$ pstree -uA
systemd-+-ModemManager---2*[{ModemManager}]
....(中间省略)....
        |-sshd---sshd---sshd(dmtsai)---bash-+-passwd(root)
        |                                   `-pstree
....(下面省略)....

从上表的结果我们可以发现,底线的部分是属于 dmtsai 这个一般帐号的权限,特殊字体的则是 root 的权限! 但你看到了, passwd 确实是由 bash 衍生出来的!不过就是权限不一样!通过这样的解析, 你也会比较清楚为何不同程序所产生的权限不同了吧!这是由于“SUID 程序运行过程中产生的程序”的关系啦!

那么既然 SUID/SGID 的权限是比较可怕的,您该如何查询整个系统的 SUID/SGID 的文件呢? 应该是还不会忘记吧?使用 find 即可啊!

find / -perm /6000

16.4.2 /proc/* 代表的意义

其实,我们之前提到的所谓的程序都是在内存当中嘛!而内存当中的数据又都是写入到 /proc/* 这个目录下的,所以啰,我们当然可以直接观察 /proc 这个目录当中的文件啊! 如果你观察过 /proc 这个目录的话,应该会发现他有点像这样:

[root@study ~]# ll /proc
dr-xr-xr-x.  8 root           root               0 Aug  4 18:46 1
dr-xr-xr-x.  8 root           root               0 Aug  4 18:46 10
dr-xr-xr-x.  8 root           root               0 Aug  4 18:47 10548
....(中间省略)....
-r--r--r--.  1 root           root               0 Aug  5 17:48 uptime
-r--r--r--.  1 root           root               0 Aug  5 17:48 version
-r--------.  1 root           root               0 Aug  5 17:48 vmallocinfo
-r--r--r--.  1 root           root               0 Aug  5 17:48 vmstat
-r--r--r--.  1 root           root               0 Aug  5 17:48 zoneinfo

基本上,目前主机上面的各个程序的 PID 都是以目录的型态存在于 /proc 当中。 举例来说,我们开机所执行的第一支程序 systemd 他的 PID 是 1 , 这个 PID 的所有相关信息都写入在 /proc/1/* 当中!若我们直接观察 PID 为 1 的数据好了,他有点像这样:

[root@study ~]# ll /proc/1
dr-xr-xr-x. 2 root root 0 Aug  4 19:25 attr
-rw-r--r--. 1 root root 0 Aug  4 19:25 autogroup
-r--------. 1 root root 0 Aug  4 19:25 auxv
-r--r--r--. 1 root root 0 Aug  4 18:46 cgroup
--w-------. 1 root root 0 Aug  4 19:25 clear_refs
-r--r--r--. 1 root root 0 Aug  4 18:46 cmdline  <==就是指令串
-r--------. 1 root root 0 Aug  4 18:46 environ  <==一些环境变量
lrwxrwxrwx. 1 root root 0 Aug  4 18:46 exe
....(以下省略)....

里面的数据还挺多的,不过,比较有趣的其实是两个文件,分别是:

  • cmdline:这个程序被启动的指令串;
  • environ:这个程序的环境变量内容。

很有趣吧!如果你查阅一下 cmdline 的话,就会发现:

[root@study ~]# cat /proc/1/cmdline
/usr/lib/systemd/systemd--switched-root--system--deserialize24

就是这个指令、选项与参数启动 systemd 的啦!这还是跟某个特定的 PID 有关的内容呢,如果是针对整个 Linux 系统相关的参数呢?那就是在 /proc 目录下面的文件啦!相关的文件与对应的内容是这样的: [3]

文件名 文件内容
/proc/cmdline 载入 kernel 时所下达的相关指令与参数!查阅此文件,可了解指令是如何启动的!
/proc/cpuinfo 本机的 CPU 的相关信息,包含频率、类型与运算功能等
/proc/devices 这个文件记录了系统各个主要设备的主要设备代号,与 mknod 有关呢!
/proc/filesystems 目前系统已经载入的文件系统啰!
/proc/interrupts 目前系统上面的 IRQ 分配状态。
/proc/ioports 目前系统上面各个设备所配置的 I/O 位址。
/proc/kcore 这个就是内存的大小啦!好大对吧!但是不要读他啦!
/proc/loadavg 还记得 top 以及 uptime 吧?没错!上头的三个平均数值就是记录在此!
/proc/meminfo 使用 free 列出的内存信息,嘿嘿!在这里也能够查阅到!
/proc/modules 目前我们的 Linux 已经载入的模块列表,也可以想成是驱动程序啦!
/proc/mounts 系统已经挂载的数据,就是用 mount 这个指令调用出来的数据啦!
/proc/swaps 到底系统挂载入的内存在哪里?呵呵!使用掉的 partition 就记录在此啦!
/proc/partitions 使用 fdisk -l 会出现目前所有的 partition 吧?在这个文件当中也有纪录喔!
/proc/uptime 就是用 uptime 的时候,会出现的信息啦!
/proc/version 核心的版本,就是用 uname -a 显示的内容啦!
/proc/bus/* 一些总线的设备,还有 USB 的设备也记录在此喔!

其实,上面这些文件鸟哥在此建议您可以使用 cat 去查阅看看,不必深入了解, 不过,观看过文件内容后,毕竟会比较有感觉啦!如果未来您想要自行撰写某些工具软件, 那么这个目录下面的相关文件可能会对您有点帮助的喔!

16.4.3. 查询已打开文件或已执行程序打开之文件

其实还有一些与程序相关的指令可以值得参考与应用的,我们来谈一谈:

  • fuser:借由文件(或文件系统)找出正在使用该文件的程序

有的时候我想要知道我的程序到底在这次启动过程中打开了多少文件,可以利用 fuser 来观察啦! 举例来说,你如果卸载时发现系统通知:“ device is busy ”,那表示这个文件系统正在忙碌中, 表示有某支程序有利用到该文件系统啦!那么你就可以利用 fuser 来追踪啰!fuser 语法有点像这样:

[root@study ~]# fuser [-umv] [-k [i] [-signal]] file/dir
选项与参数:
-u  :除了程序的 PID 之外,同时列出该程序的拥有者;
-m  :后面接的那个文件名会主动的上提到该文件系统的最顶层,对 umount 不成功很有效!
-v  :可以列出每个文件与程序还有指令的完整相关性!
-k  :找出使用该文件/目录的 PID ,并试图以 SIGKILL 这个讯号给予该 PID;
-i  :必须与 -k 配合,在删除 PID 之前会先询问使用者意愿!
-signal:例如 -1 -15 等等,若不加的话,默认是 SIGKILL (-9) 啰!

范例一:找出目前所在目录的使用 PID/所属帐号/权限 为何?
[root@study ~]# fuser -uv .
                     USER        PID ACCESS COMMAND
/root:               root      13888 ..c.. (root)bash
                     root      31743 ..c.. (root)bash

看到输出的结果没?他说“.”下面有两个 PID 分别为 13888, 31743 的程序,该程序属于 root 且指令为 bash 。 比较有趣的是那个 ACCESS 的项目,那个项目代表的意义为:

  • c :此程序在当前的目录下(非次目录);
  • e :可被触发为执行状态;
  • f :是一个被打开的文件;
  • r :代表顶层目录 (root directory);
  • F :该文件被打开了,不过在等待回应中;
  • m :可能为分享的动态函数库;

那如果你想要查阅某个文件系统下面有多少程序正在占用该文件系统时,那个 -m 的选项就很有帮助了! 让我们来做几个简单的测试,包括实体的文件系统挂载与 /proc 这个虚拟文件系统的内容, 看看有多少的程序对这些挂载点或其他目录的使用状态吧!

范例二:找到所有使用到 /proc 这个文件系统的程序吧!
[root@study ~]# fuser -uv /proc
/proc:               root     kernel mount (root)/proc
                     rtkit       768 .rc.. (rtkit)rtkit-daemon
# 数据量还不会很多,虽然这个目录很繁忙~没关系!我们可以继续这样作,看看其他的程序!

[root@study ~]# fuser -mvu /proc
                     USER        PID ACCESS COMMAND
/proc:               root     kernel mount (root)/proc
                     root          1 f.... (root)systemd
                     root          2 ...e. (root)kthreadd
.....(下面省略).....
# 有这几支程序在进行 /proc 文件系统的存取喔!这样清楚了吗?

范例三:找到所有使用到 /home 这个文件系统的程序吧!
[root@study ~]# echo $$
31743  # 先确认一下,自己的 bash PID 号码吧!
[root@study ~]# cd /home
[root@study home]# fuser -muv .
                     USER        PID ACCESS COMMAND
/home:               root     kernel mount (root)/home
                     dmtsai    31535 ..c.. (dmtsai)bash
                     root      31571 ..c.. (root)passwd
                     root      31737 ..c.. (root)sudo
                     root      31743 ..c.. (root)bash    # 果然,自己的 PID 在啊!
[root@study home]# cd ~
[root@study ~]# umount /home
umount: /home: target is busy.
        (In some cases useful info about processes that use
         the device is found by lsof(8) or fuser(1))
# 从 fuser 的结果可以知道,总共有五只 process 在该目录下运行,那即使 root 离开了 /home,
# 当然还是无法 umount 的!那要怎办?哈哈!可以通过如下方法一个一个删除~
[root@study ~]# fuser -mki /home
/home:               31535c 31571c 31737c  # 你会发现, PID 跟上面查到的相同!
Kill process 31535 ? (y/N) # 这里会问你要不要删除!当然不要乱删除啦!通通取消!

既然可以针对整个文件系统,那么能不能仅针对单一文件啊?当然可以啰!看一下下面的案例先:

范例四:找到 /run 下面属于 FIFO 类型的文件,并且找出存取该文件的程序
[root@study ~]# find /run -type p
.....(前面省略).....
/run/systemd/sessions/165.ref
/run/systemd/sessions/1.ref
/run/systemd/sessions/c1.ref   # 随便抓个项目!就是这个好了!来测试一下!

[root@study ~]# fuser -uv /run/systemd/sessions/c1.ref
                     USER        PID ACCESS COMMAND
/run/systemd/sessions/c1.ref:
                     root        763 f.... (root)systemd-logind
                     root       5450 F.... (root)gdm-session-wor
# 通常系统的 FIFO 文件都会放置到 /run 下面,通过这个方式来追踪该文件被存取的 process!
# 也能够晓得系统有多忙碌啊!呵呵!

如何?很有趣的一个指令吧!通过这个 fuser 我们可以找出使用该文件、目录的程序,借以观察的啦! 他的重点与 ps, pstree 不同。 fuser 可以让我们了解到某个文件 (或文件系统) 目前正在被哪些程序所利用!

  • lsof :列出被程序所打开的文件文件名

相对于 fuser 是由文件或者设备去找出使用该文件或设备的程序,反过来说,如何查出某个程序打开或者使用的文件与设备呢?呼呼!那就是使用 lsof 啰~

[root@study ~]# lsof [-aUu] [+d]
选项与参数:
-a  :多项数据需要“同时成立”才显示出结果时!
-U  :仅列出 Unix like 系统的 socket 文件类型;
-u  :后面接 username,列出该使用者相关程序所打开的文件;
+d  :后面接目录,亦即找出某个目录下面已经被打开的文件!

范例一:列出目前系统上面所有已经被打开的文件与设备:
[root@study ~]# lsof
COMMAND   PID   TID    USER   FD   TYPE    DEVICE  SIZE/OFF       NODE NAME
systemd     1          root  cwd    DIR     253,0      4096        128 /
systemd     1          root  rtd    DIR     253,0      4096        128 /
systemd     1          root  txt    REG     253,0   1230920     967763 /usr/lib/systemd/systemd
....(下面省略)....
# 注意到了吗?是的,在默认的情况下, lsof 会将目前系统上面已经打开的
# 文件全部列出来~所以,画面多的吓人啊!您可以注意到,第一个文件 systemd 执行的
# 地方就在根目录,而根目录,嘿嘿!所在的 inode 也有显示出来喔!

范例二:仅列出关于 root 的所有程序打开的 socket 文件
[root@study ~]# lsof -u root -a -U
COMMAND     PID USER   FD   TYPE             DEVICE SIZE/OFF   NODE NAME
systemd       1 root    3u  unix 0xffff8800b7756580      0t0  13715 socket
systemd       1 root    7u  unix 0xffff8800b7755a40      0t0   1902 @/org/freedesktop/systemd1/notify
systemd       1 root    9u  unix 0xffff8800b7756d00      0t0   1903 /run/systemd/private
.....(中间省略).....
Xorg       4496 root    1u  unix 0xffff8800ab107480      0t0  25981 @/tmp/.X11-unix/X0
Xorg       4496 root    3u  unix 0xffff8800ab107840      0t0  25982 /tmp/.X11-unix/X0
Xorg       4496 root   16u  unix 0xffff8800b7754f00      0t0  25174 @/tmp/.X11-unix/X0
.....(下面省略).....
# 注意到那个 -a 吧!如果你分别输入 lsof -u root 及 lsof -U ,会有啥信息?
# 使用 lsof -u root -U 及 lsof -u root -a -U ,呵呵!都不同啦!
# -a 的用途就是在解决同时需要两个项目都成立时啊! ^_^

范例三:请列出目前系统上面所有的被启动的周边设备
[root@study ~]# lsof +d /dev
COMMAND     PID           USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
systemd       1           root    0u   CHR                1,3      0t0 1028 /dev/null
systemd       1           root    1u   CHR                1,3      0t0 1028 /dev/null
# 看吧!因为设备都在 /dev 里面嘛!所以啰,使用搜寻目录即可啊!

范例四:秀出属于 root 的 bash 这支程序所打开的文件
[root@study ~]# lsof -u root | grep bash
ksmtuned    781 root  txt    REG    253,0    960384   33867220 /usr/bin/bash
bash      13888 root  cwd    DIR    253,0      4096   50331777 /root
bash      13888 root  rtd    DIR    253,0      4096        128 /
bash      13888 root  txt    REG    253,0    960384   33867220 /usr/bin/bash
bash      13888 root  mem    REG    253,0 106065056   17331169 /usr/lib/locale/locale-archive
....(下面省略)....

这个指令可以找出您想要知道的某个程序是否有启用哪些信息?例如上头提到的范例四的执行结果呢! ^_^

  • pidof :找出某支正在执行的程序的 PID
[root@study ~]# pidof [-sx] program_name
选项与参数:
-s  :仅列出一个 PID 而不列出所有的 PID
-x  :同时列出该 program name 可能的 PPID 那个程序的 PID

范例一:列出目前系统上面 systemd 以及 rsyslogd 这两个程序的 PID
[root@study ~]# pidof systemd rsyslogd
1 742
# 理论上,应该会有两个 PID 才对。上面的显示也是出现了两个 PID 喔。
# 分别是 systemd 及 rsyslogd 这两支程序的 PID 啦。

很简单的用法吧,通过这个 pidof 指令,并且配合 ps aux 与正则表达式,就可以很轻易的找到您所想要的程序内容了呢。 如果要找的是 bash ,那就 pidof bash ,立刻列出一堆 PID 号码了~