1. 进程
程序放在硬盘中,在运行它的时候加载到内存,在内存里程序以进程的方式运行,进程有惟一的 ID ,叫 PID。
写个简单的 Hellow world 程序,让它产生 PID:
[root@test]$ cat test.cpp
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int i = 0;
for(i==0;i<100;i++)
{
sleep(2);
printf("Hello World\n");
}
}
[root@test]$ gcc test.cpp -o test.out
[root@test]$ ./test.out &
[1] 35166
[root@test]$ Hello World
Hello World
^C
[root@test]$ Hello World
ps Hello World
-lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 34824 34817 0 80 0 - 28750 do_wai 22:49 pts/15 00:00:00 -bash
0 S root 35166 34824 0 80 0 - 1056 hrtime 22:51 pts/15 00:00:00 ./test.out
0 R root 35174 34824 0 80 0 - 38356 - 22:51 pts/15 00:00:00 ps -lf
[root@test]$ Hello World
执行命令 ./test.out ,Hello World 程序被执行,产生了一个 PID 为 35166 的进程。
上面示例中用到了 ps -lf 命令,该命令会查看当前终端的进程信息。各参数的意思是:
F:进程标志(Process Flags),说明进程权限。常见号码有: 4 表示进程权限为 root,1 表示该进程只能够复制(fork),不能被执行。
S:进程状态。S 表示 sleep,该进程正在睡眠状态 idle,能够被唤醒(signal)。R 表示 running,即该进程正在运行。T 表示 stop,该进程出于中止状态,多是后台暂停或者 traced 状态。Z 表示 zombie 僵尸进程,该进程已经终止可是还常驻在内存中。D 表示该进程处于不可被唤醒的睡眠状态,如等待 I/O 的状况。
UID: 执行该进程的用户身份。
PID: 进程 ID。
PPID: 父进程的 ID。
C:表示 CPU 的使用率,单位为百分比。
PRI:Priority,表示该进程执行的优先级,系统动态调整,不可设置。
NI: Nice,加权的优先级系数,可设置;PRI/NI 数值越小表示该进程越快被 CPU 执行。
ADDR:内存相关。表示进程在内存的哪一个部分,running 的进程通常显示 “-”。
SZ:内存相关。表示进程用掉多少内存。
WCHAN:表示目前进程是否正在运行,“-”表示正在运行。
STIME: 进程的启动时间。
TTY: 登录用户的终端机位置,远程登录则显示 pts/n 。
TIME: 进程的运行时间。
CMD: 什么命令触发了该进程。
能够看出,进程 35166 的父进程是 34824 即 bash 的进程。
在远程客户端登录主机服务端时,主机会提供一个 bash 进程给远端,这样客户端就能够以 tty 终端来访问服务端主机了。在 bash 进程上执行命令时,bash 进程就成了执行命令产生进程的父进程。好比:
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 34824 34817 0 80 0 - 28750 do_wai 22:49 pts/15 00:00:00 -bash
0 R root 38815 34824 0 80 0 - 38356 - 23:30 pts/15 00:00:00 ps -lf
[root@test]$ bash
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 34824 34817 0 80 0 - 28750 do_wai 22:49 pts/15 00:00:00 -bash
4 S root 38838 34824 0 80 0 - 28715 do_wai 23:30 pts/15 00:00:00 bash
0 R root 38882 38838 0 80 0 - 38356 - 23:30 pts/15 00:00:00 ps -lf
[root@test]$ exit
exit
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 34824 34817 0 80 0 - 28750 do_wai 22:49 pts/15 00:00:00 -bash
0 R root 38900 34824 0 80 0 - 38356 - 23:30 pts/15 00:00:00 ps -lf
在 bash 进程 34824 上执行 bash 命令产生一个新的 bash 进程 38838,它的父进程正是 34824,而且父进程是处于 sleep 状态的。
执行 Hello World 程序使用的是 ./test.out & 命令,在命令后加 & 号会将当前工做转到后台执行,同时终端界面会打印 [1] 35166,[1] 表示该工做的 ID,35166 表示产生的进程 PID。
不过,即便工做转到后台了,程序执行的输出仍是会打印到终端上。能够加个输入流,将执行的正确信息流到 std.out 中,错误信息流到 std.err 中:
[root@test]$ ./test.out 1> std.out 2> std.err &
[1] 41371
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 34824 34817 0 80 0 - 28750 do_wai Apr21 pts/15 00:00:00 -bash
0 S root 41371 34824 0 80 0 - 1056 hrtime 00:00 pts/15 00:00:00 ./test.out
0 R root 41379 34824 0 80 0 - 38356 - 00:00 pts/15 00:00:00 ps -lf
[root@test]$ cat std.out
[root@test]$
数据流到 std.out 中,可是 cat std.out 却没有记录。将 Hello World 更改下从新执行:
[root@test]$ cat test.cpp
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int i = 0;
for(i==0;i<10;i++)
{
sleep(2);
printf("Hello World\n");
}
}
[root@test]$ gcc test.cpp -o test.out
[root@test]$ cat std.out
[root@test]$ ./test.out 1> std.out 2> std.err &
[1] 41775
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 34824 34817 0 80 0 - 28750 do_wai Apr21 pts/15 00:00:00 -bash
0 S root 41775 34824 0 80 0 - 1056 hrtime 00:04 pts/15 00:00:00 ./test.out
0 R root 41779 34824 0 80 0 - 38356 - 00:04 pts/15 00:00:00 ps -lf
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 34824 34817 0 80 0 - 28750 do_wai Apr21 pts/15 00:00:00 -bash
0 R root 41870 34824 0 80 0 - 38356 - 00:05 pts/15 00:00:00 ps -lf
[1]+ Done ./test.out > std.out 2> std.err
[root@test]$ cat std.out
Hello World
Hello World
Hello World
...
[root@test]$
此次 std.out 就有输出了,对比上面两个例子能够发现,当进程执行完以后才会把数据写到 std.out 中,而不是执行一行写一行,系统会有个数据缓冲区,进程在执行的时候会将数据写到缓冲区,在进程结束以后在取出缓冲区中的数据,写到文件 std.out 中。
固然,除了 ps -lf 命令能够查看进程信息,还有诸如 ps aux 和 ps -lA 命令也能够查看进程信息。还有很多 ps 加各类选项参数的命令也也能够查看进程信息,可是最经常使用的仍是 ps aux , ps - lA 和 ps -lf 这几个:
[root@test:/home/robot]
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.4 0.0 171620 11604 ? Ss 14:16 0:26 /usr/lib/systemd/systemd --switched-root --system --deserialize 17
systemd+ 591 0.0 0.0 19152 6400 ? Ss 14:17 0:00 /usr/lib/systemd/systemd-networkd
avahi 761 0.0 0.0 9656 6244 ? Ss 14:17 0:04 avahi-daemon: running [test
root 766 0.0 0.0 6028 2152 ttyS0 Ss+ 14:17 0:00 /sbin/agetty -o -p -- \u --keep-baud 115200,38400,9600 ttyS0 vt220
root 767 0.0 0.0 6388 1604 tty1 Ss+ 14:17 0:00 /sbin/agetty -o -p -- \u --noclear tty1 linux
root 777 0.0 0.0 614464 18100 ? Ssl 14:17 0:04 python3 -u /opt/nokia/libexec/StorageUtils/script/lsa/local_storage_agent.py
avahi 778 0.0 0.0 8944 432 ? S 14:17 0:00 avahi-daemon: chroot helper
_test+ 779 0.0 0.0 164384 5784 ? Ssl 14:17 0:01 /opt/nokia/bin/eventdaemon --global
root 784 0.0 0.0 0 0 ? Z 14:17 0:00 [cat] <defunct>
root 785 0.0 0.0 7672 1196 ? S 14:17 0:00 avahi-publish -a test.local
_test+ 787 0.0 0.0 26736 16288 ? Ss 14:17 0:00 /opt/nokia/test-acl/bin/ipsetmanager
...
[root@test:/home/robot]
# ps -lA
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
4 S 0 1 0 0 80 0 - 42905 SyS_ep ? 00:00:26 systemd
0 S 0 531 525 0 80 0 - 1455 hrtime ? 00:00:00 schedmon
4 S 0 534 1 0 80 0 - 1879 hrtime ? 00:00:00 hypertracerolcd
4 S 0 550 1 0 80 0 - 205661 core_s ? 00:00:15 python3
4 S 192 591 1 0 80 0 - 4788 SyS_ep ? 00:00:00 systemd-network
...
ps aux 和 ps -lA 均可以查看系统运行的全部进程,不一样的是输出信息不一样,ps -lA 的输出信息和 ps -lf 一致,而 ps aux 的输出信息是 %CPU / %MEM / VSZ / RSS / STAT 等等。
各参数的意思分别是:
%CPU:进程使用 CPU 的资源百分比;
%MEM:进程占用物理内存百分比;
VSZ:进程使用的虚拟内存量(KB);
RSS:进程占用的固定内存量(KB);
STAT:进程目前的状态。有的进程会同时有多个字符,它们表明的意思是:
D 不可中断,Uninterruptible sleep (usually IO)
R 正在运行,或在队列中的进程
S 处于休眠状态
T 中止或被追踪
Z 僵尸进程
X 死掉的进程
< 高优先级
N 低优先级
L 有些页被锁进内存
s 包含子进程
+ 位于后台的进程组
l 多线程,克隆线程 multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
除了 ps 命令查看进程信息的命令,还有 pstree 命令可查看各进程的依赖关系:
[root@test]$ pstree -p
systemd(1)─┬─abrt-dbus(45043)─┬─{abrt-dbus}(45044)
│ └─{abrt-dbus}(45046)
├─agetty(2037)
├─agetty(2039)
├─atd(1989)
├─auditd(604)─┬─audispd(19274)─┬─sedispatch(19275)
│ │ └─{audispd}(19276)
│ └─{auditd}(605)
├─automount(2029)─┬─{automount}(2030)
│ ├─{automount}(2031)
│ └─{automount}(2056)
...
其中 -p 选项可查看各进程的 PID,能够看到有个 PID 为 1 的 systemd 进程,它是 Liunx 的启动的第一个进程是 systemd 进程,是全部进程的进程之王,它下面管理着不少子进程,这些子进程又会生出多个子子进程。如 PID 为 2029 的 automount 进程,它的父进程是 systemd ,它也是 2030/2031/2056 的父进程。2030/2031/2056 它们几个是兄弟进程。
ps 和 pstree 执行的结果是静态的,而使用 top 命令能够动态查看进程的变化状况:
[root@test]$ top -H
top - 20:49:51 up 264 days, 3:07, 14 users, load average: 0.13, 0.08, 0.02
Threads: 567 total, 1 running, 565 sleeping, 0 stopped, 1 zombie
%Cpu(s): 0.2 us, 0.1 sy, 0.0 ni, 99.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 37077464 total, 12919928 free, 2649664 used, 21507872 buff/cache
KiB Swap: 1020 total, 0 free, 1020 used. 33678848 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
44644 root 20 0 12.7g 335660 21284 S 0.7 0.9 0:04.60 java
43794 root 20 0 12.7g 323332 21332 S 0.7 0.9 0:06.44 java
1959 fluentd 20 0 1382452 28436 4896 S 0.3 0.1 42:04.92 fluentd
top 的输出信息有好几行,从上到下分别表示:
第一行: 当前时间;系统开机到如今过了多长时间(看看 264 天没关机,这是 windows 能比的了哒);当前登录系统的用户人数;系统在 1/5/15 分钟的平均工做负载,分别是 0.13/0.08/0.02,越小表示系统越闲置,若高于 1 表示系统 load 偏高。
第二行: 当前进程的总量,以及进程的整体运行状态。
第三行: CPU 的总体负载。us 表示 CPU 消耗在用户空间(user space)的时间百分比;sy 表示 CPU 消耗在内核空间(kernel space)的时间百分比;<关于用户空间和内核空间的文章可看这里>ni(niceness) 表示 CPU 消耗在 nice 进程(低优先级)的时间百分比;id(idle) 表示 CPU 消耗在闲置进程的时间百分比,值越低表示 CPU 越忙,这里 id 是 99.6 ,看来 CPU 不忙啊。wa(wait) CPU 等待外部 I/O 的时间百分比,在等待 I/O 的时间里 CPU 不能干其它事情;hi(hardware interrupt) CPU 响应硬件中断请求的时间百分比;si(software interrupt) CPU 响应软件中断请求的时间百分比;
第四行:物理内存 Mem 使用状况;
第五行:虚拟内存 swap 使用状况;
第六行: PR(Priority) 进程的优先级执行顺序,越小表示越早被执行,该值由系统指定,用户不能更改;NI(nice)与优先级相关,用户可更改。
2. 工做管理
将程序/命令转到后台执行的形式都属于工做管理的范畴,顾名思义,工做管理就是管理 Liunx 系统下各个工做的。使用 & 将命令转到后台执行,搭配流输入将数据转到文件中,从而避免在终端界面显示,前面已经演示过了。
& 命令将工做转到后台,工做是出于运行状态的。而直接使用快捷键 Ctrl + Z 会将工做转到后台并处于暂停状态:
[root@test test]# vi log.txt
[1]+ Stopped vi log.txt
[root@test test]# ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
0 S root 107543 107542 0 80 0 - 6374 - 15:58 pts/0 00:00:00 -bash
0 T root 108848 107543 0 80 0 - 9126 - 15:58 pts/0 00:00:00 vi log.txt
0 R root 109559 107543 0 80 0 - 14894 - 15:58 pts/0 00:00:00 ps -lf
[root@test test]#
命令 vi log.txt 产生的进程被转到后台,而且它的状态是 T stopped。
jobs 命令能够查看当前工做的状况,fg 命令可将当前工做从后台转到前台,而且该工做是出于运行状态:
[root@test test]# jobs
[1]+ Stopped vi log.txt
[root@test test]# fg %1
vi log.txt
[root@test test]# jobs
[1]- Stopped vi log.txt
[2]+ Stopped vi abc
[root@test test]# fg
vi abc
注意,fg 后加 job 编号即表示将该 job 从后台转到前台,前面要加 % 号。jobs 的输出 job 编号后有 + 和 - 号,+ 表示默认调用的工做,不加编号的 fg 命令会调用 + 所在的那个 job。
fg 命令是将工做从后台转到前台。bg 命令会将后台出于暂停状态的工做转为运行状态,一样的,bg 后需加 % 和工做编号:
[root@test]$ ./test.out
Hello World
^Z
[1]+ Stopped ./test.out
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 49612 49605 0 80 0 - 28750 do_wai 00:13 pts/15 00:00:00 -bash
0 T root 49854 49612 0 80 0 - 1056 do_sig 00:14 pts/15 00:00:00 ./test.out
0 R root 49880 49612 0 80 0 - 38356 - 00:14 pts/15 00:00:00 ps -lf
[root@test]$ bg %1
[1]+ ./test.out &
Hello World
[root@test]$ Hello World
Hello World
^C
[root@test]$ Hello World
Hello World
Hello World
上面的例子中,程序转到后台执行时会循环打印 Hello World,使用 Ctrl + C 也没办法将终止程序,仍是会继续打印。此时就要用到 kill 命令杀掉执行该程序的进程或者杀掉该工做。
kill -l 命令可查看 kill 可以使用的信号:
[root@test]$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
一般使用的最多的信号就是 1/9/15。1 SIGHUP 表示从新读取参数的配置文件,9 SIGKILL 强制删除工做/进程,15 SIGTERM 以正常方式结束一个工做/进程,若是一个工做/进程不能以正常方式结束掉,仍是要使用 9 强制结束的。
不加信号的 kill 默认使用的是 15 SIGTERM 这个信号。若是要 kill 工做的话,须要在工做编号前加 % 号,默认 kill 的是进程的 PID,下面用两种方式 kill Hello World 程序产生的进程:
# kill -9 %n
[root@test]$ ./test.out
Hello World
Hello World
^Z
[1]+ Stopped ./test.out
[root@test]$ kill -9 %1
[1]+ Killed ./test.out
# kill -9 PID
[root@test]$ ./test.out
Hello World
^Z
[1]+ Stopped ./test.out
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 49612 49605 0 80 0 - 28750 do_wai 00:13 pts/15 00:00:00 -bash
0 T root 50944 49612 0 80 0 - 1056 do_sig 00:28 pts/15 00:00:00 ./test.out
0 R root 50950 49612 0 80 0 - 38356 - 00:28 pts/15 00:00:00 ps -lf
[root@test]$ bg %1
[1]+ ./test.out &
Hello World
[root@test]$ Hello World
Hello World
[1]+ Killed ./test.out
[root@tu-qiaolin-683ci-1 ~]$ kill -9 50944 # exec at another tty
使用 & 和 Ctrl + Z 将工做放到后台运行时,若是当前终端关闭了那么相应的工做也会被停掉(当前终端 bash 是 & 和 Ctrl + Z 的父进程,父进程关掉了,相应的子进程也会随之结束),使得工做不能继续。Liunx 提供了 nohup 命令能够将工做放到系统后台运行,即便当前终端关闭了,工做仍是能够继续运行(当前终端关闭了,nohup 会将 systemd 做为本身的父进程继续执行),举例:
[root@test]$ cat test.cpp
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int i = 0;
for(i==0;i<20;i++)
{
sleep(2);
printf("Hello World\n");
}
}
[root@test]$ gcc test.cpp -o test.out
[root@test]$ nohup ./test.out &
[1] 50772
[root@test]$ nohup: ignoring input and appending output to ‘nohup.out’
[root@test]$ ps -lf
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
4 S root 45024 45018 0 80 0 - 28752 do_wai 20:39 pts/15 00:00:00 -bash
0 S root 50772 45024 0 80 0 - 1056 hrtime 21:41 pts/15 00:00:00 ./test.out
0 R root 50777 45024 0 80 0 - 38356 - 21:41 pts/15 00:00:00 ps -lf
[root@test]$ exit
logout
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Session stopped
Last login: Thu Apr 23 23:48:09 2020 from 10.140.119.177
[root@tu-qiaolin-683ci-1 ~]$ ps -lA | grep 50772
0 S 0 50772 1 0 80 0 - 1056 hrtime ? 00:00:00 test.out
[root@tu-qiaolin-683ci-1 ~]$ cat test/nohup.out
Hello World
Hello World
Hello World
...
[root@tu-qiaolin-683ci-1 ~]$
退出了当前终端,工做仍是继续在系统后台运行,且 nohup 产生的进程被 systemd 进程给接管了。
(完)