# 特殊文件与进程

在第 6 章中讲到特殊权限 SUID、SGID、SBIT,那么这些权限对于你的 进程 是如何影响的?进程用到的系统资源,比如硬盘资源,使用 umount 硬盘时,出现提示 「device is busy」的提示是怎么回事?

# 具有 SUID、SGID 权限的指令执行状态

SUID 的权限与进程的相关性非常大,SUID 的程序是如何被一般用户执行,具有什么特色?

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

所以,整个 SUID 的权限会生效是由于具有该权限的程序被触发,一个进程表示一个程序的运行,所以执行者可以具有程序拥有者的权限就是在该程序变成进程的时候

比如执行了 passwd 后你就具有 root 的权限?是因为你再触发 passwd 后,会取得一个新的进程与 PID,该 PID 产生时通过 SUID 来给予该 PID 特殊的权限设置

下面使用 mrcode 登录系统并执行 passwd 后,通过工作控制来理解

[mrcode@study ~]$ export LANG=C
[mrcode@study ~]$ passwd
Changing password for user mrcode.
Changing password for mrcode.
(current) UNIX password: 		# 这里按下 ctrl + z,并按下 enter 键

[1]+  Stopped                 passwd
[mrcode@study ~]$ pstree -uA
systemd-+-ModemManager---2*[{ModemManager}]
        |-sshd-+-sshd---sshd(mrcode)-+-bash---su(root)---bash
        |      |                     |-bash---top
        |      |                     |-bash---sleep
        |      |                     `-sftp-server
        |      `-sshd---sshd(mrcode)-+-bash-+-passwd(root)
        |                            |      `-pstree
        |                            |-bash---top
        |                            |-bash---sleep
        |                            `-sftp-server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

从上面的进程来看,在执行 passwd 前是 mrcode 的权限,passwd 则是 root 权限,passwd 是由 bash 衍生出来的,但是权限不一样,这样一来就能理解为什么不同程序所产生的权限不同了,是由于 SUID 程序运行过程中产生的进程的关系

[mrcode@study ~]$ type passwd
passwd is hashed (/usr/bin/passwd)
[mrcode@study ~]$ ll /usr/bin/passwd
-rwsr-xr-x. 1 root root 27856 Aug  9  2019 /usr/bin/passwd
#可以看到,的确该指令也有 s 权限

# 还可以通过以下指令查找 SUID/SGID 的文件
find / -perm /6000
1
2
3
4
5
6
7
8

# /proc/* 代表的意义

