htop і багато іншого на пальцях



Протягом довгого часу я не до кінця розумів htop. Я думав, що середня завантаженість [load average] в 1.0 означає, що процесор завантажений на 50%, але це не зовсім так. Та й потім, чому саме 1.0?

Потім я вирішив у всьому розібратися і написати про це. Кажуть, що кращий спосіб навчитися новому — спробувати це пояснити.

htop на Ubuntu Server 16.04 x64
Нижче скріншот htop, який я буду розглядати в статті.



Uptime
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. Але це не зовсім спрацює, оскільки за замовчуванням він виводить на стандартний потік помилок (stderr). Можна перенаправити stderr в стандартний потік з допомогою 2>&1.

Результат такий:

$ 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 форматує висновок у читаному вигляді, тоді як секунди у файлі зручно використовувати при написанні власних скриптів і програм.

Load average
Крім часу безперервної роботи, uptime показує і середню завантаження системи, вони відображені як 3 числа.

$ 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

Перші 3 числа вимірюють середню завантаження системи за останні 1, 5 і 15 хвилин. 4-ий параметр це кількість активних процесів і їх загальне число. Останнє число ID останнього використаного процесу.

Почнемо з кінця.

Коли запускається процес, йому присвоюється ID. Як правило, вони йдуть у зростаючому порядку, за винятком випадків, коли число вичерпалася і системі доводиться починати відлік заново. ID 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, ви бачите, що виконується тільки один процес, це сам процес htop.

Якщо запустити sleep 30 і відкрити htop, то число виконуваних процесів все одно буде 1. Це тому, що процес sleep не виконується, а «спить», тобто знаходиться в стані спокою, іншими словами чекає певної події. Виконання йди активний процес, це процес, який на даний момент обробляється в процесор (CPU), або чекає своєї черги в процесорі.

Спробуйте запустити cat /dev/urandom > /dev/null, де генеруються випадкові байти записуються у спеціальний файл, зчитування з якого неможливо. Тоді ви побачите, що виконуються процесів тепер вже 2.

$ cat /dev/urandom > /dev/null &
[1] 1639
$ cat /proc/loadavg
1.00 0.69 0.35 2/124 1679

Так, активних процесів рівно 2 (генератор випадкових чисел і утиліта cat, яка читає файл /proc/loadavg), ще можна помітити що середня завантаженість зросла.

load average це середнє завантаження системи протягом певного періоду часу.

Число завантажень вважається як сума кількості процесів, які запущені (виконуються або перебувають в очікуванні запуску) і непрерываемых процесів (про види процесів буде розказано нижче). Тобто це просто число процесів.

А середнє завантаження виходить просто осереднене значення за 1, 5 і 15 хвилин, так?

Виявляється, не все так просто.

Говорячи математичною мовою, всі три значення усереднюють середню завантаження за весь час роботи системи. Вони застарівають експоненціально, але з різною швидкістю. Таким чином, середнє завантаження за 1 хвилину це сума 63% завантаження за останню хвилину + 37% завантаження з моменту запуску без урахування останньої хвилини. Те ж співвідношення вірно і для 5, 15 хвилин. Тому не зовсім вірно, що середня завантаженість за останню хвилину включає активність тільки за останню хвилину, але здебільшого за останню хвилину.

Ви це знали?

Повернемося до генератора випадкових чисел.

$ cat /proc/loadavg
1.00 0.69 0.35 2/124 1679

Хоча це не зовсім правильно, але от як я спростив для розуміння показник середньої завантаження.

В даному випадку генератор використовує процесор, середнє завантаження за останню хвилину 1.00, іншими словами-в середньому 1 виконуваний процес.

У моїй системі це означає, що процесор завантажений на 100%, т. к. процесор один, а він може виконувати тільки один процес за раз.

Якби процесорів було 2, то завантаження відповідно була б 50%, т. к. можна було б одночасно виконувати 2 процесу. Максимальна середня завантаження (100% використання CPU) системи з двома процесорами становить 2.00.

Кількість процесорів в системі можна дізнатися в лівому верхньому куті htop та nproc.

Процеси
У правому верхньому куті, htop показує загальну кількість процесів і скільки з них активні. Але чому там написано завдання [Tasks], а не процеси?

