前言
在Linux系统中,当程式【通常为binary file,以实体档案的型态存在,比如/bin/cp】被触发后,执行者的权限与属性,程式的程式码等所需资料都会被载入记忆体中,作业系统会给这个记忆体内的单元一个识别码PID且依上信息给予这个PID一组有效的权限设定,即程序就是一个正在运作中的程式。Linux的程序呼叫通常为fork-and-exec的流程,即父进程以fork的方式产生一个一摸一样的子程序,然后被复制出来的子程序【继承了父进程绝大部分环境变量】以exec的方式执行实际要进行的程序,最终成为一个子程序。
另外,还有一些常驻在记忆体中的程序,它通常会负责一些系统所提供的功能以服务使用者各项任务,这些常驻程式就会被我们称为服务,比如crond,atd,Apache等等。
bash环境下的工作管理
当我们登入bash后,就会取得一个名为bash的PID,而这个环境下所执行的其他指令就几乎都是所谓的子程序了。我们可以在单一终端介面下同时进行多个工作的行为管理,即job control【注:我们无法以job control的方式由tty1的环境去管理tty2的bash!】。
假设我们进入了一个终端界面,则出现提示字元让你操作的环境就称为前景(foreground),至于其他工作就可以放入背景(background)去暂停或运作【注:放入背景的工作运作时,必须不能与使用者互动,也不能使用ctrl+c来终止】。同时,bash只能够管理自己的工作而不能管理其他bash的工作。那么实际进行job control的指令有哪些?
直接将指令丢到背景中执行的&
当我们只有一个bash环境时,如果想要同时进行多个工作,就可以将某些工作直接丢到背景环境执行,让我们可以继续操作前景的工作。
1 | $ tar -zpcf /tmp/etc.tar.gz /etc & |
将【目前】的工作丢到背景中【暂停】:ctrl+z
举个例子,如果你正在使用vim,发现有个档案不知道放在哪里,需要到bash环境下搜寻,此时只要暂时将vim丢到背
景当中等待即可:
1 | $ vim ~/.bashrc |
观察目前的背景工作状态:jobs
jobs [-lrs]
-l:除列出job number与指令串外,同时列出PID;
-r:仅列出正在背景中run的工作;
-s:仅列出正在背景中stop的工作;
1 | $ jobs -l |
将背景工作拿到前景来处理:fg
fg %jobnumber
1 | $ jobs -l |
让工作在背景下的状态变成运作中:bg
我们知道可以通过ctrl+z将目前的工作丢到背景底下去【暂停】,那如何让一个工作在背景底下【Run】呢?
1 | #執行 find / -perm /7000 > /tmp/text.txt 後,立刻丟到背景去暫停! |
管理背景中的工作:kill
根据上文,我们可以让一个已经在背景中的工作继续工作,也可以让该工作fg拿到前景来,那么如果想要将该工作直接移除,或者将该工作重新启动呢?这时就需要给予该工作一个signal了。
1 | $ kill -signal %jobnumber |
-l:列出目前kill能够使用的讯号signal;
signal:代表给予工作的指示,用man 7 signal可知;
-1:重新读取一次参数设定档(类似reload);
-2:代表与键盘输入ctrl+c同样动作;
-9:立即强制删除一个工作;
-15:以正常的程序方式终止一项工作;
注:kill后面接的数字预设是PID,如果想要管理bash的工作控制就要加上%数字。
1 | $ jobs |
代号 | 名称 | 内容 |
---|---|---|
1 | SIGHUP | 启动被终止的程序,可让该PID重新读取自己的设定档,类似重新启动 |
2 | SIGINT | 相当于键入ctrl+c中断程序执行 |
9 | SIGKILL | 强制中断程序执行,可能会有【半成品】产生 |
15 | SIGTERM | 以正常的接受程序来终止程序,如果程序已经发生问题,输入该signal也无用 |
19 | SIGSTOP | 相当于键入ctrl+z暂停某程序执行 |
离线管理问题
以上工作管理中提到的【背景】都是bash的背景,并不是放到系统的背景去哦,即工作管理的背景依然与终端机有关,比如:假设你以远端连接方式连接到Linux主机且将某工作以&方式放到背景去,那当你在工作尚未结束离开时,该工作是不会继续进行的。那如果你的工作真的需要进行一大段时间,想放置到系统背景下该如何处理呢(可使用例行性工作排程章节的at指令)?
1 | nohup [指令与参数] 《==在终端机前景中工作 |
nohup指令可以让你在离线或登出系统后,还能让工作继续执行,注意nohup不支持bash内建指令。
1 | $ vim sleep500.sh |
程序管理
查阅系统上面正在运作当中的程序:静态的ps或是动态的top,及查阅程序树间关系pstree指令。
ps:将某个时间点的程序运作情况摘取下来
1 | ps aux <==注意没有-,观察系统所有的程序资料 |
- 选项参数:
-A:所有的process均显示出来,同-e;
-a:不与terminal有关的所有process;
-u:有效使用者(effective user)相关的process;
x:通常与a这个参数一起使用,可列出较完整的资讯; - 输出格式规则:
l:较长、较详细的将该PID的资讯列出;
j:工作的格式【job format】;
-f:做一个更为完整的输出;1
2
3
4
5
6
7#仅观察自己的bash相关程序
$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 14830 13970 0 80 0 - 52686 poll_s pts/0 00:00:00 sudo
4 S 0 14835 14830 0 80 0 - 50511 wait pts/0 00:00:00 su
4 S 0 14836 14835 0 80 0 - 29035 wait pts/0 00:00:00 bash
0 R 0 15011 14836 0 80 0 - 30319 - pts/0 00:00:00 ps
其中F代表程序旗标,说明这个程序的终结权限;
S表示程序的状态,主要的状态有:R——Running,S——Sleep,可以被signal唤醒;D:不可被唤醒的睡眠状态,通常此程式可能在等待I/O的情况;T——Stop,可能被工作控制(背景暂停)或除错(traced)状态;Z——Zombie,程序已经终止但无法移除至记忆体外;
UID/PID/PPID:代表此程序被UID拥有/程序的PID/父程序PID;
C:表示CPU使用率,单位为百分比;
PRI/NI:Priority/Nice,代表此程序被CPU所执行的优先顺序,数值越小该程序越快被CPU执行;
ADDR/SZ/WCHAN:都与记忆体有关,ADDR指出该程序在记忆体的哪个部分,running程序一般会显示-;SZ表示程
序用掉多少记忆体;WCHAN表示程序是否运作中,-表示正在运作;
TTY:登入者的终端位置,远端登入时使用动态终端界面(pts/n);
TIME:此程序实际花费CPU运作的时间;
CMD:程式指令;
1 | #观察系统所有的程序 |
top:动态观测程序的变化
相较于ps指令摘取一个时间点的程序状态,top指令可以持续侦测程序运作的状态。top [-d数字] | top [-bnp]
-d:后接秒数,即整个程序里面更新的秒数,预设5秒;
-b:以批处理的方式执行top,通常会搭配资料流重导向来将批次的结果输出为档案;
-n:与-b搭配,需要进行几次top的输出结果;
-p:指定某些个PID来进行观测而已;
在top的执行过程中还可以使用的按键指令:
?:显示在top当中可以输入的按键指令;
P:以CPU的使用资源排序展示;
M:以memory的使用资源展示;
N:以PID来排序;
T:由process使用的CPU时间累计(TIME+)排序;
k:给予某个PID一个讯号(signal);
r:给予某个PID重新制定一个nice值;
q:离开top软件;
1 | $ top -d 2 |
top显示的内容主要分为两个方面:
- 上半部分为整个系统的资源使用情况,基本上总共由六行:第一行的资讯分别为:目前时间,开机到目前为止所经历的时间up 6:07
,已登入系统使用人数,系统在1、5、15分钟的平均工作负载;第二行表示目前程序的总量与个别程序在什么状态(running,sleepi
ng,stopped,zombie);第三行表示CPU的整体负载,每个项目可使用?查阅,需特别注意的wa代表I/O wait;第四行与第五行表示
目前的实际记忆体与虚拟记忆体的使用情况,注意swap的使用量要尽量的少;第六行为当在top程式中输入指令时,显示状态的地方。 - 下半部分则是每个process使用的资源情况:预设以CPU使用率作为排序。
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#将top的资讯进行两次,然后将结果输出到/tmp/top.txt
$ top -b -n 2 > /tmp/top.txt
#使用top观测某单一程序
$ echo $$
14836 <==就是這個數字!他是我們 bash 的 PID
$ top -d 2 -p 14836
top - 01:00:53 up 6:14, 3 users, load average: 0.00, 0.01, 0.05
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.1 sy, 0.0 ni, 99.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2916388 total, 1839264 free, 353424 used, 723700 buff/cache
KiB Swap: 1048572 total, 1048572 free, 0 used. 2318848 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14836 root 20 0 116272 3136 1848 S 0.0 0.1 0:00.07 bash
#在top下进行一些操作,比如修改NI数值
# top页面当中直接按下 r 之后,會出現如下的样式!
top - 01:02:01 up 6:15, 3 users, load average: 0.00, 0.01, 0.05
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.0 sy, 0.0 ni, 99.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 2916388 total, 1839140 free, 353576 used, 723672 buff/cache
KiB Swap: 1048572 total, 1048572 free, 0 used. 2318724 avail Mem
PID to renice [default pid = 14836] 14836
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14836 root 20 0 116272 3136 1848 S 0.0 0.1 0:00.07 bash
#根据提示信息一步步操作即可
pstree查找程序间相关性
pstree [-A|-U] [-up]
- 选项与参数:
-A:各程序树间的连接以ASCII字元连接;
-U:各程序树间的连接以Unicode字元连接,某些终端界面可能显示错误;
-p:同时列出每个process的PID;
-u:同时列出每个process的所属账户名称;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17$ pstree -A
systemd-+-ModemManager---2*[{ModemManager}] # 這行是 ModenManager 與其子程序
|-NetworkManager---3*[{NetworkManager}] # 前面有數字,代表子程序的數量!
....(中間省略)....
|-sshd---sshd---sshd---bash---bash---sudo---su---bash---pstree <==我們指令執行的相依性
....(底下省略)....
#承上例同時秀出PID与users
$ pstree -Aup
systemd(1)-+-ModemManager(745)-+-{ModemManager}(785)
| `-{ModemManager}(790)
|-NetworkManager(870)-+-{NetworkManager}(907)
| |-{NetworkManager}(911)
| `-{NetworkManager}(914)
....(中間省略)....
|-sshd(1326)---sshd(13923)---sshd(13927,dmtsai)---bash(13928)---bash(13970)---
....(底下省略)....
可以看到,所有程序都是依附在systemd这个程式下,他也是由Linux核心所主动呼叫第一支程式,PID=1。
killall -signal 指令名称
由于kill后面必须要加上PID(或者job number),所以通常kill都会配合ps,pstree等指令,如此一来就有些麻烦,那是否有可以利用【下达指令的名称】来给予讯号的呢?killall [-iIe] [command name]
- 选项参数:
-i:互动式,会出现提示字元给使用者;
-I:指令名称(可能含有参数)忽略大小写;
-e:exact,表示后面接的command name要一致,但整个完整指令不能超过15个字元;1
2
3
4
5
6
7
8
9
10
11
12
13給予 rsyslogd 這個指令啟動的 PID 一個 SIGHUP 的訊號
[root@study ~]# killall -1 rsyslogd
#如果用 ps aux 仔細看一下,若包含所有參數,則 /usr/sbin/rsyslogd -n 才是最完整的!
範例二:強制終止所有以 httpd 啟動的程序 (其實並沒有此程序在系統內)
[root@study ~]# killall -9 httpd
範例三:依次詢問每個 bash 程式是否需要被終止運作!
[root@study ~]# killall -i -9 bash
Signal bash(13888) ? (y/N) n <==這個不殺!
Signal bash(13928) ? (y/N) n <==這個不殺!
Signal bash(13970) ? (y/N) n <==這個不殺!
Signal bash(14836) ? (y/N) y <==這個殺掉!
综上,当要刪除某个程序时我们可以使用PID或者是启动该程序的指令名称,而当要刪除某个服务时最简单的方法就是利用 killall ,它能將系統当中所有以某各指令名称启动的程序全部刪除。
Priority与Nice值
对操作系统有些许了解的话,相信大家对CPU调度执行程序应该并不陌生。Linux会给予程序一个所谓的【优先执行序Priority,PRI】,PRI 值越低代表越优先,但此PRI值是由核心动态调整的,用户无法直接调整。那如果需要调整程序的优先执行次序,就要通过Nice指令了:PRI(new) = PRI(old) + nice
:
- root可随意调整自己或他人程序的Nice值,且范围为-20——19;
- 一般用户可调整自己程序的Nice值,且取值范围为0——19【避免一般用户抢占系统资源】;
给予某个程序nice值的方式有两种:
- 开始执行程式时就立即给一个特定的nice值:
nice [-n 數字] command
- 调整某个已经存在的PID的nice值:
renice [number] PID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#用root给一个nice 值为 -5 ,用于执行vim
$ nice -n -5 vim &
[1] 19865
$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 14836 14835 0 90 10 - 29068 wait pts/0 00:00:00 bash
4 T 0 19865 14836 0 85 5 - 37757 signal pts/0 00:00:00 vim
0 R 0 19866 14836 0 90 10 - 30319 - pts/0 00:00:00 ps
$ renice -5 14836
14836 (process ID) old priority 10, new priority -5
$ ps -l
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 14836 14835 0 75 -5 - 29068 wait pts/0 00:00:00 bash
0 R 0 19910 14836 0 75 -5 - 30319 - pts/0 00:00:00 ps
#虽然修改的是 bash程序,但是该程序所触发的 ps 指令当中的 nice 也会继承为-5
free:观察记忆体使用情况
free [-b|-k|-m|-g|-h] [-t] [-s N -c N]
- 选项与参数:
-b :默认显示单位是 Kbytes,也可以使用 b(bytes),m(Mbytes),k(Kbytes),及 g(Gbytes)來作为显示單位
-t :在输出结果中显示实际记忆体与swap 的总量。
-s :可以让系統每几秒輸出一次,不间断刷新
-c :与 -s 同同时处理,表示让 free 列出几次~1
2
3
4
5
6
7
8#显示目前系統的记忆体容量
$ free -m
total used free shared buff/cache available
Mem: 2848 346 1794 8 706 2263
Swap: 1023 0 1023
Mem 那一行显示的是实际记忆体的量,Swap則是记忆体置換空間的量。
#shared/buffers/cached表示已使用量中用来最为缓存或快取的量,在系統比较忙碌时,available为可以被释出而继续利用的量。
#一般來說, swap 最好不要被使用【实际记忆体不足才会使用】,尤其 swap 最好不要被使用超過 20% 以上
uptime:观察系统启动时间与工作负载
其实 uptime 就可以显示出 top 指令结果的最上面一行!
1 | $ uptime |
netstat:追踪网络或插槽档
基本上,netstat的输出分为两大部分:网络与系统自己的程序相关性:netstat -[atunlp]
- 选项与参数:
-a:将目前系统上所有的连接、监听、socket资料都列出来;
-t:列出TCP网络封包的资料;
-u:列出udp网络封包的资料;
-n:不以程序的服务名称,以端号(port number)显示;
-l:列出目前正在网络监听(listen)的服务;
-p:列出该网络服务的程序PID;1
2
3
4
5
6
7
8
9
10
11
12
13#列出目前系统已经建立的网络连接与unix socket状态
$ netstat
Active Internet connections (w/o servers) <==與網路較相關的部分
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 172.16.15.100:ssh 172.16.220.234:48300 ESTABLISHED
Active UNIX domain sockets (w/o servers) <==與本機的程序自己的相關性(非網路)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ] DGRAM 1902 @/org/freedesktop/systemd1/notify
unix 2 [ ] DGRAM 1944 /run/systemd/shutdownd
....(中間省略)....
unix 3 [ ] STREAM CONNECTED 25425 @/tmp/.X11-unix/X0
unix 3 [ ] STREAM CONNECTED 28893
unix 3 [ ] STREAM CONNECTED 21262
- 网际网路连接情况部分:
Proto是网路的封包协定,主要有TCP与UDP包;Recv-Q是非由使用者程式连接到此socket的复制的总bytes数;Send-Q是非由远端
主机传送来的acknowledged总bytes数;Local Address:本地端的IP:port情况;Foreign Address:远端主机的IP:port情
况;State是连接状态,主要有建立(ESTABLISHED)及监听(LISTEN);那么这一行的意义就是:透过TCP封包的连接,远端的
172.16.220.234:48300连接到本地的172.16.15.100:ssh,这条连接的状态是ESTABLISHED。 - 插槽档(socket file)部分:
socket file可以沟通两个程序之间的资讯,上表中socket file的输出档位有:Proto,一般就是Unix;RefCnt是连接到此socket
的程序数量;Flags是连接的旗标;Type是socket存取类型,主要有确认连接STREAM与不需确认DGRAM两种;State若为
CONNECTED表示多个程序间已经建立连接;PATH是连接到此socket的相关程式路径,或者相关资料输出路径;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#找出目前系统中已经在监听的网络连接及其PID
$ netstat -tulnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1326/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2349/master
tcp6 0 0 :::22 :::* LISTEN 1326/sshd
tcp6 0 0 ::1:25 :::* LISTEN 2349/master
udp 0 0 0.0.0.0:123 0.0.0.0:* 751/chronyd
udp 0 0 127.0.0.1:323 0.0.0.0:* 751/chronyd
udp 0 0 0.0.0.0:57808 0.0.0.0:* 743/avahi-daemon: r
udp 0 0 0.0.0.0:5353 0.0.0.0:* 743/avahi-daemon: r
udp6 0 0 :::123 :::* 751/chronyd
udp6 0 0 ::1:323 :::* 751/chronyd
#将上述的0.0.0.0:57808的网路服务关闭
$ kill -9 743
$ killall -9 avahi-daemon
pidof:找出某支正在执行的程式的PID
pidof [-sx] program_name
- 选项与参数:
-s:仅列出一个PID而不是列出所有的PID;
-x:同时列出该program name可能的PPID那个程序的PID;
综上,本节我们初步了解了Linux下程序fork-and-exec的效果,以及bash工作环境管理和程序管理的一些常用命令,尚未深究到底层,比如内存与进程线程间的调度执行等等。