규도자 개발 블로그

리눅스, 유닉스 백그라운드프로세스 방법 정리 (&, bg, nohup, screen) 본문

Linux /Unix

리눅스, 유닉스 백그라운드프로세스 방법 정리 (&, bg, nohup, screen)

규도자 (gyudoza) 2022. 2. 17. 01:04

리눅스, 유닉스 백그라운드프로세스 방법 정리 (&, bg, nohup, screen)

서버는 왜 필요할가. 당연히 내가 보고 있지 않아도 서비스를 안전하게 제공하기 위해서다. 백그라운드 프로세스는 왜 필요할까. 당연히 내가 보고 있지 않는 상태에서도 돌아가고 있는 프로세스를 실행하고 싶거나 혹은 여러개의 프로세스를 돌리고 싶기 때문일 것이다. 하지만 백그라운드 프로세스를 돌리는 방법은 많고 각각의 방법은 어떤 특성이 있는지 여기저기 파편화돼있는 정보들을 스스로 취합하지 않으면 언제 어떤 방법을 선택해야할지 알기 힘들다. 그래서 여기에 정리해본다.

 

 

1. &(ampersand)

리눅스기반 os의 명령어에서 흔히 쓰이는 Semicolon(;)과 Pipe(|)의 형제라고 할 수 있다. &연산자 자체는 "앞의 명령어를 백그라운드로 실행하고-"까지의 의미밖에 없다. 그래서

$ echo 1 & echo 2
[1] 19658
1
2
gyu@gyuui-MacBookPro IdeaProjects %
[1]  + done       echo 1

echo 1 & echo 2하면 이렇게 단순하게 1과 2와 출력은 되지만

$ mkdir test & cd test
cd: no such file or directory: test

이렇게 이동은 안 된다. 인간의 언어로는 test를 생성하고- cd이지만 &는 and가 아니라 ampersand이기 때문에 그렇게 동작하진 않는다. (mkdir test && cd test는 앞의 프로세스가 성공하면 실행한다는 선제조건이 있기 때문에 실행된다.)

&의 장점이라고 한다면 실행하기 간단하다는 점이다. 예를 들어 django로 만든 프로젝트에서 python manage.py runserver로 서버를 실행시키면 해당 서버를 실행시킨 터미널은 python script가 포어그라운드로 실행되어 python script를 제외한 다른 입출력은 받을 수 없는 상태가 되는데 뒤에 & 하나 붙이고 실행하면 서버를 실행하고도 다른 명령어를 또 실행할 수 있다.

하지만 위에 썼듯이 리눅스에서 쓰이는 이 ampersand의 의미 그 자체는 "& 앞에 쓰여있는 걸 백그라운드로 실행하고-"의 의미로 끝나기 때문에 그 터미널을 종료하면 해당 프로세스도 같이 종료된다.

 

 

2. bg (feat. jobs)

jobs와 함께 쓰이는 bg이다. 파이썬 스크립트로 예를 들어보자.

from time import sleep

count = 10

while count:
    print(count)
    count -= 1
    sleep(1)

해당 프로세스를 실행하면 10초간 10부터 1까지 출력한 후에 종료되는 스크립트이다. 해당 스크립트를 실행하고 ctrl + z로 프로세스를 '중지'한 뒤 jobs명령어를 통해 현재 실행중인 상태에 있는 작업들의 목록을 확인할 수 있다. 여기서의 중지는 ctrl + c로 하는 '종료'와는 다른 개념이라는 것을 꼭 알아두자.

$ python print_interval.py
10
9
8
^Z # ctrl + z로 중지
zsh: suspended  python print_interval.py
$ jobs
[1]  + suspended  python print_interval.py

중간에 ctrl + z로 종료하고 jobs명령어로 조회한 결과 suspended된 print_interval.py 프로세스를 확인할 수 있다. 여기에서 분기가 갈리는데 fg % 1을 하면 해당 숫자의 job을 다시 포어그라운드로 실행시키고 bg % 1을 하면 해당 숫자의 잡을 다시 백그라운드로 실행시킨다. 이 글의 주제는 백그라운드이니 bg % 1을 하면 해당 작업이 백그라운드에서 진행된다.

하지만 이것 역시도 해당 터미널이 종료되면 같이 종료된다. 왜냐, jobs라는 건 terminal pipe에 귀속되기 때문이다.

이렇게 왼쪽 터미널에서 보이는 job이 오른쪽 터미널에서는 안보이는 걸 확인할 수 있다.

그러니까 이 방법은 결국 실행 -> 중단 -> bg전환이라는 귀찮은 과정을 거침에도 불구하고 터미널이 꺼지면 프로세스도 꺼지게 되어 1번 방법과 별 차이도 없음에도 불구하고 공정이 많아 자주 사용되는 방법은 아니다.

 

자 이제부턴 터미널이 종료되도 계속해서 실행되는 백그라운드 프로세스를 만드는 방법이다.

 

 

3. nohup

이 nohup이라는 것 자체는 스크립트를 데몬형태로 실행시키는 게 전부이다. 그리고 해당 스크립트의 결과물을 nohup.out이라는 파일에 쓰는데 이것을 &와 결합해서 쓰면 해당 프로세스를 포어그라운드로 실행했을 때의 terminal output을 nohup.out이라는 파일에 쓰면서 다른 리눅스 작업을 진행할 수 있다. django의 server시작 명령어와 합치면 이런 모습이 된다.