Завдання це синонім процесу. У ядрі Linux процеси це і є завдання. htop використовує термін завдання, можливо, тому, що це назва коротше і економить небагато місця.
У htop можна побачити і потоки [threads]. Для перемикання цієї опції потрібно використовувати комбінацію Shift+H. Якщо з'являється щось на зразок Tasks: 23, 10 thr, то це значить вони видимі.

Відображення потоків виконання ядра [kernel threads] можна включити комбінацією Shift+K, і тоді завдання будуть виглядати як Tasks: 23, 40 kthr.

ID процесу / PID
При кожному запуску процесу, йому присвоюється ідентифікатор (ID), скорочено PID.

Якщо запускати програму у фоновому режимі (&) з bash, то PID виводиться в квадратних дужках.

$ sleep 1000 &
[1] 12503

Ще один спосіб, це використовувати змінну $! bash, яка зберігає PID останнього процесу, запущеного в тлі.

$ echo $!
12503

ID процесу дуже корисна. За допомогою нього можна дізнатися подробиці процесу і керувати ним.

Існує псевдо файлова система procfs, з допомогою якої програми можуть отримати інформацію від ядра системи шляхом читання файлів. Найчастіше вона монтується в /proc/ і для користувача, що виглядає як звичайний каталог, який можна дивитися командами, такими як ls та cd.

Вся інформація про процес знаходиться в /proc/<рid>/.
$ 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/<рid>/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 6 Jul 10:10 /proc/12503/cwd -> /home/ubuntu
lrwxrwxrwx 1 ubuntu ubuntu 0 6 Jul 10:10 /proc/12503/exe -> /bin/sleep

Таким чином утиліти htop, top, ps і інші показують інформацію про процесі, вони просто читають /proc/<рid>/<файл>.

Дерево процесів
Коли запускається новий процес, процес, який запускає його прийнято називати батьківським або просто батьком. Таким чином новий процес це дочірній процес батьківського. Ці відносини утворюють структуру у вигляді дерева.

Якщо натиснути F5 htop, то можна побачити ієрархію процесів.

Той самий ефект і від прапора f команди ps.

$ 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 є батьківськими для деяких процесів, то ось чому.

Нижче я написав, що відбувається, якщо ви, приміром, викличте date з консолі bash.

  • bash створить нову копію свого процесу (використовуючи системний виклик fork)
  • Потім він перемістить виконуваний файл /bin/date в пам'ять (за допомогою системного виклику exec).
  • bash, як батьківський процес буде чекати закінчення роботи дочірнього.
Таким чином, /sbin/init, у якого ID 1, почав виконуватися при старті системи і породив демона SSH sshd. Після підключення до системи, sshd породить процес для поточної сесії, який у свою чергу запустить консоль bash.

Я віддаю перевагу використовувати деревоподібну структуру у htop коли хочеться побачити всі потоки.

Власник процесу
У кожного процесу є власник — користувач. У користувачів, у свою чергу, існують чисельні 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/passwd", O_RDONLY|O_CLOEXEC) = 3
open("./etc/group", O_RDONLY|O_CLOEXEC) = 3

Це звичайні текстові файли, в яких 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:

пароль? Але де паролі? А вони насправді у /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:::

Якщо ви запустите програму, то вона запуститься від вашого імені, навіть якщо ви не є її власником. Якщо ж вам потрібно запустити її як 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.

Якщо не хочеться щоразу вводити пароль адміністратора при запуску програм, то можна вимкнути цю функцію, додавши своє ім'я користувача в файл /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

Що за...?

Тут ми намагаємося викликати echo від імені адміністратора, але при цьому пишемо в файл /etc/sudoers все так само від нашого імені.

Як правило, є 2 виходи з даної ситуації:

  • echo "$USER ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
  • sudo bash -c «echo '$USER ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers»
У першому випадку, tee -a запише зі стандартного потоку вводу в файл від імені адміністратора.

У другому випадку, ми запускаємо консоль від імені адміністратора і просимо виконати команди (c) і всі команди виконуються від імені root. Зверніть увагу на розстановку лапок "/', за допомогою яких змінна $USER разыменуется правильно.

Припустимо, ви захотіли змінити свій пароль. Команда пароль вам в допомогу. Вона збереже паролі у файлі /etc/shadow, який ми бачили вище.

Цей файл доступний для запису тільки для root:

$ ls -l /etc/shadow
-rw-r----- 1 root shadow 1122 Nov 27 18:52 /etc/shadow

