Saturday, 4 April 2020

disown, reptyr, nohup, tmux, screen - chạy job ở background

Khi chạy các câu lệnh command line, đặc biệt khi đang SSH vào server, ta thường nảy ra nhu cầu: cho câu lệnh tiếp tục chạy nhưng không cần phải kết nối vào server, hay tắt terminal đi.

Cách làm truyền thống là dùng lệnh `nohup` (no hang up) khi gọi câu lệnh. Câu lệnh sẽ tiếp tục chạy dù tắt terminal hay thoát khỏi server. Cú pháp:
nohup câu_lệnh_như_bình_thường

Vấn đề ở chỗ: dùng nohup yêu cầu ta phải tính trước chuyện này, phải gõ nohup trước câu lệnh cần chạy, nếu nhỡ quên nohup mà đang chạy dở chừng thì làm sao?

disown là giải pháp
tmux/screen là giải pháp gián tiếp.

disown là gì?

disown là built-in command của các shell hay dùng: bash, zsh, ksh, không phải cài đặt gì cả.

 $ man bash | grep '    disown' -A4
       disown [-ar] [-h] [jobspec ... | pid ... ]
              Without options, remove each jobspec from the table of active jobs.  If jobspec is not present, and neither the -a nor the -r option is supplied, the current job is used.  If the -h option is given, each jobspec is not removed from the table, but is marked so that SIGHUP is not sent  to  the  job if the shell receives a SIGHUP.  If no jobspec is supplied, the -a option means to remove or mark all jobs; the -r option without a jobspec argument restricts operation to running jobs.  The return value is 0 unless a jobspec does not specify a valid job.

jobs , background job, foreground job

Khi chạy 1 câu lệnh trên bash, câu lệnh đó sẽ giữ quyền điều khiển, ta không thể chạy câu lệnh nào khác cho đến khi câu lệnh đang chạy chạy xong, ví dụ sau chạy wget để tải 1 flle cài OpenBSD

$ wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs
--2020-04-04 19:25:55--  https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs
Resolving cdn.openbsd.org (cdn.openbsd.org)... 151.101.10.217
Connecting to cdn.openbsd.org (cdn.openbsd.org)|151.101.10.217|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 472317952 (450M) [application/octet-stream]
Saving to: ‘install66.fs’

install66.fs                              0%[                                                                              ]   1011K   207KB/s    eta 42m 1s ^Z
[1]+  Stopped                 wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs

Bấm ctrl Z (^Z) để dừng process đang chạy này lại, trả quyền điều khiển về cho bash. Vậy câu lệnh đang chạy nó đi đâu mất rồi?
bash có chứa một danh sách các chương trình đang chạy này và gọi là `job`.
Gõ lệnh jobs để hiện danh sách các active job:

$ jobs
[1]+  Stopped                 wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs
process này hiện ở trạng thái "stopped/suspend", nó không chạy mà đang bị "tạm dừng".
Gõ bg kèm số jobs (ở trên có thấy là thấy số 1), vậy gõ bg %1 để chạy job này ở "background".

 $ bg %1
[1]+ wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs &
Redirecting output to ‘wget-log’.

Giờ gõ lại `jobs` sẽ thấy chữ "Running" chứ không phải "Stopped", và process được chạy trở lại.

$ jobs
[1]+  Running                 wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs &

Để chuyển chương trình này về foreground, chiếm lại "sân khấu", gõ fg %1:

$ fg %1
wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs
--2020-04-04 19:25:55--  https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs
Resolving cdn.openbsd.org (cdn.openbsd.org)... 151.101.10.217
Connecting to cdn.openbsd.org (cdn.openbsd.org)|151.101.10.217|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 472317952 (450M) [application/octet-stream]
Saving to: ‘install66.fs’


2020-04-04 19:47:01 (602 KB/s) - Read error at byte 23899568/472317952 (Success). Retrying.