nohup python manage.py runserver &

해당 명령어로 django서버를 실행시키면 다른 터미널 작업을 연달아 할 수 있을 뿐만 아니라 이 명령어를 실행시킨 터미널을 종료해도 작업이 백그라운드 프로세스로 계속 실행된다.

 

자 마지막은 screen이다.

 

 

4. screen

screen은 어찌보면 백그라운드 프로세스를 만든다기보다는 가상의 스크린(터미널)을 생성하는 것이다. 위에서 계속 설명한 것처럼 터미널 파이프에 프로세스가 귀속되는 것이 리눅스의 기본값이기 때문에 아예 종료되지 않는 터미널을 만들어놓자는 게 이 screen의 기본 컨셉이다.

간단하게 screen이라는 명령어로 실행시킬 수 있다.

그럼 이런 창이 뜨면서 실행된다. enter를 누르면 공허한 터미널이 반겨준다. 터미널에서 나가는 방법은 ctrl + a, d이다. ctrl과 a와 d를 동시에 누르는 게 아니라 ctrl + a를 누르면 명령어를 입력하는 상태가 되는 것이고 거기에서 d를 누르면 screen으로부터 detach되는 것이다.

 

겁주는듯한 라이센스 안내 + 직관적이지 않은 사용법 + 햇갈리는 인터페이스 + 처음 켰을 때의 공허한 터미널 창이라는 안좋은 느낌을 많이 받아서 처음엔 손이 잘 안갔는데 인터넷에서 커스터마이징 할 수 있는 수단을 찾고 나선 조금 정이 붙어서 요즘 애용중이다.

 

계정 profile root directory에 .screenrc라는 파일을 만들어 아래 내용을 붙여넣자.

ck 5000
vbell off
defscrollback 10000
termcapinfo xterm* ti@:te@
startup_message off
hardstatus on
hardstatus alwayslastline
hardstatus string "%{.bW}%-w%{.rW}%n*%t%{-}%+w %= %c ${USER}@%H"
bindkey -k k1 select 0
bindkey -k k2 select 1
bindkey -k k3 select 2

그리고 다시 screen에 들어가보면

이렇게 "아 내가 지금 스크린을 실행중이구나"라고 생각이 들법한 인터페이스가 생성된다.

다른 명령어는 아래와 같다. 아, 스크린 내부에서도 아래 방법을 통해 또 다른 터미널 탭을 생성할 수 있다.

 

  • Ctrl+a, c : 스크린에서 새창 띄우기
  • Ctrl+a, 숫자 : 해당 번호의 스크린으로 이동
  • Ctrl+a, n : 다음 창으로 이동 (Ctrl+a, space와 동일)
  • Ctrl+a, p : 이전 창으로 이동 (Ctrl+a, Backspace와 동일)

이건 스크린 내부에서의 명령어이고 스크린 자체의 명령어는 이렇다.

screen -ls : 현재 생성돼있는 스크린 목록을 확인한다.

gyu@gyuui-MacBookPro ~ % screen -ls
There are screens on:
	20310.ttys002.gyuui-MacBookPro	(Detached)
	20359.ttys019.gyuui-MacBookPro	(Detached)
	20316.ttys002.gyuui-MacBookPro	(Detached)
3 Sockets in /var/folders/y7/s692nclx6nj8t4slh__6db4m0000gn/T/.screen.

그냥 스크린명령어를 때리면 [number].[name].[terminal]형태로 생성되는 걸 확인할 수 있다. 다시 해당 스크린에 접속하는 건 number와 name을 지정해서 할 수 있다.

자주 사용하는 옵션은 아래와 같다.

 

 

스크린 생성 관련

 

  • screen -S [name]: screen에 이름을 지정하여 생성하면서 진입. 중복된 이름이 있더라도 다른 id로 생성돼 진입한다.
  • screen -R [name]: screen에 이름을 지정하여 진입하지만 이미 같은 이름의 스크린이 생성돼있으면 그곳에 접속. 없으면 생성한다.

스크린 접속 관련

 

  • screen -r [number | name]: screen에 재접속. (분리됨). Detached된(아무도 사용하고 있지 않은) screen에만접속할 수 있다.
  • screen -x [number | name]: screen에 재접속. (통합됨). Attached된 screen에도 접속할 수 있다.

여기서 분리됨과 통합됨의 차이는 한 스크린에서의 작용이다. 통합된 -x로 접속하면 해당 화면에 어떤 명령어를 치고 어떤 output이 출력되는지 전부 확인할 수 있다. 화면을 완전 똑같이 공유하고 있다고 보면 된다. 그래서 Attached된 screen에도 접속할 수 있는 것이다.

screen -x 명령어를 통해 같은 터미널에 접속한 모습. 한쪽에 명령어를 써도 자동으로 싱크가 된다.

 

스크린 종료 관련

 

  • screen -X -S [number] kill
  • 혹은 종료하고 싶은 스크린에 접속해서 exit명령어로 해당 스크린의 터미널을 종료한다.

 

숙련된 엔지니어는 상황에 맞는 최선의 기술과 방법을 선택하여 적용하는 사람이라고 생각한다. 리눅스 백그라운드 프로세스를 구성하고 관리하는 방법도 이렇게 다양하니 각각 방법을 상황에 맞게 적용해보면 좋을 것이다.

Comments