Як же це можливо, що програма запускається від імені користувача може записувати в захищений файл?

Я вже говорив, що при виклику, програма запускається від імені користувача, що запускає її, навіть якщо вона належить іншій особі.

Виявляється, це поведінка можна змінити правками дозволу файлу. Давайте подивимося.

$ 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.

Так звані виконувані файли setuid можна шукати за допомогою find /bin -root user -perm -u+s.

Так само це можна здійснити і для груп (g+s).

Стану процесу
Далі, ми будемо розбиратися зі стовпцем стану процесів у htop, в якому, на прикладі, знаходяться символи S.

Можливі значення стану:

  • R — [running or runnable] запущені або знаходяться в черзі на запуск
  • S — [interruptible sleep] переривчастий сон
  • D — [uninterruptible sleep] неперерваний сон (в основному IO)
  • Z — [zombie] процес зомбі, припинений, але не забраний батьком
  • T — Зупинений сигналом управління завданнями
  • t — Зупинений відладчиком
  • X — Мертвий (не повинен показуватися)
Вони відсортовані по тому, як часто я їх бачу.

Зауважте, що при запуску 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 — Запущені або в черзі
Процеси в цьому стані або занедбані, або перебувають у черзі для запуску.

Що це означає?

Коли ви компилируете код, то на виході отримуєте виконуваний файл у вигляді інструкцій для процесора. При запуску, цей файл поміщається в пам'ять, де процесор виконує ці інструкції, простіше кажучи займається обчисленнями.

S — Переривчастий сон
При цьому стані інструкції програми не виконуються в процесорі, простіше кажучи сплять. Процес чекає події або якого-небудь умови для продовження. Після того, як подія відбулася, стан змінюється на занедбане.

Для прикладу можна взяти утиліту sleep з coreutils. Він буде знаходиться в стані сну певну кількість секунд.

$ 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, тому що це насправді системний виклик, який може послати сигнал процесу. Існує однойменна програма /bin/kill, яка може виконати системномный виклик з користувальницького оточення і за замовчуванням посилає сигнал TERM, який знищує процес, вбиває його.

Сигнал-це всього лише число. Числа складно запам'ятати, тому їх назвали іменами. Їх імена зазвичай пишуть великими літерами і можуть бути з префіксом SIG.

Часто використовуються сигнали, це: INT, KILL, STOP, CONT, HUP.

Спробуємо перервати сплячий процес, пославши йому сигнал INT, він же SIGINT просто 2 або сигнал переривання з терміналу.

$ kill -INT 10089
[1]+ Interrupt sleep 1000

Цей же сигнал надсилається, якщо натиснути комбінацію CTRL+C. bash пошле сигнал SIGINT процесу тлі точно так само як ми це зробили вручну.

До речі, у bash команда kill вбудована, хоча в багатьох системах є програма /bin/kill. Чому? Щоб «убити» процес навіть якщо перевищено ліміт на кількість створюваних процесів.

Наступні команди ідентичні:

  • kill 10089
  • kill -INT 10089
  • kill -2 10089
  • /bin/kill -2 10089
Інший корисний сигнал це SIGKILL або 9. Ви, можливо, використовували його, коли не могли завершити процес нестримним кликаньем CTRL+C.

При написанні програми, можна ці сигнали ловити і створювати функції, які будуть запускатися коли відповідний сигнал отриманий. Наприклад, можна очищати пам'ять або ж акуратно завершити роботу. Тому відправлення сигналу, такого як сигнал переривання з терміналу, не означає що процес буде припинено.

Можливо, ви зустрічали таке виключення при запуску скриптів 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 — неперерваний сон
На відміну від переривається сну, процеси в такому стані неможливо зупинити за допомогою сигналів. Тому багато хто не люблять цей стан.

При цьому стані процес чекає і не може бути перерваний, наприклад, якщо подія продовження ось-ось настане, таке як читання/запис на диск. Як правило, це відбувається за частки секунди.

На StackOverflow є хороший відповідь:
Непрерываемые процеси зазвичай знаходяться в очікуванні IO після page fault. Процес не може бути перерваний у цей час сигналом, тому що не може їх обробити. Якби він міг, то знову виник би page fault і все залишилося як є.
Іншими словами, це може статися, якщо, наприклад, використовувати протокол мережевого доступу NFS і потрібен час для читання/запису з/на нього.

