htop 完全解读:从负载均值到进程状态,一张图搞懂系统监控
一篇详尽的 htop 指南,作者逐一拆解 htop 界面上每个字段的含义,从 uptime、load average 到进程状态 (R/S/D/Z/T/t),再到 VIRT/RES/SHR 内存指标,结合 /proc 文件系统、strace、代码示例和实际实验,澄清常见误解(如负载均值 ≠ CPU 使用率)。文末还附录了 Ubuntu Server 16.04 上各启动进程的用途分析及极端精简方案。适合所有想深入理解 Linux 进程管理的后端工程师。
For the longest time I did not know what everything meant in htop.
I thought that load average 1.0 on my two core machine means that the CPU usage is at 50%. That's not quite right. And also, why does it say 1.0?
I decided to look everything up and document it here.
They also say that the best way to learn something is to try to teach it.
很长一段时间里,我都不理解 htop 界面中各种信息的含义。
我曾以为,双核机器上负载平均值 1.0 意味着 CPU 使用率是 50%。 但这并不准确。 而且,为什么它显示的是 1.0 呢?
我决定把这一切都查清楚,并记录在这里。
人们也常说,学习某个东西最好的方法就是尝试去教它。
Uptime shows how long the system has been running.
You can see the same information by running uptime:
$ uptime 12:17:58 up 111 days, 31 min, 1 user, load average: 0.00, 0.01, 0.05
How does the uptime program know that?
It reads the information from the file /proc/uptime.
9592411.58 9566042.33
The first number is the total number of seconds the system has been up. The second number is how much of that time the machine has spent idle, in seconds
The second value may be greater than the overall system uptime on systems with multiple cores since it is a sum.
How did I know that? I looked at what files the uptime program opens when it is run. We can use the strace tool to do that.
strace uptime
There will be a lot of output. We can grep for the open system call. But that will not really work since strace outputs everything to the standard error (stderr) stream. We can redirect the stderr to the standard output (stdout) stream with 2>&1.
Our output is this:
$ strace uptime 2>&1 | grep open ... open("/proc/uptime", O_RDONLY) = 3 open("/var/run/utmp", O_RDONLY|O_CLOEXEC) = 4 open("/proc/loadavg", O_RDONLY) = 4
which contains the file /proc/uptime which I mentioned.
It turns out that you can also use strace -e open uptime and not bother with grepping.
So why do we need the uptime program if we can just read the contents of the file? The uptime output is nicely formatted for humans whereas the number of seconds is more useful for using in your own programs or scripts.
Uptime 显示系统已经运行了多长时间。
通过运行 uptime 命令可以看到同样信息:
$ uptime 12:17:58 up 111 days, 31 min, 1 user, load average: 0.00, 0.01, 0.05
uptime 程序是怎么知道的?
它从 /proc/uptime 文件中读取信息。
9592411.58 9566042.33
第一个数字是系统已启动的总秒数。 第二个数字是其中机器处于空闲状态的秒数。
多核系统上,第二个值可能大于系统总运行时间, 因为它是所有核心的空闲时间之和。
我怎么知道的?我查看了 uptime 程序运行时打开了哪些文件。 我们可以用 strace 工具来做这件事。
strace uptime
输出会很多。 我们可以用 grep 过滤出 open 系统调用。 但这不太管用,因为 strace 把所有输出都写到了标准错误流(stderr)。 我们可以用 2>&1 将标准错误重定向到标准输出(stdout)。
输出如下:
$ strace uptime 2>&1 | grep open ... open("/proc/uptime", O_RDONLY) = 3 open("/var/run/utmp", O_RDONLY|O_CLOEXEC) = 4 open("/proc/loadavg", O_RDONLY) = 4
其中就包含我刚提到的 /proc/uptime 文件。
其实也可以直接用 strace -e open uptime,免去 grep 的麻烦。
既然可以直接读取文件内容,为什么还需要 uptime 程序呢? uptime 的输出对人类更友好, 而秒数更适合在程序或脚本中使用。
In addition to uptime, there were also three numbers that represent the load average.
$ uptime 12:59:09 up 32 min, 1 user, load average: 0.00, 0.01, 0.03
They are taken from the /proc/loadavg file. If you take another look at the strace output, you'll see that this file was also opened.
$ cat /proc/loadavg 0.00 0.01 0.03 1/120 1500
The first three columns represent the average system load of the last 1, 5, and 15 minute periods. The fourth column shows the number of currently running processes and the total number of processes. The last column displays the last process ID used.
Let's start with the last number.
Whenever you launch a new process, it is assigned an ID number. Process IDs are usually increasing, unless they've been exausted and are being reused. The process ID of 1 belongs to /sbin/init which is started at boot time.
Let's look at the /proc/loadavg contents again and then launch the sleep command in the background. When it's launched in the background, its process ID will be shown.
$ cat /proc/loadavg 0.00 0.01 0.03 1/123 1566 $ sleep 10 & [1] 1567
So the 1/123 means that there is one process running or ready to run at this time and there are 123 processed in total.
When you run htop and see just one running process, it means that it is the htop process itself.
If you run sleep 30 and run htop again, you'll notice that there is still just 1 running process. That's because sleep is not running, it is sleeping or idling or in other words waiting for something to happen. A running process is a process that is currently running on the physical CPU or waiting its turn to run on the CPU.
If you run cat /dev/urandom > /dev/null which repeatedly generates random bytes and writes them to a special file that is never read from, you will see that there are now two running process.
$ cat /dev/urandom > /dev/null & [1] 1639 $ cat /proc/loadavg 1.00 0.69 0.35 2/124 1679
So there are now two running processes (random number generation and the cat that reads the contents of /proc/loadavg) and you'll also notice that the load averages have increased.
The load average represents the average system load over a period of time.
The load number is calculated by counting the number of running (currently running or waiting to run) and uninterruptible processes (waiting for disk or network activity). So it's simply a number of processes.
The load averages are then the average number of those processes during the last 1, 5 and 15 minutes, right?
It turns out it's not as simple as that.
The load average is the exponentially damped moving average of the load number. From Wikipedia:
Mathematically speaking, all three values always average all the system load since the system started up. They all decay exponentially, but they decay at different speed. Hence, the 1-minute load average will add up 63% of the load from last minute, plus 37% of the load since start up excluding the last minute. Therefore, it's not technically accurate that the 1-minute load average only includes the last 60 seconds activity (since it still includes 37% activity from the past), but that includes mostly the last minute.
Is that what you expected?
Let's return to our random number generation.
$ cat /proc/loadavg 1.00 0.69 0.35 2/124 1679
While technically not correct, this is how I simplify load averages to make it easier to reason about them.
In this case, the random number generation process is CPU bound, so the load average over the last minute is 1.00 or on average 1 running process.
Since there is only one CPU on my system, the CPU utilization is 100% since my CPU can run only one process at a time.
If I had two cores, my CPU usage would be 50% since my computer can run two processes at the same time. The load average of a computer with 2 cores that has a 100% CPU utilization would be 2.00.
You can see the number of your cores or CPUs in the top left corner of htop or by running nproc.
Because the load number also includes processes in uninterruptible states which don't have much effect on CPU utilization, it's not quite correct to infer CPU usage from load averages like I just did. This also explains why you may see high load averages but not much load on the CPU.
But there are tools like mpstat that can show the instantaneous CPU utilization.
$ sudo apt install sysstat -y $ mpstat 1 Linux 4.4.0-47-generic (hostname) 12/03/2016 x86_64 (1 CPU)
10:16:20 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 10:16:21 PM all 0.00 0.00 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 10:16:22 PM all 0.00 0.00 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 10:16:23 PM all 0.00 0.00 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
...
kill cat /dev/urandom
...
10:17:00 PM all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 10:17:01 PM all 1.00 0.00 0.00 2.00 0.00 0.00 0.00 0.00 0.00 97.00 10:17:02 PM all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
Why do we use load averages then?
除了运行时间,还有三个数字表示负载平均值(load average)。
$ uptime 12:59:09 up 32 min, 1 user, load average: 0.00, 0.01, 0.03
它们取自 /proc/loadavg 文件。 再看一眼 strace 的输出,你会看到这个文件也被打开了。
$ cat /proc/loadavg 0.00 0.01 0.03 1/120 1500
前三列分别代表最近 1、5 和 15 分钟的平均系统负载。第四列显示当前正在运行的进程数和总进程数。最后一列显示最近使用的进程 ID。
先来看最后一个数字。
每当启动一个新进程,内核就会分配一个 ID 号。进程 ID 通常是递增的,除非用完了再重用。 PID 1 属于 /sbin/init,它在启动时被创建。
我们再看一下 /proc/loadavg 的内容,然后在后台运行 sleep 命令。 当它在后台启动时,会显示进程 ID。
$ cat /proc/loadavg 0.00 0.01 0.03 1/123 1566 $ sleep 10 & [1] 1567
所以 1/123 表示此时此刻有 1 个进程正在运行或待运行,总共有 123 个进程。
运行 htop 时看到只有一个 running 进程,那其实正是 htop 自己。
如果你运行 sleep 30 后再运行 htop,你会发现仍然只有一个 running 进程。 这是因为 sleep 并没有在运行,它处于 sleeping 或 idling 状态,也就是在等待某些事情发生。 Running 进程指的是当前正在物理 CPU 上运行或排队等待 CPU 的进程。
如果你运行 cat /dev/urandom > /dev/null(不断生成随机字节并写入一个从不读取的特殊文件),会看到现在有两个 running 进程。
$ cat /dev/urandom > /dev/null & [1] 1639 $ cat /proc/loadavg 1.00 0.69 0.35 2/124 1679
现在有两个正在运行的进程(随机数生成程序,以及读取 /proc/loadavg 内容的 cat), 你还会注意到负载平均值升高了。
负载平均值代表了系统在一段时间内的平均负载。
这个负载数字是通过统计 running(当前正在运行或等待运行)和 uninterruptible(等待磁盘或网络活动)状态的进程数计算得出的。 所以它本质上就是一个进程数量。
你以为负载平均值就是最近 1、5 和 15 分钟内这些进程数量的平均值?
实际并没有这么简单。
负载平均值是负载数字的指数衰减移动平均。来自维基百科:
从数学上讲,这三个值始终是系统自启动以来的全部系统负载的平均值。它们都呈指数衰减,但衰减速度不同。因此,1 分钟负载平均值会包含过去 1 分钟负载的 63%,加上系统启动以来(排除最后一分钟)负载的 37%。所以严格来说,1 分钟负载平均值并不仅仅包含过去 60 秒的活动(因为它还包含来自过去的 37% 的活动),但它主要反映最近一分钟的情况。
这和你预期的一样吗?
回到我们的随机数生成例子:
$ cat /proc/loadavg 1.00 0.69 0.35 2/124 1679
虽然技术上不精确,但以下是我简化负载平均值以便理解的方式:
这个例子中,随机数生成进程是 CPU 密集型的,所以过去一分钟的负载平均是 1.00,也就是平均 1 个 running 进程。
由于我的系统只有一个 CPU,CPU 利用率是 100%,因为 CPU 一次只能运行一个进程。
如果我有两个核心,CPU 使用率会是 50%,因为计算机可以同时运行两个进程。 一个 2 核计算机的负载平均达到 2.00 时,CPU 利用率就是 100%。
你可以在 htop 的左上角或通过运行 nproc 看到核心或 CPU 的数量。
因为负载数字也包含了处于 uninterruptible 状态的进程,它们对 CPU 利用率影响不大, 所以像上面那样从负载平均值推断 CPU 使用率并不完全正确。 这也解释了为什么你可能会看到负载平均值很高但 CPU 负载却不大。
不过有 mpstat 这样的工具可以显示瞬时 CPU 利用率。
$ sudo apt install sysstat -y $ mpstat 1 Linux 4.4.0-47-generic (hostname) 12/03/2016 x86_64 (1 CPU)
10:16:20 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 10:16:21 PM all 0.00 0.00 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 10:16:22 PM all 0.00 0.00 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 10:16:23 PM all 0.00 0.00 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
...
kill cat /dev/urandom
...
10:17:00 PM all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 10:17:01 PM all 1.00 0.00 0.00 2.00 0.00 0.00 0.00 0.00 0.00 97.00 10:17:02 PM all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
那么我们为什么还要使用负载平均值呢?
In the top right corner, htop shows the total number of processes and how many of them are running. But it says Tasks not processes. Why?
Another name for a process is a task. The Linux kernel internally refers to processes as tasks. htop uses Tasks instead of Processes probably because it's shorter and saves some screen space.
You can also see threads in htop. To toggle the visibility of threads, hit Shift+H on your keyboard. If you see Tasks: 23, 10 thr, it means it they are visible.
You can also see kernel threads with Shift+K. When they are visible, it'll say Tasks: 23, 40 kthr.
在右上角,htop 显示进程总数和其中正在运行的进程数。 但它写的是 Tasks 而不是 Processes。为什么?
进程的另一个名称就是 task。Linux 内核内部将进程称为任务。 htop 使用 Tasks 而不是 Processes,可能是因为它更短,节省屏幕空间。
你也可以在 htop 中看到线程。按 Shift+H 切换线程显示。 如果显示 Tasks: 23, 10 thr,意味着线程可见。
按 Shift+K 还可以看到内核线程。当它们可见时,会显示 Tasks: 23, 40 kthr。
Every time a new process is started it is assigned an identification number (ID) which is called process ID or PID for short.
If you run a program in the background (&) from bash, you will see the job number in square brackets and the PID.
$ sleep 1000 & [1] 12503
If you missed it, you can use the $! variable in bash that will expand to the last backgrounded process ID.
$ echo $! 12503
Process ID is very useful. It can be used to see details about the process and to control it.
procfs is a pseudo file system that lets userland programs to get information from the kernel by reading files. It is usually mounted at /proc/ and to you it looks like a regular directory that you can browse with ls and cd.
All information related to a process is located at /proc/<pid>/.
$ ls /proc/12503 attr coredump_filter fdinfo maps ns personality smaps task auxv cpuset gid_map mem numa_maps projid_map stack uid_map cgroup cwd io mountinfo oom_adj root stat wchan clear_refs environ limits mounts oom_score schedstat statm cmdline exe loginuid mountstats oom_score_adj sessionid status comm fd map_files net pagemap setgroups syscall
For example, /proc/<pid>/cmdline will give the command that was used to launch the process.
$ cat /proc/12503/cmdline sleep1000$
Ugh, that's not right. It turns out that the command is separated by the \0 byte.
$ od -c /proc/12503/cmdline 0000000 s l e e p \0 1 0 0 0 \0 0000013
which we can replace with a space or newline
$ tr '\0' '\n' < /proc/12503/cmdline sleep 1000 $ strings /proc/12503/cmdline sleep 1000
The process directory for a process can contain links! For instance, cwd points to the current working directory and exe is the executed binary.
$ ls -l /proc/12503/{cwd,exe} lrwxrwxrwx 1 ubuntu ubuntu 0 Jul 6 10:10 /proc/12503/cwd -> /home/ubuntu lrwxrwxrwx 1 ubuntu ubuntu 0 Jul 6 10:10 /proc/12503/exe -> /bin/sleep
So this is how htop, top, ps and other diagnostic utilities get their information about the details of a process: they read it from /proc/<pid>/<file>.
每次启动新进程时,都会分配一个标识号(ID), 称为进程 ID,简称 PID。
如果你在 bash 中以后台方式(&)运行程序,会看到方括号中的任务编号和 PID。
$ sleep 1000 & [1] 12503
如果没看到,可以使用 bash 中的 $! 变量,它会展开为最后一个后台进程的 ID。
$ echo $! 12503
进程 ID 非常有用,可以用来查看进程详情并控制它。
procfs 是一个伪文件系统,允许用户态程序通过读取文件来从内核获取信息。 它通常挂载在 /proc/,看起来像一个可以用 ls 和 cd 浏览的普通目录。
所有与进程相关的信息都位于 /proc/<pid>/。
$ ls /proc/12503 attr coredump_filter fdinfo maps ns personality smaps task auxv cpuset gid_map mem numa_maps projid_map stack uid_map cgroup cwd io mountinfo oom_adj root stat wchan clear_refs environ limits mounts oom_score schedstat statm cmdline exe loginuid mountstats oom_score_adj sessionid status comm fd map_files net pagemap setgroups syscall
例如,/proc/<pid>/cmdline 会给出启动进程时使用的命令。
$ cat /proc/12503/cmdline sleep1000$
唔,不对。原来命令是用 \0 字节分隔的。
$ od -c /proc/12503/cmdline 0000000 s l e e p \0 1 0 0 0 \0 0000013
我们可以用空格或换行符替换:
$ tr '\0' '\n' < /proc/12503/cmdline sleep 1000 $ strings /proc/12503/cmdline sleep 1000
进程目录还可以包含链接! 例如,cwd 指向当前工作目录, exe 是已执行的二进制文件。
$ ls -l /proc/12503/{cwd,exe} lrwxrwxrwx 1 ubuntu ubuntu 0 Jul 6 10:10 /proc/12503/cwd -> /home/ubuntu lrwxrwxrwx 1 ubuntu ubuntu 0 Jul 6 10:10 /proc/12503/exe -> /bin/sleep
这就是 htop、top、ps 以及其他诊断工具获取进程详细信息的方式: 它们从 /proc/<pid>/<file> 中读取信息。
When you launch a new process, the process that launched the new process is called the parent process. The new process is now a child process for the parent process. These relationships form a tree structure.
If you hit F5 in htop, you can see the process hierarchy.
You can also use the f switch with ps
$ ps f PID TTY STAT TIME COMMAND 12472 pts/0 Ss 0:00 -bash 12684 pts/0 R+ 0:00 _ ps f
or pstree
$ pstree -a init ├─atd ├─cron ├─sshd -D │ └─sshd │ └─sshd │ └─bash │ └─pstree -a ...
If you have ever wondered why you often see bash or sshd as parents of some of your processes, here's why.
This is what happens when you run, say, date from your bash shell:
bash creates a new process that is a copy of itself (using a fork system call)
it will then load the program from the executable file /bin/date into memory (using an exec system call)
bash as the parent process will wait for its child to exit
So the /sbin/init with an ID of 1 was started at boot, which spawned the SSH daemon sshd. When you connect to the computer, sshd will spawn a process for the session which in turn will launch the bash shell.
I like to use this tree view in htop when I'm also interested in seeing all threads.
当你启动一个新进程时,那个启动新进程的进程称为父进程。新进程就是父进程的子进程。 这些关系形成了一棵树形结构。
如果按 F5,htop 会显示进程层次。
你也可以在 ps 中使用 f 选项:
$ ps f PID TTY STAT TIME COMMAND 12472 pts/0 Ss 0:00 -bash 12684 pts/0 R+ 0:00 _ ps f
或者使用 pstree:
$ pstree -a init ├─atd ├─cron ├─sshd -D │ └─sshd │ └─sshd │ └─bash │ └─pstree -a ...
如果你曾好奇为什么经常看到 bash 或 sshd 作为某些进程的父进程,原因如下。
当你从 bash shell 运行 date 命令时,会发生以下过程:
bash 创建一个自身副本的新进程(使用 fork 系统调用)
然后它会从可执行文件 /bin/date 中加载程序到内存(使用 exec 系统调用)
bash 作为父进程会等待其子进程退出
所以 PID 为 1 的 /sbin/init 在启动时启动,它产生了 SSH 守护进程 sshd。 当你连接到计算机时,sshd 会为会话产生一个进程,该进程又会启动 bash shell。
当我也想看所有线程时,我喜欢在 htop 中使用树形视图。
Each process is owned by a user. Users are represented with a numeric ID.
$ sleep 1000 & [1] 2045 $ grep Uid /proc/2045/status Uid: 1000 1000 1000 1000
You can use the id command to find out the name for this user.
$ id 1000 uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm)
It turns out that id gets this information from the /etc/passwd and /etc/group files.
$ strace -e open id 1000 ... open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3 open("/lib/x86_64-linux-gnu/libnss_compat.so.2", O_RDONLY|O_CLOEXEC) = 3 open("/lib/x86_64-linux-gnu/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3 open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3 open("/etc/group", O_RDONLY|O_CLOEXEC) = 3 ...
That's because the Name Service Switch (NSS) configuration file /etc/nsswitch.conf says to use these files to resolve names.
$ head -n 9 /etc/nsswitch.conf
...
passwd: compat group: compat shadow: compat
The value of compat (Compatibility mode) is the same as files except other special entries are permitted. files means that the database is stored in a file (loaded by libnss_files.so). But you could also store your users in other databases and services or use Lightweight Directory Access Protocol (LDAP), for example.
/etc/passwd and /etc/group are plain text files that map numeric IDs to human readable names.
$ cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash $ cat /etc/group root:x:0: adm:x:4:syslog,ubuntu ubuntu:x:1000:
passwd? But where are the passwords?
They are actually in /etc/shadow.
$ sudo cat /etc/shadow root:$6$mS9o0QBw$P1ojPSTexV2PQ.Z./rqzYex.k7TJE2nVeIVL0dql/:17126:0:99999:7::: daemon:*:17109:0:99999:7::: ubuntu:$6$GIfdqlb/$ms9ZoxfrUq455K6UbmHyOfz7DVf7TWaveyHcp.:17126:0:99999:7:::
What's that gibberish?
$6$ is the password hashing algorithm used, in this case it stands for sha512
followed by randomly generated salt to safeguard against rainbow table attacks
and finally the hash of your password + salt
When you run a program, it will be run as your user. Even if the executable file is not owned by you.
If you'd like to run a program as root or another user, that's what sudo is for.
$ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm) $ sudo id uid=0(root) gid=0(root) groups=0(root) $ sudo -u ubuntu id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm) $ sudo -u daemon id uid=1(daemon) gid=1(daemon) groups=1(daemon)
But what if you want to log in as another user to launch various commands? Use sudo bash or sudo -u user bash. You'll be able to use the shell as that user.
If you don't like being asked for the root password all the time, you can simply disable it by adding your user to the /etc/sudoers file.
Let's try it:
$ echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers -bash: /etc/sudoers: Permission denied
Right, only root can do it.
$ sudo echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers -bash: /etc/sudoers: Permission denied
WTF?
What happens here is that you are executing the echo command as root but appending the line to the /etc/sudoers file still as your user.
There are usually two ways around it:
echo "$USER ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
sudo bash -c "echo '$USER ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers"
In the first case, tee -a will append its standard input to the file and we execute this command as root.
In the second case, we run bash as root and ask it to execute a command (-c) and the entire command will be executed as root. Note the tricky "/' business here which will dictate when the $USER variable will be expanded.
If you take a look at the /etc/sudoers file you will see that it begins with
$ sudo head -n 3 /etc/sudoers
This file MUST be edited with the 'visudo' command as root.
Uh oh.
It's a helpful warning that says you should edit this file with sudo visudo. It will validate the contents of the file before saving and prevent you from making mistakes. If you don't use visudo and make a mistake, it may lock you out from sudo. Which means that you won't be able to correct your mistake!
Let's say you want to change your password. You can do it with the passwd command. It will, as we saw earlier, save the password to the /etc/shadow file.
This file is sensitive and only writeable by root:
$ ls -l /etc/shadow -rw-r----- 1 root shadow 1122 Nov 27 18:52 /etc/shadow
So how is it possible that the passwd program which is executed by a regular user can write to a protected file?
I said earlier that when you launch a process, it is owned by you, even if the owner of the executable file is another user.
It turns out that you can change that behavior by changing file permissions. Let's take a look.
$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 54256 Mar 29 2016 /usr/bin/passwd
Notice the s letter. It was accomplished with sudo chmod u+s /usr/bin/passwd. It means that an executable will be launched as the the owner of the file which is root in this case.
You can find the so called setuid executables with find /bin -user root -perm -u+s.
Note that you can also do the same with group (g+s).
每个进程都属于一个用户。用户用数字 ID 表示。
$ sleep 1000 & [1] 2045 $ grep Uid /proc/2045/status Uid: 1000 1000 1000 1000
你可以使用 id 命令查找该用户的名称。
$ id 1000 uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm)
原来 id 是从 /etc/passwd 和 /etc/group 文件中获取这些信息的。
$ strace -e open id 1000 ... open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3 open("/lib/x86_64-linux-gnu/libnss_compat.so.2", O_RDONLY|O_CLOEXEC) = 3 open("/lib/x86_64-linux-gnu/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3 open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3 open("/etc/group", O_RDONLY|O_CLOEXEC) = 3 ...
这是因为名称服务开关(NSS)配置文件 /etc/nsswitch.conf 指定使用这些文件来解析名称。
$ head -n 9 /etc/nsswitch.conf
...
passwd: compat group: compat shadow: compat
compat(兼容模式)的值与 files 相同,只是允许其他特殊条目。 files 意味着数据库存储在文件中(由 libnss_files.so 加载)。 但你也可以将用户存储在其他数据库和服务中,例如使用轻量级目录访问协议(LDAP)。
/etc/passwd 和 /etc/group 是纯文本文件,将数字 ID 映射为人类可读的名称。
$ cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash $ cat /etc/group root:x:0: adm:x:4:syslog,ubuntu ubuntu:x:1000:
passwd?但密码在哪里?
它们其实在 /etc/shadow 中。
$ sudo cat /etc/shadow root:$6$mS9o0QBw$P1ojPSTexV2PQ.Z./rqzYex.k7TJE2nVeIVL0dql/:17126:0:99999:7::: daemon:*:17109:0:99999:7::: ubuntu:$6$GIfdqlb/$ms9ZoxfrUq455K6UbmHyOfz7DVf7TWaveyHcp.:17126:0:99999:7:::
那串乱码是什么?
$6$ 是使用的密码哈希算法,这里代表 sha512
后面是随机生成的盐,用于防止彩虹表攻击
最后是你的密码加盐后的哈希值
当你运行程序时,它会以你的用户身份运行,即使可执行文件不属于你。
如果你想以 root 或其他用户身份运行程序,可以用 sudo。
$ id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm) $ sudo id uid=0(root) gid=0(root) groups=0(root) $ sudo -u ubuntu id uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm) $ sudo -u daemon id uid=1(daemon) gid=1(daemon) groups=1(daemon)
但如果你想登录为另一个用户来启动各种命令呢? 使用 sudo bash 或 sudo -u user bash。你就可以以该用户的身份使用 shell。
如果你不喜欢总被要求输入 root 密码,可以通过将你的用户添加到 /etc/sudoers 文件中来禁用它。
试试看:
$ echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers -bash: /etc/sudoers: Permission denied
对,只有 root 才能做。
$ sudo echo "$USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers -bash: /etc/sudoers: Permission denied
怎么回事?
这里发生的是:你以 root 身份执行了 echo 命令,但将输出追加到 /etc/sudoers 文件的操作仍然以你的用户身份进行。
通常有两种解决办法:
echo "$USER ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
sudo bash -c "echo '$USER ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers"
第一种情况,tee -a 会将标准输入追加到文件,并且我们以 root 身份执行此命令。
第二种情况,我们以 root 身份运行 bash,让它执行一个命令(-c),整个命令会以 root 身份执行。 注意这里引号的使用技巧,它决定了 $USER 变量的展开时机。
如果你查看 /etc/sudoers 文件,会发现它以
$ sudo head -n 3 /etc/sudoers
This file MUST be edited with the 'visudo' command as root.
开头。
哦哦。
这是一个有用的警告,说明你应该用 sudo visudo 编辑此文件。 它会在保存前验证文件内容,防止你犯错。 如果你不使用 visudo 而犯了一个错误,可能会把自己锁在 sudo 之外。 这意味着你将无法纠正错误!
假设你想更改密码。可以用 passwd 命令。 就像我们之前看到的,它会将密码保存到 /etc/shadow 文件。
这个文件很敏感,只有 root 可写:
$ ls -l /etc/shadow -rw-r----- 1 root shadow 1122 Nov 27 18:52 /etc/shadow
那么,由普通用户执行的 passwd 程序怎么可能写入受保护的文件呢?
我之前说过,当你启动进程时,它是由你拥有的,即使可执行文件的所有者是另一个用户。
但你可以通过更改文件权限来改变这一行为。我们来看看。
$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 54256 Mar 29 2016 /usr/bin/passwd
注意那个 s 字母。它是通过 sudo chmod u+s /usr/bin/passwd 实现的。 这意味着可执行文件将以文件所有者(这里是 root)的身份启动。
你可以用 find /bin -user root -perm -u+s 来找到这些所谓的 setuid 可执行文件。
注意,你还可以对组(g+s)做同样的事情。
We are next going to look at the process state column in htop which is denoted simply with the letter S.
Here are the possible values:
R running or runnable (on run queue) S interruptible sleep (waiting for an event to complete) D uninterruptible sleep (usually IO) Z defunct ("zombie") process, terminated but not reaped by its parent T stopped by job control signal t stopped by debugger during the tracing X dead (should never be seen)
I've ordered them by how often I see them.
Note that when you run ps, it will also show substates like Ss, R+, Ss+, etc.
$ ps x PID TTY STAT TIME COMMAND 1688 ? Ss 0:00 /lib/systemd/systemd --user 1689 ? S 0:00 (sd-pam) 1724 ? S 0:01 sshd: vagrant@pts/0 1725 pts/0 Ss 0:00 -bash 2628 pts/0 R+ 0:00 ps x
接下来我们看 htop 中的进程状态列,它简单地用字母 S 表示。
可能的取值如下:
R running 或 runnable(在运行队列中) S interruptible sleep(等待某个事件完成) D uninterruptible sleep(通常是 IO) Z defunct("僵尸")进程,已终止但未被父进程回收 t 被调试器在跟踪期间停止 X dead(不应看到)
我是按它们的出现频率排序的。
注意,当你运行 ps 时,它还会显示子状态,如 Ss、R+、Ss+ 等。
$ ps x PID TTY STAT TIME COMMAND 1688 ? Ss 0:00 /lib/systemd/systemd --user 1689 ? S 0:00 (sd-pam) 1724 ? S 0:01 sshd: vagrant@pts/0 1725 pts/0 Ss 0:00 -bash 2628 pts/0 R+ 0:00 ps x
R - running or runnable (on run queue)
In this state, the process is currently running or on a run queue waiting to run.
What does it mean to run?
When you compile the source code of a program that you've written, that machine code is CPU instructions. It is saved to a file that can be executed. When you launch a program, it is loaded into memory and then the CPU executes these instructions.
Basically it means that the CPU is physically executing instructions. Or, in other words, crunching numbers.
R - running 或 runnable(在运行队列中)
在这种状态下,进程当前正在运行或位于运行队列中等待运行。
"运行"是什么意思?
当你编译自己编写的程序的源代码时, 那些机器码就是 CPU 指令。它们被保存到一个可执行文件中。 当你启动程序时,它被加载到内存中,然后 CPU 执行这些指令。
基本上意味着 CPU 正在物理地执行指令。或者换句话说,正在处理数据。
S - interruptible sleep (waiting for an event to complete)
This means that the code instructions of this process are not being executed on the CPU. Instead, this process is waiting for something - an event or a condition - to happen. When an event happens, the kernel sets the state to running.
One example is the sleep utiliy from coreutils. It will sleep for a specific number of seconds (approximately).
$ sleep 1000 & [1] 10089 $ ps f PID TTY STAT TIME COMMAND 3514 pts/1 Ss 0:00 -bash 10089 pts/1 S 0:00 _ sleep 1000 10094 pts/1 R+ 0:00 _ ps f
So this is interruptible sleep. How can we interrupt it?
By sending a signal.
You can send a signal in htop by hitting F9 and then choosing one of the signals in the menu on the left.
Sending a signal is also known as kill. That's because kill is a system call that can send a signal to a process. There is a program /bin/kill that can make this system call from userland and the default signal to use is TERM which will ask the process to terminate or in other words try to kill it.
Signal is just a number. Numbers are hard to remember so we give them names. Signal names are usually written in uppercase and may be prefixed with SIG.
Some commonly used signals are INT, KILL, STOP, CONT, HUP.
Let's interrupt the sleep process by sending the INT aka SIGINT aka 2 aka Terminal interrupt signal.
$ kill -INT 10089 [1]+ Interrupt sleep 1000
This is also what happens When you hit CTRL+C on your keyboard. bash will the send the foreground process the SIGINT signal just like we just did manually.
By the way, in bash, kill is a built-in command, even though there is /bin/kill on most systems. Why? It allows processes to be killed if the limit on processes that you can create is reached.
These commands do the same thing:
kill -INT 10089
kill -2 10089
/bin/kill -2 10089
Another useful signal to know is SIGKILL aka 9. You may have used it to kill a process that didn't respond to your frantic CTRL+C keyboard presses.
When you write a program, you can set up signal handlers that are functions that will be called when your process receives a signal. In other words, you can catch the signal and then do something, for example, clean up and shut down gracefully. So sending SIGINT (the user wants to interrupt a process) and SIGTERM (the user wants to terminate the process) does not mean that the process will be terminated.
You may have seen this exception when running Python scripts:
$ python -c 'import sys; sys.stdin.read()' ^C Traceback (most recent call last): File "<string>", line 1, in <module> KeyboardInterrupt
You can tell the kernel to forcefully terminate a process and not give it a change to respond by sending the KILL signal:
$ sleep 1000 & [1] 2658 $ kill -9 2658 [1]+ Killed sleep 1000
S - interruptible sleep(等待某个事件完成)
这意味着该进程的代码指令没有在 CPU 上执行。 相反,该进程正在等待某件事——一个事件或条件——发生。 当事件发生时,内核将状态设置为 running。
一个例子是 coreutils 中的 sleep 工具。 它会休眠特定的秒数(大约)。
$ sleep 1000 & [1] 10089 $ ps f PID TTY STAT TIME COMMAND 3514 pts/1 Ss 0:00 -bash 10089 pts/1 S 0:00 _ sleep 1000 10094 pts/1 R+ 0:00 _ ps f
所以这是可中断休眠。我们如何中断它?
通过发送信号。
在 htop 中,你可以按 F9,然后从左侧菜单中选择一个信号来发送。
发送信号也称为 kill。这是因为 kill 是一个可以向进程发送信号的系统调用。 有一个程序 /bin/kill 可以从用户态进行这个系统调用,默认发送的信号是 TERM, 它会请求进程终止,或者说试图杀死它。
信号只是一个数字。数字很难记忆,所以我们给它们起名字。 信号名称通常大写,并且可能带有 SIG 前缀。
一些常用的信号有 INT、KILL、STOP、CONT、HUP。
让我们通过发送 INT(即 SIGINT,也称为 2,终端中断信号)来中断 sleep 进程。
$ kill -INT 10089 [1]+ Interrupt sleep 1000
这和你按 CTRL+C 时发生的情况是一样的。bash 会向前台进程发送 SIGINT 信号,就像我们刚才手动做的那样。
顺便一提,在 bash 中,kill 是一个内建命令,尽管大多数系统也有 /bin/kill。 为什么?因为它允许在你达到进程创建限制时仍然能够杀死进程。
以下命令等效:
kill -INT 10089
kill -2 10089
/bin/kill -2 10089
另一个有用的信号是 SIGKILL,也称为 9。你可能用它来杀死那些对你狂按 CTRL+C 无动于衷的进程。
当你编写程序时,可以设置信号处理函数,当进程收到信号时会调用这些函数。 换句话说,你可以捕获信号,然后做一些事情,比如优雅地清理并关闭。 所以发送 SIGINT(用户想中断进程)和 SIGTERM(用户想终止进程)并不意味着进程一定会被终止。
你可能在运行 Python 脚本时看到过这个异常:
$ python -c 'import sys; sys.stdin.read()' ^C Traceback (most recent call last): File "<string>", line 1, in <module> KeyboardInterrupt
你可以通过发送 KILL 信号来强制内核终止进程,而不给它响应的机会:
$ sleep 1000 & [1] 2658 $ kill -9 2658 [1]+ Killed sleep 1000
D - uninterruptible sleep (usually IO)
Unlike interruptible sleep, you cannot wake up this process with a signal. That is why many people dread seeing this state. You can't kill such processes because killing means sending SIGKILL signals to processes.
This state is used if the process must wait without interruption or when the event is expected to occur quickly. Like reading to/from a disk. But that should only happen for a fraction of a second.
Here is a nice answer on StackOverflow.
Uninterruptable processes are USUALLY waiting for I/O following a page fault. The process/task cannot be interrupted in this state, because it can't handle any signals; if it did, another page fault would happen and it would be back where it was.
In other words, this could happen if you are using Network File System (NFS) and it takes a while to read and write from it.
Or in my experience it can also mean that some of the processes are swapping a lot which means you have too little available memory.
Let's try to get a process to go into uninterruptible sleep.
8.8.8.8 is a public DNS server provided by Google. They do not have an open NFS on there. But that won't stop us.
$ sudo mount 8.8.8.8:/tmp /tmp & [1] 12646 $ sudo ps x | grep mount.nfs 12648 pts/1 D 0:00 /sbin/mount.nfs 8.8.8.8:/tmp /tmp -o rw
How to find out what's causing this? strace!
Let's strace the command in the output of ps above.
$ sudo strace /sbin/mount.nfs 8.8.8.8:/tmp /tmp -o rw ... mount("8.8.8.8:/tmp", "/tmp", "nfs", 0, ...
So the mount system call is blocking the process.
If you're wondering, you can run mount with an intr option to run as interruptible: sudo mount 8.8.8.8:/tmp /tmp -o intr.
D - uninterruptible sleep(通常是 IO)
与可中断休眠不同,你无法通过信号唤醒此状态的进程。 这就是为什么许多人害怕看到这个状态。 你无法杀死这类进程,因为杀死意味着向进程发送 SIGKILL 信号。
当进程必须无中断地等待,或者预期事件会很快发生时,会使用此状态。 比如从磁盘读取/写入。 但这应该只持续几分之一秒。
StackOverflow 上有一个很好的回答:
不可中断进程通常是在页面错误后等待 I/O。 处于此状态的进程/任务不能被中断,因为它无法处理任何信号; 如果处理了,就会发生另一个页面错误,回到原来的状态。
换句话说,如果你使用网络文件系统(NFS), 并且读写需要一段时间,就可能出现这种情况。
或者根据我的经验,这也可能意味着某些进程正在进行大量的交换(swap), 说明可用内存太少。
让我们尝试让一个进程进入不可中断休眠。
8.8.8.8 是 Google 提供的公共 DNS 服务器。 那里没有开放 NFS。 但这难不倒我们。
$ sudo mount 8.8.8.8:/tmp /tmp & [1] 12646 $ sudo ps x | grep mount.nfs 12648 pts/1 D 0:00 /sbin/mount.nfs 8.8.8.8:/tmp /tmp -o rw
如何找出原因?用 strace!
我们用 strace 跟踪上面 ps 输出中的命令。
$ sudo strace /sbin/mount.nfs 8.8.8.8:/tmp /tmp -o rw ... mount("8.8.8.8:/tmp", "/tmp", "nfs", 0, ...
所以 mount 系统调用阻塞了进程。
如果你想知道,可以带 intr 选项运行 mount,使其变为可中断: sudo mount 8.8.8.8:/tmp /tmp -o intr。
Z - defunct ("zombie") process, terminated but not reaped by its parent
When a process ends via exit and it still has child processes, the child processes become zombie processes.
If zombie processes exist for a short time, it is perfectly normal
Zombie processes that exist for a long time may indicate a bug in a program
Zombie processes don't consume memory, just a process ID
You can't kill a zombie process
You can ask nicely the parent process to reap the zombies (the SIGCHLD signal)
You can kill the zombie's parent process to get rid of the parent and its zombies
I am going to write some C code to show this.
Here is our program.
#include <stdio.h> #include <stdlib.h> #include <unistd.h>
int main() { printf("Running\n");
int pid = fork();
if (pid == 0) { printf("I am the child process\n"); printf("The child process is exiting now\n"); exit(0); } else { printf("I am the parent process\n"); printf("The parent process is sleeping now\n"); sleep(20); printf("The parent process is finished\n"); }
return 0; }
Let's install the GNU C Compiler (GCC).
sudo apt install -y gcc
Compile it and then run it
gcc zombie.c -o zombie ./zombie
Look at the process tree
$ ps f PID TTY STAT TIME COMMAND 3514 pts/1 Ss 0:00 -bash 7911 pts/1 S+ 0:00 _ ./zombie 7912 pts/1 Z+ 0:00 _ [zombie] <defunct> 1317 pts/0 Ss 0:00 -bash 7913 pts/0 R+ 0:00 _ ps f
We got our zombie!
When the parent process is done, the zombie is gone.
$ ps f PID TTY STAT TIME COMMAND 3514 pts/1 Ss+ 0:00 -bash 1317 pts/0 Ss 0:00 -bash 7914 pts/0 R+ 0:00 _ ps f
If you replaced sleep(20) with while (true) ; then the zombie would be gone right away.
With exit, all of the memory and resources associated with it are deallocated so they can be used by other processes.
Why keep the zombie processes around then?
The parent process has the option to find out its child process exit code (in a signal handler) with the wait system call. If a process is sleeping, then it needs to wait for it to wake up.
Why not simply forcefully wake it up and kill it? For the same reason, you don't toss your child in the trash when you're tired of it. Bad things could happen.
Z - defunct("僵尸")进程,已终止但未被父进程回收
当一个进程通过 exit 结束时,如果它还有子进程, 这些子进程就会变成僵尸进程。
如果僵尸进程存在时间很短,这完全正常。
长期存在的僵尸进程可能表示程序有 bug。
僵尸进程不消耗内存,只占用一个进程 ID。
你无法杀死僵尸进程。
你可以礼貌地请求父进程回收僵尸(通过 SIGCHLD 信号)。
你可以杀死僵尸的父进程,从而同时消除父进程及其僵尸。
我打算写一些 C 代码来演示这一点。
这是我们的程序。
#include <stdio.h> #include <stdlib.h> #include <unistd.h>
int main() { printf("Running\n");
int pid = fork();
if (pid == 0) { printf("I am the child process\n"); printf("The child process is exiting now\n"); exit(0); } else { printf("I am the parent process\n"); printf("The parent process is sleeping now\n"); sleep(20); printf("The parent process is finished\n"); }
return 0; }
先安装 GNU C 编译器(GCC)。
sudo apt install -y gcc
编译并运行
gcc zombie.c -o zombie ./zombie
查看进程树
$ ps f PID TTY STAT TIME COMMAND 3514 pts/1 Ss 0:00 -bash 7911 pts/1 S+ 0:00 _ ./zombie 7912 pts/1 Z+ 0:00 _ [zombie] <defunct> 1317 pts/0 Ss 0:00 -bash 7913 pts/0 R+ 0:00 _ ps f
我们得到了一个僵尸!
当父进程结束时,僵尸就消失了。
$ ps f PID TTY STAT TIME COMMAND 3514 pts/1 Ss+ 0:00 -bash 1317 pts/0 Ss 0:00 -bash 7914 pts/0 R+ 0:00 _ ps f
如果你把 sleep(20) 替换为 while (true) ;,那么僵尸会立即消失。
当进程 exit 时,所有与之关联的内存和资源都会被释放,以便其他进程使用。
那为什么还要保留僵尸进程呢?
父进程可以选择(在信号处理函数中)通过 wait 系统调用获知其子进程的退出码。 如果进程在休眠,就需要等待它唤醒。
为什么不直接强行唤醒并杀死它? 就像你不会因为厌倦了孩子就把孩子扔进垃圾桶一样。 可能会发生不好的事情。
T - stopped by job control signal
I have opened two terminal windows and I can look at my user's processes with ps u.
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 1317 0.0 0.9 21420 4992 pts/0 Ss+ Jun07 0:00 -bash ubuntu 3514 1.5 1.0 21420 5196 pts/1 Ss 07:28 0:00 -bash ubuntu 3528 0.0 0.6 36084 3316 pts/1 R+ 07:28 0:00 ps u
I will omit the -bash and ps u processes from the output below.
Now run cat /dev/urandom > /dev/null in one terminal window. Its state is R+ which means that it is running.
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 3540 103 0.1 6168 688 pts/1 R+ 07:29 0:04 cat /dev/urandom
Press CTRL+Z to stop the process.
$ # CTRL+Z [1]+ Stopped cat /dev/urandom > /dev/null $ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 3540 86.8 0.1 6168 688 pts/1 T 07:29 0:15 cat /dev/urandom
Its state is now T.
Run fg in the first terminal to resume it.
Another way to stop a process like this is to send the STOP signal with kill to the process. To resume the execution of the process, you can use the CONT signal.
t - stopped by debugger during the tracing
First, install the GNU Debugger (gdb)
sudo apt install -y gdb
Run a program that will listen for incoming network connections on port 1234.
$ nc -l 1234 & [1] 3905
It is sleeping meaning it is waiting for data from the network.
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 3905 0.0 0.1 9184 896 pts/0 S 07:41 0:00 nc -l 1234
Run the debugger and attach it to the process with ID 3905.
sudo gdb -p 3905
You will see that the state is t which means that this process is being traced in the debugger.
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 3905 0.0 0.1 9184 896 pts/0 t 07:41 0:00 nc -l 1234
T - 被作业控制信号停止
我打开了两个终端窗口,可以用 ps u 查看我的用户进程。
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 1317 0.0 0.9 21420 4992 pts/0 Ss+ Jun07 0:00 -bash ubuntu 3514 1.5 1.0 21420 5196 pts/1 Ss 07:28 0:00 -bash ubuntu 3528 0.0 0.6 36084 3316 pts/1 R+ 07:28 0:00 ps u
下面我将从输出中省略 -bash 和 ps u 进程。
现在在一个终端窗口中运行 cat /dev/urandom > /dev/null。 它的状态是 R+,表示正在运行。
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 3540 103 0.1 6168 688 pts/1 R+ 07:29 0:04 cat /dev/urandom
按 CTRL+Z 停止该进程。
$ # CTRL+Z [1]+ Stopped cat /dev/urandom > /dev/null $ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 3540 86.8 0.1 6168 688 pts/1 T 07:29 0:15 cat /dev/urandom
它的状态现在是 T。
在第一个终端中运行 fg 以恢复它。
另一种停止此类进程的方法是向进程发送 STOP 信号。 要恢复进程执行,可以使用 CONT 信号。
t - 被调试器在跟踪期间停止
首先,安装 GNU 调试器(gdb)
sudo apt install -y gdb
运行一个程序,它将监听端口 1234 上的传入网络连接。
$ nc -l 1234 & [1] 3905
它正在休眠,意味着它在等待网络数据。
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 3905 0.0 0.1 9184 896 pts/0 S 07:41 0:00 nc -l 1234
运行调试器并将其附加到 ID 为 3905 的进程。
sudo gdb -p 3905
你会看到状态变为 t,表示该进程正在被调试器跟踪。
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ubuntu 3905 0.0 0.1 9184 896 pts/0 t 07:41 0:00 nc -l 1234
Linux is a multitasking operating system which means that even when you have a single CPU, you can run several processes at the same time. You can connect to your server via SSH and look at the output of htop while your web server is delivering the content of your blog to your readers over the internet.
How is that possible when a single CPU can only execute one instruction at a time?
The answer is time sharing.
One process runs for a bit of time, then it is suspended while the other processes waiting to run take turns running for a while. The bit of time a process runs is called the time slice.
The time slice is usually a few milliseconds so you don't really notice it that much when your system is not under high load. (It'd be really interesting to find out how long time slices usually are in Linux.)
This should help explain why the load average is the average number of running processes. If you have just one core and the load average is 1.0, the CPU has been utilized at 100%. If the load average is higher than 1.0, it means that the number of processes wanting to run is higher than the CPU can run so you may experience slow downs or delays. If the load is lower than 1.0, it means the CPU is sometimes idleing and not doing anything.
This should also give you a clue why sometimes the running time of a process that's been running for 10 seconds is higher or lower than exactly 10 seconds.
Linux 是一个多任务操作系统,这意味着即使只有一个 CPU, 你也能同时运行多个进程。 你可以通过 SSH 连接到服务器查看 htop 的输出, 同时你的 web 服务器正在通过互联网向读者提供博客内容。
单核 CPU 一次只能执行一条指令,这是怎么做到的呢?
答案是时间共享(time sharing)。
一个进程运行一小段时间,然后被挂起,其他等待运行的进程轮流运行一段时间。进程运行的这一小段时间称为时间片(time slice)。
时间片通常只有几毫秒,所以当系统负载不高时,你几乎察觉不到。 (了解 Linux 中时间片通常有多长会非常有趣。)
这有助于解释为什么负载平均值是正在运行的进程的平均数量。 如果你只有一个核心,负载平均为 1.0,说明 CPU 利用率是 100%。 如果负载平均高于 1.0,意味着想要运行的进程数量超过了 CPU 的处理能力,你可能会遇到速度变慢或延迟。 如果负载低于 1.0,说明 CPU 有时处于空闲状态,没有做任何事情。
这也应该能给你一些提示,为什么一个已经运行了 10 秒的进程,其运行时间有时会略高于或略低于正好 10 秒。
When you have more tasks to run than the number of available CPU cores, you somehow have to decide which tasks to run next and which ones to keep waiting. This is what the task scheduler is responsible for.
The scheduler in the Linux kernel is reponsible for choosing which process on a run queue to pick next and it depends on the scheduler algorithm used in the kernel.
You can't generally influence the scheduler but you can let it know which processes are more important to you and the scheduler may take it into account.
Niceness (NI) is user-space priority to processes, ranging from -20 which is the highest priority to 19 which is the lowest priority. It can be confusing but you can think that a nice process yields to a less nice process. So the nicer a process is, the more it yields.
From what I've pieced together by reading StackOverflow and other sites, a niceness level increase by 1 should yield a 10% more CPU time to the process.
The priority (PRI) is the kernel-space priority that the Linux kernel is using. Priorities range from 0 to 139 and the range from 0 to 99 is real time and 100 to 139 for users.
You can change the nicesness and the kernel takes it into account but you cannot change the priority.
The relation between the nice value and priority is:
PR = 20 + NI
so the value of PR = 20 + (-20 to +19) is 0 to 39 that maps 100 to 139.
You can set the niceness of a process before launching it.
nice -n niceness program
Change the nicencess when a program is already running with renice.
renice -n niceness -p PID
Here is what the CPU usage colors mean:
Blue: Low priority threads (nice > 0)
Green: Normal priority threads
Red: Kernel threads
http://askubuntu.com/questions/656771/process-niceness-vs-priority
当你需要运行的任务数超过了可用 CPU 核心数时, 必须决定先运行哪些任务,让哪些任务继续等待。 这正是任务调度器的职责。
Linux 内核中的调度器负责选择运行队列中的下一个进程, 这取决于内核使用的调度算法。
你通常无法直接影响调度器,但可以让它知道哪些进程对你更重要, 调度器可能会考虑这一点。
Nice 值(NI)是进程的用户空间优先级, 范围从 -20(最高优先级)到 19(最低优先级)。 这可能让人困惑,但你可以理解为:一个进程越 nice 就越礼让。 所以进程越 nice,它就礼让越多。
根据我在 StackOverflow 和其他网站上拼凑的信息, nice 级别每增加 1,应该会给进程多让出 10% 的 CPU 时间。
优先级(PRI)是 Linux 内核使用的内核空间优先级。 优先级范围从 0 到 139,其中 0 到 99 是实时优先级,100 到 139 用于用户进程。
你可以更改 nice 值,内核会考虑它,\n但你不能直接更改优先级。
nice 值与优先级之间的关系是:
PR = 20 + NI
所以 PR = 20 + (-20 到 +19) 得到 0 到 39,映射到 100 到 139。
你可以在启动进程前设置其 nice 值。
nice -n nice值 程序
用 renice 更改正在运行的程序的 nice 值。
renice -n nice值 -p PID
以下是 CPU 使用率的颜色含义:
蓝色:低优先级线程(nice > 0)
绿色:正常优先级线程
红色:内核线程
A process has the illusion of being the only one in memory. This is accomplished by using virtual memory.
A process does not have direct access to the physical memory. Instead, it has its own virtual address space and the kernel translates the virtual memory addresses to physical memory or can map some of it to disk. This is why it can look like processes use more memory than you have installed on your computer.
The point I want to make here is that it is not very straightforward to figure out how much memory a process takes up. Do you also want to count the shared libraries or disk mapped memory? But the kernel provides and htop shows some information that can help you estimate memory usage.
Here is what the memory usage colors mean:
Green: Used memory
Blue: Buffers
Orange: Cache
VIRT/VSZ - Virtual Image
The total amount of virtual memory used by the task. It includes all code, data and shared libraries plus pages that have been swapped out and pages that have been mapped but not used.
VIRT is virtual memory usage. It includes everything, including memory mapped files.
If an application requests 1 GB of memory but uses only 1 MB, then VIRT will report 1 GB. If it mmaps a 1 GB file and never uses it, VIRT will also report 1 GB.
Most of the time, this is not a useful number.
The non-swapped physical memory a task has used.
RES is resident memory usage i.e. what's currently in the physical memory.
While RES can be a better indicator of how much memory a process is using than VIRT, keep in mind that
this does not include the swapped out memory
some of the memory may be shared with other processes
If a process uses 1 GB of memory and it calls fork(), the result of forking will be two processes whose RES is both 1 GB but only 1 GB will actually be used since Linux uses copy-on-write.
SHR - Shared Mem size
The amount of shared memory used by a task. It simply reflects memory that could be potentially shared with other processes.
MEM% - Memory usage
A task's currently used share of available physical memory.
This is RES divided by the total RAM you have.
If RES is 400M and you have 8 gigabytes of RAM, MEM% will be 400/8192*100 = 4.88%.
进程会错觉自己是内存中的唯一存在。这是通过使用虚拟内存(virtual memory)实现的。
进程不能直接访问物理内存。相反,它有自己的虚拟地址空间, 内核将虚拟内存地址转换为物理内存,或者可以将部分映射到磁盘。 这就是为什么看起来进程使用的内存可能比你计算机上安装的内存还多。
这里我想说的是,要弄清楚一个进程占用了多少内存并不直截了当。 你是否还需要计算共享库或磁盘映射内存? 但内核提供了(并且 htop 显示了)一些可以帮助你估算内存使用情况的信息。
以下是内存使用颜色的含义:
绿色:已使用内存
蓝色:缓冲区(Buffers)
橙色:缓存(Cache)
VIRT/VSZ - 虚拟镜像
任务使用的虚拟内存总量。它 包括所有代码、数据和共享库,以及已换出的页面和已映射但未使用的页面。
VIRT 是虚拟内存使用量。 它包括一切,包括内存映射文件。
如果一个应用请求 1 GB 内存但只使用了 1 MB,那么 VIRT 会报告 1 GB。 如果它 mmap 一个 1 GB 文件但从未使用,VIRT 也会报告 1 GB。
大多数情况下,这不是一个有用的数字。
RES/RSS - 常驻内存大小
一个任务使用的非交换物理内存。
RES 是常驻内存使用量,即当前在物理内存中的内容。
虽然 RES 比 VIRT 更能指示进程使用了多少内存, 但请记住:
这不包括换出的内存
部分内存可能与其他进程共享
如果一个进程使用了 1 GB 内存并调用了 fork(), fork 的结果将是两个进程,它们的 RES 都是 1 GB, 但实际上只用了 1 GB,因为 Linux 使用了写时复制(copy-on-write)。
SHR - 共享内存大小
一个任务使用的共享内存量。 它单纯反映了可能与其他进程共享的内存。
MEM% - 内存使用率
一个任务当前占用的可用物理内存份额。
这是 RES 除以你的总 RAM。
如果 RES 是 400M,你有 8 GB RAM,那么 MEM% 就是 400/8192*100 = 4.88%。
Let's take a look at the process list in the htop screenshot.
Do you actually need them?
Here are my research notes on the processes that are run at startup on a fresh Digital Ocean droplet with Ubuntu Server 16.04.1 LTS x64.
我们来看看 htop 截图中的进程列表。
你真的需要它们吗?
以下是我对全新 Digital Ocean 实例(Ubuntu Server 16.04.1 LTS x64)上启动时运行的进程所做的研究笔记。
/sbin/init
The /sbin/init program (also called init) coordinates the rest of the boot process and configures the environment for the user.
When the init command starts, it becomes the parent or grandparent of all of the processes that start up automatically on the system.
Is it systemd?
$ dpkg -S /sbin/init systemd-sysv: /sbin/init
Yes, it is.
What happens if you kill it?
Nothing.
/sbin/init
/sbin/init 程序(也称为 init)协调后续的启动过程,并为用户配置环境。
当 init 命令启动时,它成为系统上自动启动的所有进程的父进程或祖先进程。
它是 systemd 吗?
$ dpkg -S /sbin/init systemd-sysv: /sbin/init
是的。
如果杀死它会怎样?
什么也不会发生。
/lib/systemd/systemd-journald
systemd-journald is a system service that collects and stores logging data. It creates and maintains structured, indexed journals based on logging information that is received from a variety of sources.
In other words:
One of the main changes in journald was to replace simple plain text log files with a special file format optimized for log messages. This file format allows system administrators to access relevant messages more efficiently. It also brings some of the power of database-driven centralized logging implementations to individual systems.
You are supposed to use the journalctl command to query log files.
journalctl _COMM=sshd logs by sshd
journalctl _COMM=sshd -o json-pretty logs by sshd in JSON
journalctl --since "2015-01-10" --until "2015-01-11 03:00"
journalctl --since 09:00 --until "1 hour ago"
journalctl --since yesterday
journalctl -b logs since boot
journalctl -f to follow logs
journalctl --disk-usage
journalctl --vacuum-size=1G
Pretty cool.
It looks like it is not possible to remove or disable this service, you can only turn off logging.
/lib/systemd/systemd-journald
systemd-journald 是一个收集和存储日志数据的系统服务。它根据从各种来源收到的日志信息,创建并维护结构化的、带索引的日志。
换句话说:
journald 的主要变化之一是用一种针对日志消息优化的特殊文件格式替换了简单的纯文本日志文件。这种文件格式允许系统管理员更高效地访问相关消息。它还将数据库驱动的集中式日志记录的一些功能带到了单个系统上。
你应该使用 journalctl 命令来查询日志文件。
journalctl _COMM=sshd 按 sshd 过滤日志
journalctl _COMM=sshd -o json-pretty 以 JSON 格式显示 sshd 日志
journalctl --since "2015-01-10" --until "2015-01-11 03:00"
journalctl --since 09:00 --until "1 hour ago"
journalctl --since yesterday
journalctl -b 启动以来的日志
journalctl -f 跟踪日志
journalctl --disk-usage
journalctl --vacuum-size=1G
很酷。
看起来无法删除或禁用此服务,你只能关闭日志记录。
/sbin/lvmetad -f
The lvmetad daemon caches LVM metadata, so that LVM commands can read metadata without scanning disks.
Metadata caching can be an advantage because scanning disks is time consuming and may interfere with the normal work of the system and disks.
But what is LVM (Logical Volume Management)?
You can think of LVM as "dynamic partitions", meaning that you can create/resize/delete LVM "partitions" (they're called "Logical Volumes" in LVM-speak) from the command line while your Linux system is running: no need to reboot the system to make the kernel aware of the newly-created or resized partitions.
It sounds like you should keep it if you are using LVM.
$ lvscan $ sudo apt remove lvm2 -y --purge
/sbin/lvmetad -f
lvmetad 守护进程缓存 LVM 元数据,这样 LVM 命令可以在不扫描磁盘的情况下读取元数据。
元数据缓存是一个优势,因为扫描磁盘很费时,并可能干扰系统和磁盘的正常工作。
但什么是 LVM(逻辑卷管理)?
你可以把 LVM 想象成"动态分区",意思是当你的 Linux 系统正在运行时,可以从命令行创建/调整大小/删除 LVM "分区"(LVM 术语中称为"逻辑卷"):无需重启系统即可让内核识别新创建或调整了大小的分区。
如果你使用了 LVM,应该保留它。
$ lvscan $ sudo apt remove lvm2 -y --purge
/lib/systemd/udevd
systemd-udevd listens to kernel uevents. For every event, systemd-udevd executes matching instructions specified in udev rules.
udev is a device manager for the Linux kernel. As the successor of devfsd and hotplug, udev primarily manages device nodes in the /dev directory.
So this service manages /dev.
I am not sure if I need it running on a virtual server.
/lib/systemd/timesyncd
systemd-timesyncd is a system service that may be used to synchronize the local system clock with a remote Network Time Protocol server.
So this replaces ntpd.
$ timedatectl status Local time: Fri 2016-08-26 11:38:21 UTC Universal time: Fri 2016-08-26 11:38:21 UTC RTC time: Fri 2016-08-26 11:38:20 Time zone: Etc/UTC (UTC, +0000) Network time on: yes NTP synchronized: yes RTC in local TZ: no
/usr/sbin/atd -f
atd - run jobs queued for later execution. atd runs jobs queued by at.
at and batch read commands from standard input or a specified file which are to be executed at a later time
Unlike cron, which schedules jobs that are repeated periodically, at runs a job at a specific time once.
$ echo "touch /tmp/yolo.txt" | at now + 1 minute job 1 at Fri Aug 26 10:44:00 2016 $ atq 1 Fri Aug 26 10:44:00 2016 a root $ sleep 60 && ls /tmp/yolo.txt /tmp/yolo.txt
I've actually never used it until now.
sudo apt remove at -y --purge
/usr/lib/snapd/snapd
Snappy Ubuntu Core is a new rendition of Ubuntu with transactional updates - a minimal server image with the same libraries as today's Ubuntu, but applications are provided through a simpler mechanism.
What?
Developers from multiple Linux distributions and companies today announced collaboration on the "snap" universal Linux package format, enabling a single binary package to work perfectly and securely on any Linux desktop, server, cloud or device.
Apparently it is a simplified deb package and you're supposted to bundle all dependencies in a single snap that you can distribute.
I've never used snappy to deploy or distribute applications on servers.
sudo apt remove snapd -y --purge
/lib/systemd/udevd
systemd-udevd 监听内核 uevents。对于每个事件,systemd-udevd 执行 udev 规则中指定的匹配指令。
udev 是 Linux 内核的设备管理器。作为 devfsd 和 hotplug 的继任者,udev 主要管理 /dev 目录下的设备节点。
所以这个服务管理着 /dev。
我不确定在虚拟服务器上是否需要它运行。
/lib/systemd/timesyncd
systemd-timesyncd 是一个系统服务,用于将本地系统时钟与远程网络时间协议(NTP)服务器同步。
所以它取代了 ntpd。
$ timedatectl status Local time: Fri 2016-08-26 11:38:21 UTC Universal time: Fri 2016-08-26 11:38:21 UTC RTC time: Fri 2016-08-26 11:38:20 Time zone: Etc/UTC (UTC, +0000) Network time on: yes NTP synchronized: yes RTC in local TZ: no
/usr/sbin/atd -f
atd - 运行排队等待稍后执行的作业。atd 运行由 at 排队的作业。
at 和 batch 从标准输入或指定文件中读取命令,这些命令将在稍后时间执行。
与 cron 周期性重复调度作业不同,at 在特定时间只运行一次作业。
$ echo "touch /tmp/yolo.txt" | at now + 1 minute job 1 at Fri Aug 26 10:44:00 2016 $ atq 1 Fri Aug 26 10:44:00 2016 a root $ sleep 60 && ls /tmp/yolo.txt /tmp/yolo.txt
在此之前我实际上从未用过它。
sudo apt remove at -y --purge
/usr/lib/snapd/snapd
Snappy Ubuntu Core 是 Ubuntu 的一个新版本,具有事务性更新特性——一个最小的服务器镜像,拥有与当今 Ubuntu 相同的库,但应用程序通过更简单的机制提供。
什么?
来自多个 Linux 发行版和公司的开发者今天宣布合作,推出"snap"通用 Linux 软件包格式,使单个二进制包能够在任何 Linux 桌面、服务器、云或设备上完美安全地运行。
显然它是一个简化的 deb 包,你应该将所有依赖打包到一个 snap 中以便分发。
我从未在服务器上使用 snappy 部署或分发过应用程序。
sudo apt remove snapd -y --purge
/usr/bin/dbus-daemon
In computing, D-Bus or DBus is an inter-process communication (IPC) and remote procedure call (RPC) mechanism that allows communication between multiple computer programs (that is, processes) concurrently running on the same machine
My understanding is that you need it for desktop environments but on a server to run web apps?
sudo apt remove dbus -y --purge
I wonder what time it is and whether it is being synchronized with NTP?
$ timedatectl status Failed to create bus connection: No such file or directory
Oops. Should probably keep this.
/lib/systemd/systemd-logind
systemd-logind is a system service that manages user logins.
/usr/sbin/cron -f
cron - daemon to execute scheduled commands (Vixie Cron)
-f Stay in foreground mode, don't daemonize.
You can schedule tasks to run periodically with cron.
Use crontab -e to edit the configuration for your user or on Ubuntu I tend to use the /etc/cron.hourly, /etc/cron.daily, etc. directories.
You can see the log files with
grep cron /var/log/syslog or
journalctl _COMM=cron or even
journalctl _COMM=cron --since="date" --until="date"
You'll probably want to keep cron.
But if you don't, then you should stop and disable the service:
sudo systemctl stop cron sudo systemctl disable cron
Because otherwise when trying to remove it with apt remove cron it will try to install postfix!
$ sudo apt remove cron The following packages will be REMOVED: cron The following NEW packages will be installed: anacron bcron bcron-run fgetty libbg1 libbg1-doc postfix runit ssl-cert ucspi-unix
It looks like cron needs a mail transport agent (MTA) to send emails.
$ apt show cron Package: cron Version: 3.0pl1-128ubuntu2 ... Suggests: anacron (>= 2.0-1), logrotate, checksecurity, exim4 | postfix | mail-transport-agent
$ apt depends cron cron ... Suggests: anacron (>= 2.0-1) Suggests: logrotate Suggests: checksecurity |Suggests: exim4 |Suggests: postfix Suggests: <mail-transport-agent> ... exim4-daemon-heavy postfix
/usr/sbin/rsyslogd -n
Rsyslogd is a system utility providing support for message logging.
In another words, it's what populates log files in /var/log/ like /var/log/auth.log for authentication messages like SSH login attempts.
The configuration files are in /etc/rsyslog.d.
You can also configure rsyslogd to send log files to a remote server and implement centralized logging.
You can use the logger command to log messages to /var/log/syslog in background scripts such as those that are run at boot.
#!/bin/bash
logger Starting doing something
NFS, get IPs, etc.
logger Done doing something
Right, but we already have systemd-journald running. Do we need rsyslogd as well?
Rsyslog and Journal, the two logging applications present on your system, have several distinctive features that make them suitable for specific use cases. In many situations it is useful to combine their capabilities, for example to create structured messages and store them in a file database. A communication interface needed for this cooperation is provided by input and output modules on the side of Rsyslog and by the Journal's communication socket.
So, maybe? I am going to keep it just in case.
/usr/bin/dbus-daemon
在计算机中,D-Bus 或 DBus 是一种进程间通信(IPC)和远程过程调用(RPC)机制,允许同一台机器上同时运行的多个计算机程序(即进程)之间进行通信。
我的理解是,桌面环境需要它,但服务器上运行 web 应用也需要吗?
sudo apt remove dbus -y --purge
我想知道现在是什么时间,是否与 NTP 同步?
$ timedatectl status Failed to create bus connection: No such file or directory
哦哦。可能应该保留这个。
/lib/systemd/systemd-logind
systemd-logind 是一个管理系统用户登录的系统服务。
/usr/sbin/cron -f
cron - 用于执行计划命令的守护进程(Vixie Cron)
-f 保持在前台模式,不守护化。
你可以使用 cron 周期性调度任务。
使用 crontab -e 编辑你的用户配置, 在 Ubuntu 上我倾向于使用 /etc/cron.hourly、/etc/cron.daily 等目录。
你可以通过以下命令查看日志文件:
grep cron /var/log/syslog 或
journalctl _COMM=cron 甚至
journalctl _COMM=cron --since="date" --until="date"
你可能想保留 cron。
但如果你不想要,则应停止并禁用该服务:
sudo systemctl stop cron sudo systemctl disable cron
因为如果尝试用 apt remove cron 移除它,它会试图安装 postfix!
$ sudo apt remove cron The following packages will be REMOVED: cron The following NEW packages will be installed: anacron bcron bcron-run fgetty libbg1 libbg1-doc postfix runit ssl-cert ucspi-unix
看起来 cron 需要一个邮件传输代理(MTA)来发送邮件。
/usr/sbin/rsyslogd -n
Rsyslogd 是一个提供消息日志记录支持的系统工具。
换句话说,它填充 /var/log/ 下的日志文件, 比如用于 SSH 登录尝试等认证消息的 /var/log/auth.log。
配置文件在 /etc/rsyslog.d 中。
你还可以配置 rsyslogd 将日志文件发送到远程服务器,实现集中式日志记录。
你可以使用 logger 命令在后台脚本(如启动时运行的脚本)中将消息记录到 /var/log/syslog。
#!/bin/bash
logger Starting doing something
NFS, get IPs, etc.
logger Done doing something
但我们已经有了 systemd-journald 在运行。还需要 rsyslogd 吗?
Rsyslog 和 Journal 这两个系统上的日志记录应用各有特色,适用于特定场景。在很多情况下,结合它们的功能很有用,例如创建结构化消息并存储在文件数据库中。这种协作所需的通信接口由 Rsyslog 的输入输出模块和 Journal 的通信套接字提供。
所以,也许需要?我打算保留它以防万一。
/usr/sbin/acpid
acpid - Advanced Configuration and Power Interface event daemon
acpid is designed to notify user-space programs of ACPI events. acpid should be started during the system boot, and will run as a background process, by default.
In computing, the Advanced Configuration and Power Interface (ACPI) specification provides an open standard that operating systems can use to perform discovery and configuration of computer hardware components, to perform power management by, for example, putting unused components to sleep, and to do status monitoring.
But I'm on a virtual server that I don't intend to suspend/resume.
I am going to remove it for fun and see what happens.
sudo apt remove acpid -y --purge
I was able to successfully reboot the droplet but after halt Digital Ocean thought it was still on so I had to Power Off using the web interface.
So I should probably keep this.
/usr/bin/lxcfs /var/lib/lxcfs/
Lxcfs is a fuse filesystem mainly designed for use by lxc containers. On a Ubuntu 15.04 system, it will be used by default to provide two things: first, a virtualized view of some /proc files; and secondly, filtered access to the host’s cgroup filesystems.
In summary, on a 15.04 host, you can now create a container the usual way, lxc-create ... The resulting container will have “correct” results for uptime, top, etc.
It’s basically a userspace workaround to changes which were deemed unreasonable to do in the kernel. It makes containers feel much more like separate systems than they would without it.
Not using LXC containers? You can remove it with
sudo apt remove lxcfs -y --purge
/usr/lib/accountservice/accounts-daemon
The AccountsService package provides a set of D-Bus interfaces for querying and manipulating user account information and an implementation of these interfaces based on the usermod(8), useradd(8) and userdel(8) commands.
When I removed DBus it broke timedatectl, I wonder what removing this service will break.
sudo apt remove accountsservice -y --purge
Time will tell.
/sbin/mdadm
mdadm is a Linux utility used to manage and monitor software RAID devices.
The name is derived from the md (multiple device) device nodes it administers or manages, and it replaced a previous utility mdctl. The original name was "Mirror Disk", but was changed as the functionality increased.
RAID is a method of using multiple hard drives to act as one. There are two purposes of RAID: 1) Expand drive capacity: RAID 0. If you have 2 x 500 GB HDD then total space become 1 TB. 2) Prevent data loss in case of drive failure: For example RAID 1, RAID 5, RAID 6, and RAID 10.
You can remove it with
sudo apt remove mdadm -y --purge
/usr/lib/policykit-1/polkitd --no-debug
polkitd — PolicyKit daemon
polkit - Authorization Framework
My understanding is that this is like fine-grained sudo. You can allow non privilegded users to do certain actions as root. For instance, reboot your computer when you're running Linux on a desktop computer.
But I'm running a server. You can remove it with
sudo apt remove policykit-1 -y --purge
Still wondering if this breaks something.
/usr/sbin/acpid
acpid - 高级配置与电源接口事件守护进程
acpid 旨在将 ACPI 事件通知给用户空间程序。acpid 应在系统启动时启动,默认作为后台进程运行。
在计算机中,高级配置与电源接口(ACPI)规范提供了一个开放标准,操作系统可以使用它来发现和配置计算机硬件组件,通过将未使用的组件置于休眠等方式进行电源管理,并进行状态监控。
但我在一台不打算挂起/恢复的虚拟服务器上。
我打算为了好玩把它移除,看看会发生什么。
sudo apt remove acpid -y --purge
我成功重启了 droplet,但在关机后 Digital Ocean 认为它仍然在线,因此我不得不使用 Web 界面强制关机。
所以可能还是应该保留它。
/usr/bin/lxcfs /var/lib/lxcfs/
Lxcfs 是一个 FUSE 文件系统,主要设计用于 LXC 容器。在 Ubuntu 15.04 系统上,它默认被用来提供两件事:第一,某些 /proc 文件的虚拟化视图;第二,对宿主机 cgroup 文件系统的过滤访问。
简而言之,在 15.04 宿主机上,你现在可以用通常的方式创建容器。得到的容器对于 uptime、top 等会有"正确"的结果。
它本质上是用户空间对内核中认为不合理更改的一种变通方案。它让容器感觉起来更像独立的系统。
不使用 LXC 容器?你可以通过以下命令移除:
sudo apt remove lxcfs -y --purge
/usr/lib/accountservice/accounts-daemon
AccountsService 包提供了一组 D-Bus 接口,用于查询和操作用户账户信息,以及基于 usermod(8)、useradd(8) 和 userdel(8) 命令的这些接口的实现。
当我移除 DBus 时,它破坏了 timedatectl,我好奇移除这个服务会破坏什么。
sudo apt remove accountsservice -y --purge
时间会告诉我们。
/sbin/mdadm
mdadm 是一个用于管理和监控软件 RAID 设备的 Linux 工具。
名称源自它管理或操作的 md(多设备)设备节点,它取代了以前的工具 mdctl。最初的名字是"Mirror Disk"(镜像磁盘),但随着功能的增加而更改。
RAID 是一种使用多个硬盘驱动器作为一个整体的方法。RAID 有两个目的:1)扩展驱动器容量:RAID 0。如果你有 2 个 500 GB 硬盘,总空间变成 1 TB。2)防止驱动器故障时的数据丢失:例如 RAID 1、RAID 5、RAID 6 和 RAID 10。
你可以通过以下命令移除:
sudo apt remove mdadm -y --purge
/usr/lib/policykit-1/polkitd --no-debug
polkitd — PolicyKit 守护进程
polkit - 授权框架
我的理解是这类似于细粒度的 sudo。 你可以允许非特权用户以 root 身份执行某些操作。 例如,在桌面上运行 Linux 时重启计算机。
但我运行的是服务器。你可以通过以下命令移除:
sudo apt remove policykit-1 -y --purge
仍然怀疑这是否会破坏某些东西。
/usr/sbin/sshd -D
sshd (OpenSSH Daemon) is the daemon program for ssh.
-D When this option is specified, sshd will not detach and does not become a daemon. This allows easy monitoring of sshd.
/sbin/iscsid
iscsid is the daemon (system service) that runs in the background, acting on iSCSI configuration, and managing the connections. From its manpage:
The iscsid implements the control path of iSCSI protocol, plus some management facilities. For example, the daemon could be configured to automatically re-start discovery at startup, based on the contents of persistent iSCSI database.
I had never heard of iSCSI:
In computing, iSCSI (Listeni/aɪˈskʌzi/ eye-skuz-ee) is an acronym for Internet Small Computer Systems Interface, an Internet Protocol (IP)-based storage networking standard for linking data storage facilities.
By carrying SCSI commands over IP networks, iSCSI is used to facilitate data transfers over intranets and to manage storage over long distances. iSCSI can be used to transmit data over local area networks (LANs), wide area networks (WANs), or the Internet and can enable location-independent data storage and retrieval.
The protocol allows clients (called initiators) to send SCSI commands (CDBs) to SCSI storage devices (targets) on remote servers. It is a storage area network (SAN) protocol, allowing organizations to consolidate storage into data center storage arrays while providing hosts (such as database and web servers) with the illusion of locally attached disks.
You can remove it with
sudo apt remove open-iscsi -y --purge
/sbin/agetty --noclear tty1 linux
agetty - alternative Linux getty
getty, short for "get tty", is a Unix program running on a host computer that manages physical or virtual terminals (TTYs). When it detects a connection, it prompts for a username and runs the 'login' program to authenticate the user.
Originally, on traditional Unix systems, getty handled connections to serial terminals (often Teletype machines) connected to a host computer. The tty part of the name stands for Teletype, but has come to mean any type of text terminal.
This allows you to log in when you are physically at the server. In Digital Ocean, you can click on Console in the droplet details and you will be able to interact with this terminal in your browser (it's a VNC connection I think).
In the old days, you'd see a bunch of ttys started a system boot (configured in /etc/inittab), but nowadays they are spun up on demand by systemd.
For fun, I removed this configuration file that launches and generates agetty:
sudo rm /etc/systemd/system/getty.target.wants/[email protected] sudo rm /lib/systemd/system/[email protected]
When I rebooted the server, I could still connect to it via SSH but I was no longer able to log in from the Digital Ocean web console.
sshd: root@pts/0 & -bash & htop
sshd: root@pts/0 means that there has been an SSH session established for the user root at the #0 pseudoterminal (pts). A pseudoterminal emulates a real text terminal.
bash is the shell that I am using.
Why is there a dash at the beginning? Reddit user hirnbrot helpfully explained it:
There's a dash at the beginning because launching it as "-bash" will make it a login shell. A login shell is one whose first character of argument zero is a -, or one started with the --login option. This will then cause it to read a different set of configuration files.
htop is an interactive process viewer tool that is running in the screenshot.
/usr/sbin/sshd -D
sshd(OpenSSH 守护进程)是 ssh 的守护程序。
-D 指定此选项时,sshd 不会脱离控制终端,也不会成为守护进程。这样可以方便监控 sshd。
/sbin/iscsid
iscsid 是在后台运行的守护进程(系统服务),根据 iSCSI 配置进行操作并管理连接。从它的 man 手册页:
iscsid 实现了 iSCSI 协议的控制路径,以及一些管理功能。例如,可以配置守护进程在启动时根据持久 iSCSI 数据库的内容自动重新开始发现。
我从未听说过 iSCSI:
在计算机中,iSCSI 是互联网小型计算机系统接口的缩写,是一种基于互联网协议(IP)的存储网络标准,用于连接数据存储设施。
通过 IP 网络承载 SCSI 命令,iSCSI 用于促进内联网上的数据传输和远程存储管理。iSCSI 可用于在局域网(LAN)、广域网(WAN)或互联网上传输数据,并能实现位置无关的数据存储和检索。
该协议允许客户端(称为发起者)向远程服务器上的 SCSI 存储设备(目标)发送 SCSI 命令。它是一种存储区域网络(SAN)协议,允许组织将存储整合到数据中心存储阵列中,同时向主机(如数据库和 Web 服务器)提供本地连接磁盘的假象。
你可以通过以下命令移除:
sudo apt remove open-iscsi -y --purge
/sbin/agetty --noclear tty1 linux
agetty - 替代性 Linux getty
getty,是"get tty"的缩写,是一个运行在宿主机上的 Unix 程序,管理物理或虚拟终端(TTY)。当检测到连接时,它会提示输入用户名并运行 'login' 程序来认证用户。
最初,在传统的 Unix 系统上,getty 处理与连接到宿主机上的串行终端(通常是电传打字机)的连接。名称中的 tty 部分代表 Teletype,但现在已指代任何类型的文本终端。
这允许你物理上在服务器前时登录。 在 Digital Ocean 中,你可以在 droplet 详情中点击 Console, 然后就能在浏览器中与此终端交互 (我猜这是 VNC 连接)。
在过去,你会看到系统启动时启动了一堆 tty(在 /etc/inittab 中配置), 但现在它们由 systemd 按需生成。
为了好玩,我移除了这个用于启动和生成 agetty 的配置文件:
sudo rm /etc/systemd/system/getty.target.wants/[email protected] sudo rm /lib/systemd/system/[email protected]
当我重启服务器后,我仍然可以通过 SSH 连接, 但无法再从 Digital Ocean Web 控制台登录了。
sshd: root@pts/0 & -bash & htop
sshd: root@pts/0 表示已为用户 root 在 #0 伪终端(pts)上建立了一个 SSH 会话。伪终端模拟了一个真实的文本终端。
bash 是我正在使用的 shell。
为什么开头有个短横?Reddit 用户 hirnbrot 很有帮助地解释道:
开头有短横是因为以 "-bash" 启动会使其成为登录 shell。 登录 shell 是参数零的第一个字符为 - 的 shell,或者使用 --login 选项启动的 shell。 这将导致它读取一组不同的配置文件。
htop 是截图中正在运行的交互式进程查看工具。
Extreme edition:
sudo apt remove dbus -y --purge sudo apt remove rsyslog -y --purge sudo apt remove acpid -y --purge sudo systemctl stop cron && sudo systemctl disable cron sudo rm /etc/systemd/system/getty.target.wants/[email protected] sudo rm /lib/systemd/system/[email protected]
I followed the instructions in my blog post about unattended installation of WordPress on Ubuntu Server and it works.
Here's nginx, PHP7 and MySQL.
极限版本:
sudo apt remove dbus -y --purge sudo apt remove rsyslog -y --purge sudo apt remove acpid -y --purge sudo systemctl stop cron && sudo systemctl disable cron sudo rm /etc/systemd/system/getty.target.wants/[email protected] sudo rm /lib/systemd/system/[email protected]
我按照博客文章中关于在 Ubuntu Server 上无人值守安装 WordPress 的说明操作, 它成功了。
下面是 nginx、PHP7 和 MySQL 的情况。
Source code
Sometimes looking at strace is not enough.
Another way to figure out what a program does is to look at its source code.
First, I need to find out where to start looking.
$ which uptime /usr/bin/uptime $ dpkg -S /usr/bin/uptime procps: /usr/bin/uptime
Here we find out that uptime is actually located at /usr/bin/uptime and that on Ubuntu it is part of the procps package.
You can then go to packages.ubuntu.com and search for the package there.
Here is the page for procps: http://packages.ubuntu.com/source/xenial/procps
If you scroll to the bottom of the page, you'll see links to the source code repositories:
Debian Package Source Repository git://git.debian.org/collab-maint/procps.git
Debian Package Source Repository (Browsable) https://anonscm.debian.org/cgit/collab-maint/procps.git/
File descriptors and redirection
When you want to redirect standard error (stderr) to standard output (stdout), is it 2&>1 or 2>&1?
You can memorize where the ampersand & goes by knowing that echo something > file will write something to the file file. It's the same as echo something 1> file. Now, echo something 2> file will write the stderr output to file.
If you write echo something 2>1, it means that you redirect stderr to a file with the name 1. Add spaces to make it more clear: echo something 2> 1.
If you add & before 1, it means that 1 is not a filename but the stream ID. So it's echo something 2>&1.
Colors in PuTTY
If you have missing elements in htop when you are using PuTTY, here is how to solve it.
Right click on the title bar
Click Change settings...
Go to Window -> Colours
Select the Both radio button
Click Apply
源码
有时仅查看 strace 是不够的。
另一种弄清程序行为的方法是查看其源代码。
首先,我需要找到从哪里开始查找。
$ which uptime /usr/bin/uptime $ dpkg -S /usr/bin/uptime procps: /usr/bin/uptime
这里我们发现 uptime 实际位于 /usr/bin/uptime, 并且在 Ubuntu 上它属于 procps 包。
然后你可以访问 packages.ubuntu.com 并在那里搜索该包。
这是 procps 的页面:http://packages.ubuntu.com/source/xenial/procps
如果滚动到页面底部,你会看到源代码仓库的链接:
Debian Package Source Repository git://git.debian.org/collab-maint/procps.git
Debian Package Source Repository (Browsable) https://anonscm.debian.org/cgit/collab-maint/procps.git/
文件描述符与重定向
当你想要将标准错误(stderr)重定向到标准输出(stdout)时, 是 2&>1 还是 2>&1?
你可以通过知道 echo something > file 会将内容写入文件 file 来记忆 & 的位置。 这等价于 echo something 1> file。现在,echo something 2> file 会将 stderr 输出写入文件 file。
如果你写 echo something 2>1,这意味着你将 stderr 重定向到一个名为 1 的文件。 加上空格会更清楚:echo something 2> 1。
如果你在 1 前面加上 &,意味着 1 不是一个文件名,而是流 ID。 所以应该是 echo something 2>&1。
PuTTY 中的颜色
如果你在使用 PuTTY 时 htop 中缺少某些元素,以下是解决方法。
右键单击标题栏
单击"Change settings..."
转到 Window -> Colours
选择 Both 单选按钮
单击"Apply"
Let's write a very simple shell in C that demonstrates the use of fork/exec/wait system calls. Here's the program shell.c.
让我们用 C 编写一个非常简单的 shell,用于演示 fork/exec/wait 系统调用的使用。以下是程序 shell.c。