进程在内存中,内存中的数据都是写入到 /proc/* 目录下的,可以直接查看该目录

[mrcode@study ~]$ ll /proc/
total 0
dr-xr-xr-x.  9 root           root                         0 Mar 15 20:13 1
dr-xr-xr-x.  9 root           root                         0 Mar 15 20:13 10
dr-xr-xr-x.  9 root           root                         0 Mar 15 20:13 11
...
dr-xr-xr-x.  2 root           root                         0 Mar 15 20:54 sysvipc
-r--r--r--.  1 root           root                         0 Mar 15 20:54 timer_list
-rw-r--r--.  1 root           root                         0 Mar 15 20:54 timer_stats
dr-xr-xr-x.  4 root           root                         0 Mar 15 20:54 tty
-r--r--r--.  1 root           root                         0 Mar 15 20:16 uptime
-r--r--r--.  1 root           root                         0 Mar 15 20:54 version
-r--------.  1 root           root                         0 Mar 15 20:54 vmallocinfo
-r--r--r--.  1 root           root                         0 Mar 15 20:54 vmstat
-r--r--r--.  1 root           root                         0 Mar 15 20:54 zoneinfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

基本上,目前主机上面的各个进程的 PID 都是以目录的形态存在该目录中。如第 1 行的 PID 为 1,它是开机执行的第一个程序 systemd,该 PID 的所有相关信息都写入在 /proc/1/* 下面

[root@study ~]# ll /proc/1
total 0
dr-xr-xr-x. 2 root root 0 Mar 15 20:48 attr
-rw-r--r--. 1 root root 0 Mar 15 20:53 autogroup
-r--------. 1 root root 0 Mar 15 20:53 auxv
-r--r--r--. 1 root root 0 Mar 15 20:13 cgroup
-r--r--r--. 1 root root 0 Mar 15 20:13 cmdline			# 指令串
-r--------. 1 root root 0 Mar 15 20:13 environ			# 一些环节变量
lrwxrwxrwx. 1 root root 0 Mar 15 20:13 exe -> /usr/lib/systemd/systemd

1
2
3
4
5
6
7
8
9
10

里面数据很多,可以查询下 cmdline 的内容

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

上面指令显示了是以什么参数启动的 systemd 指令,这个是针对 PID 有关的内容,下面是针对整个 Linux 系统相关的参数,对应与 /proc 目录下的文件如下

文件名 文件内容
/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 使用 fsidk -l 会出现目前所有的 partition,在该文件中也有记录
/proc/uptime 使用 uptime 出现的信息
/proc/version 核心的版本,使用 uname -a 显示的信息
/proc/bus/* 一些总线的装置,还有 USB 的装置也记录在这里

这些文件内容建议使用 cat 去查阅看看,不必深入了解,如果未来你要写某些工具软件,那么这个目录下相关文件可能会对你有点帮助

# 查询已开启文件或已执行进程开启之文件

还有一些与进程相关的指令可以参考与应用

# fuser:由文件(或文件系统)找出正在使用该文件的进程

fuser [-umv] [-k [i] [signal]] file/dir

选项与参数:
	-u:除了进程的 PID 之外,同时列出该进程的拥有者
	-m:后面接的文件名会主动的上提到该文件系统的最顶层,对 umount 不成功很有效
	-v:可以列出每个文件与进程还有指令的完整相关性
	-k:找出使用该文件/目录的 PID,并试图以 SIGKILL 这个信号给予该 PID
	-i:必须与 -k 配合使用,在删除 PID 之前会先询问使用者
	-signal:例如 -1 -15 等,若不加的话,预设是 -9:SIGKILL
1
2
3
4
5
6
7
8
9
# 范例 1:找出目前所在目录的使用 PID、所属账户、权限
[root@study ~]# fuser -uv .
                     USER        PID ACCESS COMMAND
/root:               root       2604 ..c.. (root)bash

1
2
3
4
5

有一个进程属于 root,而 ACCESS 项则略为复杂一点:

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

如果想知道某个文件系统下又多少进程正在占用该文件系统时,可以使用 -m 选项

下面做几个简单测试,包括实体文件系统挂载与 /proc 虚拟文件系统的内容,看看有多少的进程对这些挂载点或其他目录的使用状态

# 范例 2:找到所有使用到 /proc 这个文件系统的进程
[root@study ~]# fuser -uv /proc/
                     USER        PID ACCESS COMMAND
/proc:               root     kernel mount (root)/proc
                     rtkit       834 .rc.. (rtkit)rtkit-daemon

[root@study ~]# fuser -muv /proc/
                     USER        PID ACCESS COMMAND
/proc:               root     kernel mount (root)/proc
                     root          1 f.... (root)systemd
                     root        589 f.... (root)systemd-journal
                     rtkit       834 .rc.. (rtkit)rtkit-daemon
                     root        844 f.... (root)udisksd
                     root        929 f.... (root)NetworkManager
                     root       1277 F.... (root)libvirtd
                     root       1638 F.... (root)X
                     gdm        1693 f.... (gdm)gnome-shell
                     root       1759 f.... (root)packagekitd
                     mrcode     2280 f.... (mrcode)top
                     mrcode     7722 f.... (mrcode)top
# 这就能看到有几个程序在对该目录进行存取


# 范例 3:找到所有使用到 /home 这个文件系统的进程
[root@study ~]# echo $$
2604				# 先确定下自己的 bash 的进程 PID
[root@study ~]#  cd /home/
[root@study home]# fuser -muv .
                     USER        PID ACCESS COMMAND
/home:               root     kernel mount (root)/home
                     mrcode     1346 ..c.. (mrcode)bash
                     mrcode     1371 ..c.. (mrcode)bash
                     mrcode     1378 ..c.. (mrcode)sleep
                     mrcode     1399 ..c.. (mrcode)sleep
                     mrcode     1958 ..c.. (mrcode)bash
                     mrcode     1991 ..c.. (mrcode)sftp-server
                     mrcode     1992 ..c.. (mrcode)bash
                     mrcode     2280 ..c.. (mrcode)top
                     root       2604 ..c.. (root)bash			# 看这里,自己的 bash 存在列表中
                     mrcode     7294 ..c.. (mrcode)bash
                     mrcode     7358 ..c.. (mrcode)sftp-server
                     mrcode     7362 ..c.. (mrcode)bash
                     mrcode     7722 ..c.. (mrcode)top
                     root       8884 ..c.. (root)passwd

[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 知道有好几个进程在该目录下运行,可以通过如下的方式一个一个删除
[root@study ~]# fuser -mki /home/
/home:                7294c  7358c  7362c  7722c  8884c 19238c 19289c 19291c 19601c 25650c 25674c 25685c 25746c
Kill process 7294 ? (y/N)
# 以上指令有一个问题,颇为棘手,就是很容易杀到自己 bash 的进程,那么久直接把直接踢掉了
# 不知道这个这么排除掉是出方便的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

上面可以针对整个文件系统,其实也可以针对单一文件

# 范例 4:找到 /run 下属于 FIFO 类型的文件,并找出存取该文件的进程
[root@study ~]# find /run -type p
/run/dmeventd-client
/run/dmeventd-server
/run/systemd/inhibit/7.ref
/run/systemd/inhibit/6.ref
/run/systemd/inhibit/5.ref
/run/systemd/inhibit/4.ref
/run/systemd/inhibit/2.ref
/run/systemd/inhibit/1.ref
/run/systemd/sessions/13.ref
/run/systemd/sessions/5.ref
/run/systemd/sessions/c1.ref
/run/systemd/initctl/fifo

# 随便找到文件测试
[root@study ~]# fuser -uv /run/systemd/sessions/c1.ref
                     USER        PID ACCESS COMMAND
/run/systemd/sessions/c1.ref:
                     root        842 f.... (root)systemd-logind
                     root       1649 F.... (root)gdm-session-wor
# 通常系统的 FIFO 文件都会放置到 /run 下,通过该方式来追踪该文件存取的 process
# 同样也能够看到系统有多忙碌(进程多当然就忙碌)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

fuser 的重点是可以让我们了解到某个文件系统或文件目前正在被哪些进程所使用

# lsof:列出被进程所开启的文件名

fuser 是通过文件或则装置名去找使用它的进程,而 lsof 则是通过某个进程去找它开启或使用的文件与装置

lsof [-aUu] [+d]

选项与参数:
	-a:多想数据需要同时成立才显示出结果时
	-U:仅列出 Unix like 系统的 socket 文件类型
	-u:后面接 username,列出该使用者相关进程所开启的文件
	+d:后面接目录,找出某个目录下已经被开启的文件
1
2
3
4
5
6
7
# 范例 1:列出目前系统上所有已经被开启的文件与装置
[root@study ~]# lsof
libvirtd   1277 1318           root  mem       REG              253,0     53848    9645351 /usr/lib64/libavahi-common.so.3.5.3
libvirtd   1277 1318           root  mem       REG              253,0    155784    8569818 /usr/lib64/libselinux.so.1
libvirtd   1277 1318           root  mem       REG              253,0     37056    8655202 /usr/lib64/libacl.so.1.1.0
# 文件很多很多,直接刷屏了


# 范例 2:仅列出关于 root 的所有进程开启的 socket 文件
[root@study ~]# lsof -u root -a -U
COMMAND     PID USER   FD   TYPE             DEVICE SIZE/OFF   NODE NAME
systemd       1 root   12u  unix 0xffff8922437ed800      0t0  12513 /run/systemd/private
systemd       1 root   13u  unix 0xffff892243dc2c00      0t0  25917 /run/systemd/journal/stdout
systemd       1 root   15u  unix 0xffff892243e71800      0t0  25941 /run/systemd/journal/stdout
systemd       1 root   16u  unix 0xffff892243e8fc00      0t0  25942 /run/systemd/journal/stdout
systemd       1 root   17u  unix 0xffff892243e6ec00      0t0  26002 /run/systemd/journal/stdout
systemd       1 root   18u  unix 0xffff892243e6dc00      0t0  26009 /run/systemd/journal/stdout
systemd       1 root   23u  unix 0xffff89224359a800      0t0   7620 /run/systemd/notify
# 注意 -a 参数,分别执行 lsof -u root 及 lsof -U 信息都不同
# -a 取他们的交集结果


# 范例 3:列出目前系统上所有被启动的周边装置
[root@study ~]# lsof +d /dev/
COMMAND     PID           USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
systemd       1           root    0u   CHR                1,3      0t0 5342 /dev/null
systemd       1           root    1u   CHR                1,3      0t0 5342 /dev/null
systemd       1           root    2u   CHR                1,3      0t0 5342 /dev/null
systemd       1           root   27r   CHR             10,235      0t0 7250 /dev/autofs
systemd       1           root   30u  unix 0xffff8922436ce000      0t0 7645 /dev/log
kdevtmpfs    13           root  cwd    DIR                0,5     3340    3 /dev
# 因为都在目录中,所以搜索目录即可

# 范例 4:列出 root 的 bash 程序开启的文件
[root@study ~]# lsof -u root | grep bash
ksmtuned    921 root  txt       REG              253,0    964600     309027 /usr/bin/bash
bash      20030 root  cwd       DIR              253,0      4096   25165889 /root
bash      20030 root  rtd       DIR              253,0       251         64 /
bash      20030 root  txt       REG              253,0    964600     309027 /usr/bin/bash
bash      20030 root  mem       REG              253,0 106075056     309022 /usr/lib/locale/locale-archive
bash      20030 root  mem       REG              253,0     61624    8548289 /usr/lib64/libnss_files-2.17.so
bash      20030 root  mem       REG              253,0   2156160    8532847 /usr/lib64/libc-2.17.so
bash      20030 root  mem       REG              253,0     19288    8532853 /usr/lib64/libdl-2.17.so
bash      20030 root  mem       REG              253,0    174576    8548350 /usr/lib64/libtinfo.so.5.9
bash      20030 root  mem       REG              253,0    163400    8532840 /usr/lib64/ld-2.17.so
bash      20030 root  mem       REG              253,0     26254   16946906 /usr/lib64/gconv/gconv-modules.cache
bash      20030 root    0u      CHR              136,0       0t0          3 /dev/pts/0
bash      20030 root    1u      CHR              136,0       0t0          3 /dev/pts/0
bash      20030 root    2u      CHR              136,0       0t0          3 /dev/pts/0
bash      20030 root  255u      CHR              136,0       0t0          3 /dev/pts/0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

可以通过范例 4 找出某个进程是否有启用哪些信息

# pidof:找出某个正在运行的程序的 PID

pidof [-sx] program_name

选项与参数:
	-s:仅列出一个 PID 而不列出所有的 PID
	-x:同时列出该程序可能的 PPID 那个进程的 PID
1
2
3
4
5
# 范例 1:列出目前系统上 systemd 以及 rsyslogd 这两个程序的 PID
[root@study ~]# pidof systemd rsyslogd
1 1265
# 结果显示的是两个 PID
1
2
3
4

pidof 指令较简单,可配合 pas aux 与正则表示法,就可以很轻易的找到你想要的进程内容了。如果要找的是 bash,那就 pidof bash ,就列出一堆 PID 号码了