По своєму досвіду можу сказати, що таке трапляється коли процес часто підкачується, тобто для нього недостатньо вільної пам'яті.

Спробуємо викликати цей стан.

8.8.8.8 це публічний DNS від Google. Там немає NFS, але це нас не зупинить.

$ sudo mount 8.8.8.8:/tmp /tmp &
[1] 12646
$ sudo x ps | 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 блокує процес.

А mount, до речі, можна викликати з опцією intr, щоб його можна було переривати: sudo mount 8.8.8.8:/tmp /tmp -o intr.

Z — Зомбі процес
Коли процес закінчує свою роботу з допомогою exit і у неї залишаються дочірні процеси, дочірні процеси стають в стані зомбі.

  • Абсолютно нормально, якщо зомбі процес існує недовго
  • Зомбі процеси які існують тривалий час, можуть говорити про ба у програмі
  • Зомбі процеси не використовують пам'ять, лише ідентифікатор процесу
  • Зомбі процес не можна «вбити»
  • Можна ввічливо попросити батьківський процес позбутися від зомбі (пославши SIGCHLD)
  • Можна завершити батьківський процес, щоб позбутися від обох
Я продемонструю це, написавши невеликий код на С.

#include < stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
printf("Running\n");

int pid = fork();

if (pid == 0) {
printf("Я батьківський процес\n");
printf("Батьківський процес завершує роботу\n");
exit(0);
} else {
printf("Я дочірній процес\n");
printf("Дочірній процес спить\n");
sleep(20);
printf("Дочірній процес завершено\n");
}

return 0;
}