--2020-04-04 19:47:02--  (try: 2)  https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs
Connecting to cdn.openbsd.org (cdn.openbsd.org)|151.101.10.217|:443... connected.
HTTP request sent, awaiting response... 206 Partial Content
Length: 472317952 (450M), 448418384 (428M) remaining [application/octet-stream]
Saving to: ‘install66.fs’

install66.fs                              5%[+++>                                                                          ]  23,99M   148KB/s    eta 49m 8s ^Z
[1]+  Stopped                 wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs

Một process khi ở trạng thái stopped/suspend, nếu xem trong bảng ps/top, sẽ thấy trạng thái là T to:
 
$ ps xau | grep wget
hvn      30707  0.0  0.0  49628  6280 pts/0    T    19:25   0:00 wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs

  T = stopped by job control signal

Một cách khác để thay đổi trạng thái T này sang chạy, là gửi signal: SIGCONT

$ ps xau | grep wget
hvn      30707  0.0  0.0  49628  6280 pts/0    T    19:25   0:00 wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs

$ kill -SIGCONT 30707
Redirecting output to ‘wget-log’.

$ ps xau | grep wget
hvn      30707  0.0  0.0  49628  6280 pts/0    S    19:25   0:00 wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs

Disown


Gõ disown %1 để bỏ jobs này khỏi list jobs:
$ disown %1
$ jobs
$ ps auwwxf | grep -B2 wget
hvn      14428  0.0  0.0  31816  6064 pts/1    Ss+  15:22   0:00  |   \_ bash
hvn      19332  0.0  0.2  43044 17536 pts/0    Ss   17:57   0:01  |   \_ bash
hvn      30707  0.0  0.0  49628  6280 pts/0    S    19:25   0:01  |   |   \_ wget https://cdn.openbsd.org/pub/OpenBSD/6.6/amd64/install66.fs

Giờ tắt terminal đi, hay thoát khỏi server khi đang SSH thì process này vẫn cứ tiếp tục chạy.

Khi tắt terminal, hay thoát khỏi server, bash shell sẽ nhận được SIGHUP, và mặc định nó sẽ truyền SIGHUP này tới tất cả process con của nó để tắt hết đi. Disown 1 job sẽ bỏ qua job đó và không truyền SIGHUP trong tương lại. Khi thoát khỏi shell, process bash kết thúc, process con bị mất parent trở thành orphan process, thường sẽ được system manager PID 1 nhận làm process con.

/* Cause all jobs, running or stopped, to receive a hangup signal.  If
   a job is marked J_NOHUP, don't send the SIGHUP. */
void
hangup_all_jobs ()
{
  register int i;

  /* XXX could use js.j_firstj here */
  for (i = 0; i < js.j_jobslots; i++)
    {
      if (jobs[i])
 {
   if  (jobs[i]->flags & J_NOHUP)
     continue;
   killpg (jobs[i]->pgrp, SIGHUP);
   if (STOPPED (i))
     killpg (jobs[i]->pgrp, SIGCONT);
 }
    }
}
http://git.savannah.gnu.org/cgit/bash.git/tree/jobs.c?h=bash-4.4-testing#n1414

https://github.com/att/ast/blob/683bccf3bab8545b6334ab7b7c179e08f5eb89fa/src/cmd/ksh93/sh/jobs.c#L981

reptyr

 Ngược lại với disown, reptyr  giúp "chiếm" lại quyền điều khiển process sau khi bị disown.

$ apt-cache search reptyr
reptyr - Tool for moving running programs between ptys

Screen và Tmux

Thay vì phải nohup,disown, thì một giải pháp khác là luôn chạy lệnh trong tmux/screen. Câu lệnh đầu tiên sau khi SSH vào server là gõ tmux.
Tmux hay screen sẽ tiếp tục chạy câu lệnh dù kết nối đến server bị đứt, hay thoát khỏi terminal.

Tham khảo
- man bash
- https://superuser.com/questions/184047/bash-zsh-undoing-disown

Hết
HVN at https://pymi.vn và https://www.familug.org
Đăng ký học Python tại https://pymi.vn

No comments:

Post a comment