Встановлюємо компілятор С, GNU C Compiler (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 — Зупинено сигналом управління завданнями
Я відкрив два термінали і можу подивитися на свої процеси командою 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.

Тепер в одному з терміналів запустимо cat /dev/urandom > /dev/nul. Його стан буде 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 з допомогою kill, а для продовження, відповідно, сигнал CONT.

t — Зупинено відладчиком
Для початку встановимо налагоджувача GNU Debugger (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

Запустимо відладчик і прив'яжемо його до процесу з PID 3905.

sudo gdb -p 3905

Тепер процес буде простежуватися [trace] налагоджувач і його стан зміниться на 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 — багатозадачна операційна система, це означає, що навіть якщо процесор один, то можна одночасно запускати на ньому кілька завдань. Наприклад, можна підключитися до віддаленого сервера через SSH і подивитися на висновок htop, а при цьому сам сервер буде показувати ваш блог читачам в інтернеті.

Як же можливо, що єдиний процесор може одночасно виконувати кілька завдань?

Поділом часу.

Кожен процес виконується певний інтервал часу, при якому інші припинені, потім виконується наступний процес і т. д.

Як правило, інтервал часу виконання становить мілісекунди, тому користувач цього і не помітить, якщо, звичайно ж, система не навантажена.

Люб'язність і пріоритет процесів
Коли кількість завдань перевищує кількість процесорів, але виконати їх всі необхідно, потрібно якимось чином визначити порядок їх виконання. За це відповідає планувальник завдань.

Планувальник у ядрі Linux вирішує який процес вибрати з черги на запуск і це залежить від результату алгоритму, закладеного в ядрі.

Користувач, як правило, не може прямо впливати на планування, але може підказати який з процесів їй особливо важливий і планувальник, можливо, прислухається до нього.

Люб'язність або пріоритет nice (NI) це пріоритет процесу в просторі користувача, варьирующаяся від -20, що є найвищий пріоритет, до 19, відповідно найменший пріоритет. Це може заплутати, але уявіть це саме як люб'язність, тобто чим процес привітніше, тим він поступливішими іншим процесам.

З того, що я прочитав на StackOverflow та інших сайтах, слід що збільшення люб'язності процесу на 1 веде до відступлення 10% часу роботи процесора.

Пріоритет (PRI) ж в свою чергу це параметр пріоритету в просторі ядра. Пріоритет варіюється від 0 до 139. Пріоритети від 0 до 99 зарезервовані для процесів реального часу, а вище, т. е від 100 до 139, для користувача.

Можна змінити люб'язність процесу, і тоді, можливо, ядро прийме це до уваги, але сам пріоритет міняти не можна.

Співвідношення люб'язності і пріоритету наступне: PR = 20 + NI.
Таким чином область визначення PR = 20 + (-20… +19) лежить на відрізку від 100 до 139.

Можна встановити люб'язність процесу безпосередньо перед запуском.

nice -n люб'язність program

А міняти люб'язність під час виконання можна за допомогою renice.

renice -n niceness -p PID

http://askubuntu.com/questions/656771/process-niceness-vs-priority

Пам'ять — VIRT/RES/SHR/MEM
У процесів створюється ілюзія, що пам'ять крім них ніхто не використовує. Така ілюзія — результат роботи віртуальної пам'яті.

Процеси не мають прямого доступу до фізичної пам'яті. Для них виділяється ділянка віртуальної пам'яті, адрес в якій, проектуються ядром вже на адреси фізичної пам'яті, або на диск. Тому, іноді здається, що процеси використовують більше пам'яті, ніж встановлено в системі.

Я хочу сказати, що з-за цього не зовсім легко зрозуміти скільки ж саме пам'яті використовує процес. А що щодо загальних [shared] бібліотек і пам'яті, вивантаженої на диск? Але, на щастя, ядро і, зокрема, htop дозволяють отримати деяку інформацію, щоб зрозуміти апетит процесу по відношенню до пам'яті.

VIRT/VSZ — Віртуальний образ
Загальна кількість пам'яті, займана процесом. Воно включає в себе весь код, дані, загальні бібліотеки, сторінки які були переміщені на диск, а також сторінки, які проектувалися ядром, але не були використані.

Таким чином VIRT це все, що використовується процесом.

Якщо додаток запитує 1 Гб пам'яті, але використовує при це тільки 1 Мб, пам'ять VIRT буде відображатися все одно як 1 Гб. Навіть якщо воно викличе mmap для файлу вагою в 1 Гб і ніколи ним не скористається, то VIRT все одно залишиться 1 Гб.

В більшості випадків цей показник марний.

RES/RSS — Резидентна пам'ять
Пам'ять RSS [resident set size] це область, яка не вивантажена на диск і знаходиться в оперативній пам'яті.

RES, можливо, краще відображає реальне використання пам'яті процесора ніж VIRT, але потрібно мати на увазі:

  • Туди не включена пам'ять, выгруженная на диск
  • Деяка пам'ять може бути що використовується спільно декількома процесами
Якщо процес використовує 1 Гб пам'яті і викликає fork(), то в результаті в обох процесів значення RES буде 1 Гб, в той час як в оперативній пам'яті буде зайнято лише 1 Гб, тому що в Linux є механізм копіювання при записі [copy-on-write].

SHR — Спільна пам'ять
Об'єм пам'яті, який може бути спільно використаний іншими процесами.

#include < stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
printf("Запуск\n");
sleep(10);

size_t memory = 10 * 1024 * 1024; // 10 MB
char* buffer = malloc(memory);
printf("Виділено 10M\n");
sleep(10);

for (size_t i = 0; i < memory/2; i++)
buffer[i] = 42;
printf("Використано 5M\n");
sleep(10);

int pid = fork();
printf("Новий потік\n");
sleep(10);

if (pid != 0) {
for (size_t i = memory/2; i < memory/2 + memory/5; i++)
buffer[i] = 42;
printf("дод. 2M нащадку\n");
}
sleep(10);

return 0;
}

Потім

fallocate -l 10G
gcc -std=c99 mem.c -o mem
./mem

І

Процес Повідомлення VIRT RES SHR
головний Запуск 4200 680 604
головний Виділено 10M 14444 680 604
головний Використано 5M 14444 6168 1116
головний Новий потік 14444 6168 1116
нащадок Новий потік 14444 5216 0
головний дод. 2M нащадку 8252 1116
нащадок дод. 2M нащадку 5216 0

(прим. Цей розділ не дописаний до кінця, як тільки стаття оновиться, я опублікую оновлення)

MEM% — Використання пам'яті
Відсоток використання фізичної пам'яті. Це RES, поділений на загальний обсяг оперативної пам'яті.

Якщо, наприклад, RES становить 200М і в системі встановлено 8 Гб пам'яті, то MEM% буде 200/8192*100 = 2.4%

Процеси
Я запустив віртуальну машину з Ubuntu Server Digital Ocean. Які ж процеси запускаються при старті системи? Потрібні вони?

Нижче наведено аналіз процесів, які запускаються на чистій версії машини з Ubuntu Server 16.04.1 LTS x64 Digital Ocean.




/sbin/init
Ця програма координує всі інші програми при запуску і конфігурує оточення користувача. Після запуску, вона стає батьком або прабатьком усіх автоматично запускаються процесів.

Це ж systemd?

$ dpkg -S /sbin/init
systemd-sysv: /sbin/init

Так, він самий. Що станеться, якщо його зупинити? Нічого.

/lib/systemd/systemd-journald
ystemd-journald це системна служба, яка збирає і зберігає логи. Вона створює структуровані, проіндексований журнали на основі інформації, отриманої з різних джерел і керує ними.

Іншими словами,

Одним з основних переваг journald є заміна звичайних текстових файлів логів спеціально оформлені структурованими повідомленнями. Це дозволяє адміністраторам ефективніше працювати з журналами подій.

Якщо потрібно знайти подія, краще використовувати journalctl.

  • journalctl _COMM=sshd пошук по sshd
  • journalctl _COMM=sshd -o json-pretty пошук по sshd в 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 історія з моменту запуску системи
  • journalctl -f щоб стежити за логами
  • journalctl --disk-usage
  • journalctl --vacuum-size=1G
Вражаюче. Цей процес, здається, не можна зупинити або прибрати, можна лише відключити ведення історії.

/sbin/lvmetad -f
Демон lvmetad кешує метадані LVM, щоб команди LVM отримували доступ до метаданих без сканування диска. Кешування допомагає уникнути можливого вмешивания в роботу інших додатків і заощадити час сканування диска.

Але що таке LVM [Logical Volume Management] (Менеджер логічних томів)? Можна вважати, що LVM це динамічні розділи, що передбачає створення/редагування/видалення розділів, так званих «логічних томів» з командного рядка «на льоту», без необхідності перезавантаження системи.

Звучить так, що потрібен він тільки якщо користуватися LVM.

$ lvscan
$ sudo apt remove lvm2 -y --purge

/lib/systemd/udevd
systemd-udevd стежить за подіями uevents ядра. Для кожної події, systemd-udevd запускає відповідну інструкцію на основі правил у udev.

udev це диспетчер пристроїв ядра Linux. Як наступник devfsd та hotplug, udev в основному працює з пристроями в каталозі /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

Подивимося на відкриті порти системи:

$ sudo netstat -nlput
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 2178/sshd
tcp6 0 0 :::22 :::* LISTEN 2178/sshd

Краса! В Ubuntu 14.04 це виглядало так:

$ sudo apt-get install ntp -y
$ sudo netstat -nlput
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 1380/sshd
tcp6 0 0 :::22 :::* LISTEN 1380/sshd
udp 0 0 10.19.0.6:123 0.0.0.0:* 2377/ntpd
udp 0 0 139.59.256.256:123 0.0.0.0:* 2377/ntpd
udp 0 0 127.0.0.1:123 0.0.0.0:* 2377/ntpd
udp 0 0 0.0.0.0:123 0.0.0.0:* 2377/ntpd
udp6 0 0 fe80::601:6aff:fxxx:123 :::* 2377/ntpd
udp6 0 0 ::1:123 :::* 2377/ntpd
udp6 0 0 :::123 :::* 2377/ntpd

Брр.

/usr/sbin/atd -f
atd запускає завдання, призначені в певний час за допомогою о.

На відміну від cron, який виконує завдання з періодичністю, о одноразово виконує завдання у певний час.

$ echo "touch /tmp/yolo.txt" | now at + 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, де потрібно прикріплювати всі залежності. Я ніколи не користувався snappy для установки або створення додатків на серверах.

sudo apt remove snapd -y --purge


/usr/bin/dbus-daemon
D-Bus — система межпроцессного взаємодії, яка дозволяє додаткам в операційній системі спілкуватися один з одним.

Я так розумію, що це потрібно тільки для домашнього оточення, але навіщо це сервера веб додатків?

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, я віддаю перевагу каталоги /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 потрібен сервер пошти для розсилки.

$ apt-show cron
Package:cron
Версія: 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 — утиліта допомагає вести логи.

Іншими словами, це те, що створює файли у /var/log/, такі як /var/log/auth.log для повідомлень про спроби автентифікації користувача через SSH.

Файли конфігурації тут /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 — демон для вдосконаленого інтерфейсу управління конфігурацією і харчуванням.

acpid потрібен щоб повідомляти користувальницькі програми про події ACPI. За замовчуванням, він запускається при старті системи у фоновому режимі.

ACPI це інтерфейс з відкритим стандартом, який використовують операційні системи для керування апаратними засобами, щоб, наприклад, відключати невикористовувані девайси для економії енергії.

Але у мене віртуальна машина і я не збираюся відключати пристрої. Заради експерименту я спробую видалити його.

sudo apt remove acpid -y --purge

Мені вдалося успішно перезавантажити машину за допомогою reboot, але після halt, Digital Ocean все ще думав, що машина включена і мені довелося вимкнути її через веб інтерфейс провайдера.

Тому, я б залишив цю службу.

/usr/bin/lxcfs /var/lib/lxcfs/
Lxcfs це свого роду оберігає файлова система. В Ubuntu 15.04 вона використовується з двох причин: перше, візуалізувати деякі файли у /proc і друге, обмежити доступ до файлової системи cgroup хоста.

У підсумку, можна створювати контейнери звичним чином з lxc-create і біля контейнера будуть правильні значення uptime, top, і т. д. Файлова система дозволяє контейнера більше вести себе як окрема система, ніж без даної файлової системи.

Якщо не використовуєте контейнери LXC, то можна видалити за допомогою

sudo apt remove lxcfs -y --purge

/usr/lib/accountservice/accounts-daemon
AccountsService надає інтерфейси D-Bus для маніпуляцій з обліковими даними користувачів. Використання інтерфейсів реалізовано в командах usermod(8), useradd(8) та userdel(8).

Коли я видалив D-Bus, це зламало timedatectl. Мені цікаво, що зламається, коли я видалю цю службу.

sudo apt remove accountsservice -y --purge

Час покаже.

/sbin/mdadm
mdadm це утиліта для адміністрування Linux та моніторингу програмних RAID пристроїв.

RAID — технологія віртуалізації даних, яка об'єднує кілька дисків в один логічний елемент. У RAID є 2 основні задачі: 1) збільшення обсягу логічного диска: RAID 0. Якщо об'єднати 2 диски по 500 Гб, то вийде 1 Тб. 2) Уникнути втрату даних якщо один з дисків відмовить: наприклад, RAID 1, RAID 5, RAID 6, RAID 10.

Можна видалити за допомогою:

sudo apt remove mdadm -y --purge

/usr/lib/policykit-1/polkitd --no-debug
polkit це фреймворк авторизації. Я так розумію, що це свого роду sudo і він дозволяє непривілейованим користувачам виконувати певні команди від імені адміністратора, наприклад, перезавантажувати систему.

Але в мене сервер. Можна видалити за допомогою

sudo apt remove policykit-1 -y --purge

Мені досі цікаво, що з-за нього зламається.

/usr/sbin/sshd -D
sshd (OpenSSH Daemon) демон для ssh. З D він не буде переведений в режим роботи демона. Це дозволить легше здійснювати моніторинг sshd.

/sbin/iscsid
iscsid це системна служба, яка запускається у фоновому режимі, працює з конфігурацією iSCSI і управляє з'єднаннями.

Я ніколи не чув про iSCSI:

iSCSI — протокол, який базується на TCP/IP і розроблений для встановлення взаємодії і управління системами зберігання даних, серверами і клієнтами.

Можна видалити з
sudo apt remove open-iscsi -y --purge


/sbin/agetty --noclear tty1 linux
agetty — Linux альтернатива getty.

getty це Unix програма, що працює на системах з фізичними або віртуальними терміналами. При підключенні, вона запитує ім'я користувача і запускає програму login для аутентифікації.

Це дозволяє увійти в систему при фізичному доступі до нього. В Digital Ocean, наприклад, можна відкрити консоль з браузера і підключитися до цього терміналу (здається через VNC).

Раніше, можна було спостерігати як кілька терміналів стартували систему (налаштованих /etc/inittab), але зараз все робить systemd.

Заради експерименту, я видалив файл конфігурації, який запускає і створює agetty:

sudo rm /etc/systemd/system/getty.target.wants/getty@tty1.service
sudo rm /lib/systemd/system/getty@.service

При перезавантаженні сервера, я раніше міг аутентифицироваться через SSH, але не через веб консоль провайдера.


sshd: root@pts/0, -bash і htop
sshd: root@pts/0 означає, що була встановлена SSH сесія для користувача root псевдотерминале (pts) №0.

bash це командна оболонка, яку я використовую. Але чому перед bash варто дефіс? Користувач Reddit під ніком hirnbrot пояснив:

Там стоїть дефіс, тому що запуск його як "-bash", активує login-оболонку. Login-оболонка це така оболонка, у якій перший символ аргументу під номером 0 дефіс, або він запущений з параметром --login. В результаті використовуються різні файли налаштувань.

htop — інтерактивна програма для перегляду процесів, яка зображена на скріншоті.

Після
sudo apt remove lvm2 -y --purge
sudo apt remove at -y --purge
sudo apt remove snapd -y --purge
sudo apt remove lxcfs -y --purge
sudo apt remove mdadm -y --purge
sudo apt remove open-iscsi -y --purge
sudo apt remove accountsservice -y --purge
sudo apt remove policykit-1 -y --purge


Крайня ступінь:
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/getty@tty1.service
sudo rm /lib/systemd/system/getty@.service


Я так само спробував встановити програмне забезпечення по своїй інструкції про автоматичній установці WordPress на Ubuntu Server і все працювало.

Тут nginx, PHP7 і MySQL.


За кадром
Вихідний код
Іноді не досить лише 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

Внизу є посилання на репозиторії з вихідними кодами:

Дескриптори файлів і перенаправлення
Якщо потрібно перенаправити стандартний потік помилок (stderr) в стандартний вихідний потік, потрібно це робити з 2&>1 або 2>&1?

Можна запам'ятати положення амперсанда розумінням того, що echo щось > файл запише щось файл файл. Це теж саме, що echo щось 1> файл. А ось echo щось 2> файл запише потік помилок у файл.

Якщо написати echo щось 2> 1, то потік помилок перенаправится у файл з іменем 1.

Якщо поставити перед 1 амперсанд &, то це буде означати, що 1 це не ім'я файлу, а ідентифікатор потоку. Тому правильно echo щось 2>&1.

Кольори у PuTTY

Якщо деякі елементи зверху не відображаються при використанні PuTTY, то можна це виправити так.

  1. Натисніть правою кнопкою миші на заголовок вікна
  2. Виберіть Change settings
  3. Перейдіть до Window → Colours
  4. Відзначте опцію Both
  5. Натисніть Apply


Командна оболонка C
Спробуємо написати дуже просту командну оболонку на C, яка б використовувала системні виклики fork/exec/wait. Програма shell.c:

#include < stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include < string.h>
#include < sys/wait.h>

int main() {
printf("Ласкаво просимо\n");
char line[1024];

while (1) {
printf("> ");

fgets(line, sizeof(line), stdin);
line[strlen(line)-1] = '\0'; // strip \n
if (strcmp(line, "exit") == 0) // shell built-in
break;

int pid = fork();
if (pid == 0) {
printf("Запуск: %s\n", line);
if (execlp(line, "", NULL) == -1) {
printf("Помилка!\n");
exit(1);
}
} else if (pid > 0) {
int status;
waitpid(pid, &status, 0);
printf("Нащадок вийшов з кодом %d\n", WEXITSTATUS(status));
} else {
printf("Помилка!\n");
break;
}
}

return 0;
}

Скомпилируем програму:

gcc shell.c -o shell

І запустимо її:

$ ./shell
Ласкаво просимо
> date
Запуск: date
Thu Dec 1 14:10:59 UTC 2016
Нащадок вийшов з кодом 0
> true
Запуск: true
Нащадок вийшов з кодом 0
> false
Запуск: false
Нащадок вийшов з кодом 1
> exit

Ви коли-небудь цікавилися чому при запуску програми у фоновому режимі ви бачите, що вона завершила свою роботу, тільки після натискання Enter?

$ sleep 1 &
[1] 11686
$ # натиснули Enter
[1]+ Done sleep 1

Це тому, що оболонка чекає введення. Тільки після вводу команди, оболонка перевіряє стану процесів у фоні і виводить, що вони завершилися.

Від себе
Спасибі, якщо дочитали переклад до кінця. Стаття мені здалася дуже цікавою і корисною. Хочу відзначити, що особисто мені подобаються статті, в яких описується те, про що в них йдеться і ще пояснюється все, що близько. Тут багато тексту, і, можливо, я щось упустив, прибрехав або змусив вас зіщулитися від кострубатого перекладу. Буду радий, якщо Ви повідомите про це в особистому повідомленні, а коментарі залишаться тільки для обговорення по темі статті.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.