다시 시작하는 리눅스 #11 프로세스와 잡

최동민·2022년 12월 30일
0

Linux

목록 보기
20/25

리눅스는 프로그램을 여러 개 동시에 돌릴 수 있는 멀티태스킹 기능을 지원한다.
파일을 복사하는 도중 텍스트 파일을 편집할 수도 있다.
셸에서 프로그램 여러 개를 실행하고 정지하는 방법, 멀티태스킹 기능을 활용하는 방법을 알아보자.

프로세스란

지금까지 여러 명령어를 사용해보았다. 이들 명령어의 실체는 디스크에 있는 파일이다.
셸에서 명령어를 입력하면 커널이 디스크에 있는 해당 파일을 읽어서 메모리에 올린 뒤 CPU가 프로그램을 실행한다. 여기서 메모리 위에 올린 프로그램을 프로세스라 한다.

같은 프로그램을 실행하더라도 각 프로세스는 별도의 메모리 영역을 가진다.
예를 들어 ls 명령어를 동시에 여러 번 실행해도 각 프로세스가 내부적으로 사용하는 데이터가 섞일 일은 없다. 그리고 파일 소유자 외에는 해당 파일에 대한 조작 권한이 제한되는 것과 비슷하게 프로세스를 실행한 사용자 외에는 해당 프로세스를 조작할 수 있는 권한이 제한된다.

리눅스 커널은 각 프로세스에게 프로세스 ID 라는 고유한 번호를 할당해서 관리한다.
프로세스를 관리하는 것은 리눅스 커널의 중요한 기능 중 하나이다.
그리고 리눅스에서 새로운 프로세스는 이미 기존에 존재하는 별도의 프로세스를 기반으로 만들어진다.
여기서 새로운 프로세스를 만드는 프로세스를 부모 프로세스라고 하며, 새롭게 만들어지는 프로세스를 자식 프로세스라고 한다. 예를 들어 셸에서 ls 명령어를 실행하면 셸이 부모 프로세스이며, ls 명령어는 자식 프로세스가 된다.

ps : 프로세스 목록 표시

ps 명령어는 현재 시스템에서 실행 중인 프로세스의 목록을 출력한다.
아무런 인자 없이 ps를 실행하면 현재 접속한 터미널에서 실행한 프로세스만을 출력한다.

위 예에서 PID프로세스 ID를 의미하며, CMD는 실행한 명령어를 의미한다.
즉, 프로세스 ID가 10344인 bash, 14610인 bash, 15142인 ps가 실행 중인 것이다.
bash는 현재 실행 중인 셸이며, ps는 방금 실행한 명령어 자신이다.
프로세스 ID는 프로세스가 종료하기 전까지 바뀌지 않는다. 따라서 특정 프로세스를 지정할 때 프로세스 ID를 사용한다.

현재 접속한 터미널 이외의 프로세스를 출력하고 싶은 경우

다른 터미널에서 실행 중인 프로세스나 터미널과 무관하게 돌아가는 프로세스인 데몬(daemon)을 출력하려면 x 옵션을 사용한다. 그러면 사용자가 현재 실행 중인 모든 프로세스를 확인할 수 있다. 또 프로세스 간 부모 자식 관계를 표시하는 f 옵션을 함께 실행해본다.

위와 같이 프로세스 ID(PID)가 14610인 -bash 아래에 방금 실행한 ps -xf가 있는 식의 프로세스들 간의 부모 자식 관계를 확인할 수 있다.

TTY는 터미널을 의미한다. pts/0 이라는 터미널을 사용하고 있다는 것이고, ?는 해당 프로세스가 터미널에 접속되어 있지 않은 데몬임을 의미한다.

옵션 형식

앞서 ps 명령어 실행 시 옵션에 하이픈(-)을 사용하지 않았다. 리눅스의 ps 명령어는 역사적인 이유로 두 종류의 옵션 형식을 제공한다.

  • UNIX 옵션 : - 을 사용하여 옵션을 지정한다 예) ps -aef
  • BSD 옵션 : - 없이 옵션을 지정한다. 예) ps xf

하이픈(-) 유무에 따라 옵션 문자가 가지는 의미가 전혀 달라서 완전히 다른 옵션 체계라고 볼 수 있다. 여기서는 BSD 옵션을 사용한다.

모든 프로세스 표시

사용자가 실행한 프로세스 외에도 시스템을 위해 동작 중인 프로세스들이 있다.
이러한 프로세스들은 대부분 슈퍼 사용자의 권한으로 실행된다. 함께 확인하려면 a 옵션도 추가해준다.

보통 리눅스가 기동하면 프로세스 수십 개가 돌아간다. 이처럼 리눅스는 멀티태스킹을 지원하여 다양한 프로세스가 동시에 돌아간다는 것을 기억하자.

ps 옵션
x : ps 명령어를 실행한 사용자의 프로세스를 출력
ux : ps 명령어를 실행한 사용자의 프로세스를 상세하게 출력
ax : 모든 사용자의 프로세스를 출력
aux : 모든 사용자의 프로세스를 상세하게 출력
auxww : aux 옵션의 출력 결과가 화면에 잘리지 않도록 출력

앞서 살펴본 프로세스는 리눅스 커널의 입장에서 바라본 처리 단위이다.
한편 셸에서 바라본 처리 단위를 잡(job)이라고 한다.
셸의 커맨드 라인에 입력한 한 행이 곧 잡 하나에 해당한다.

커맨드 라인에서 명령어 하나만을 입력했다면 프로세스와 잡이 같다.
복수의 명령어를 파이프로 연결했다면 프로세스는 명령어마다 만들어지지만 잡은 하나만 만들어진다.
ex) ls -l / | cat -n | less
생성되는 프로세스는 3개, 잡은 1개

프로세스에는 시스템 전체에 걸쳐 유일한 값이 프로세스 ID로 할당된다.
한편 잡은 셸 별로 관리된다. 따라서 터미널 에뮬레이터 여러 개를 사용해서 셸 여러 개를 사용하면 중복된 잡 번호가 할당될 수 있다.
잡을 일시 정지하거나 백그라운드에서 실행할 수 있다. 즉, 여러 작업을 병행시켜 효율적으로 처리할 수 있다.

배시의 man 페이지를 읽는 것과 ~/.bashrc 파일을 편집하는 두 작업을 효율적으로 진행하는 방법을 실습해보자.

명령어를 일시 정지하는 방법

man bash 로 배시의 메뉴얼을 출력 후

Ctrl + z를 입력하면 다음과 같이 정지 표시와 함께 프롬프트가 표시된다.
즉, 셸에 다시 명령어를 입력할 수 있게 되는데, man 명령어가 종료된 것이 아니라 정지 상태가 된 것이다.

이 상태에서 vim ~/.bashrc 에디터를 기동해본다.

Ctrl + z를 입력하여 이 또한 정지할 수 있다.

그리고 jobs 명령어로 현재 잡 목록을 확인할 수 있다.
[1]이나 [2]는 잡 번호를 의미하며, 둘 다 정지된 상태이다.

잡 번호뿐만 아니라 프로세스 ID도 표시하려면 -l 옵션을 지정

fg : 잡을 포그라운드로 전환

사용자의 입력을 받아들일 수 있는 잡의 상태를 포그라운드(foreground)라고 한다.
정지 상태에 있는 bash 메뉴얼을 다시 확인하기 위해서는 포그라운드로 되돌려야 한다.
fg %<잡 번호>
man bash의 잡 번호가 1이었으므로 fg %1로 실행

그러면 이전에 작업하던, 혹은 읽었던 부분에서부터 다시 볼 수 있으며 키보드로 조작할 수도 있다. 이처럼 fg 명령어를 사용하여 재개가 가능하다.

fg 명령어에서 잡 번호를 생략하면 현재 잡(jobs 명령어에서 +로 표시된 잡)이 포그라운드가 된다.

bg : 잡을 백그라운드로 전환

file1 파일을 만든 후 임의의 크기의 파일을 만드는 설정을 하여 파일 크기를 크게 한다.
그리고 cp 명령어 실행 중 Ctrl + z 를 실행해보면 복사 명령어 처리가 중지된다.
이러한 경우에는 잡을 멈추지 않은 채 셸로 돌아가는 것이 좋다.
포그라운드와 반대로 사용자가 조작할 수 없는 잡의 상태를 백그라운드(background) 라 한다.

bg %<잡 번호>

cp 명령어를 백그라운드로 돌린다. 프롬프트가 표시되어 셸을 사용할 수 있게 되었지만 여전히 복사 작업은 백그라운드에서 실행되고 있다. 이처럼 긴 시간이 걸리는 작업을 백그라운드로 실행하면 작업이 끝나는 것을 기다리지 않고도 다른 작업을 진행할 수 있다.
백그라운드에서 실행 중인 잡도 jobs 명령어로 상태 확인이 가능하다.
마지막에 표시된 &jobs이 백그라운드로 돌고 있음을 의미한다.

말했듯이 긴 시간이 걸리는 작업을 진행한다면 처음부터 백그라운드에서 실행하면 효율적일 것이다. 그럴 때는 커맨드 라인의 마지막에 &을 추가한다.

정리
포그라운드 : 사용자가 조작하면서 실행 중인 상태
백그라운드 : 사용자가 조작할 수 없지만 실행 중인 상태
정지 : 처리가 일시적으로 중단된 상태

명령어 실행 중 중지한 후 bg로 돌리고 (혹은 애초에 bg로 명령어 실행), jobs로 상태 체크하고 조작할 수 있는 환경인 fg로 다시 돌리는 식의 작업 형태를 이룰 수 있겠다.

참고로 잡에 대한 조작을 위해 알아본 명령어들은 전부 셸의 내장 명령어이다.

프로세스는 리눅스 커널의 처리 단위이며 잡은 셸의 처리 단위라 하였다.
프로세스에 부여된 프로세스 ID를 확인하여 조작해도 되지만, 셸과 결부된 보다 상위 개념인 잡을 조작하는 것이 편리한 경우가 많다.

잡과 프로세스의 종료

리눅스를 다루다 보면 명령어를 잘못 입력하거나 실행 중인 프로그램이 비정상적으로 동작하여 강제 종료를 해야 하는 경우가 발생한다.

잡 종료

잡 상태에 따라 잡을 종료하는 방법이 다르다.
Ctrl + c : 포그라운드로 실행 중인 잡을 종료하기 위해서 사용한다. 많은 프로그램이 이 명령어를 입력받으면 종료되도록 설계되어 있다.

kill %<잡 번호> : 백그라운드에서 실행 중인 잡을 종료하려면 잡 번호를 지정하여 kill 명령어를 실행한다.

다음과 같이 백그라운드 잡이 세 개인 경우 잡의 번호를 지정하여 해당 잡을 종료시킬 수 있다.

프로세스 종료

프로세스를 종료할 때도 백그라운드 잡과 마찬가지로 kill 명령어를 사용한다.
kill <프로세스 ID>
프로세스의 경우에는 %를 붙이지 않고 프로세스 ID만을 지정한다.
다만 다른 사용자가 실행 중인 프로세스는 함부로 종료할 수 없다.
하지만 예외적으로 슈퍼 사용자는 모든 사용자의 프로세스를 강제 종료할 수 있다.
대부분의 서비스 애플리케이션은 단말과 분리되어 실행되어 잡 번호를 가지지 않기 때문에 이렇게 프로세스를 종료하는 법을 반드시 알아두어야 한다.

kill : 시그널 전송

kill 명령어는 사실 잡이나 프로세스를 종료하는 명령어가 아니라 시그널을 전송하는 명령어이다.
시그널이란 말 그대로 프로세스에게 전송되는 신호이다. 프로세스는 전달받은 시그널의 종류에 따라 종료, 정지, 재기동 등의 처리를 수행한다. 즉, 시그널을 통해 프로세스에게 메시지를 보내는 것.
전송할 시그널의 종류는 kill -<시그널 이름>이렇게 지정할 수 있으며 지정하지 않을 시에는 기본값으로 TERM이라는 시그널이 전송된다.
kill 4695 kill -TERM 4695 이 두 명령어는 동일한 의미이다.
시그널에는 고유 번호도 있어 TERM 시그널의 경우 15번, kill -15 4695로 활용할 수 있다.

TERM 시그널은 terminate, 종료 시그널을 의미한다.
포그라운드에서 동작하는 잡에게 입력한 Ctrl + z, Ctrl + c도 사실 내부적으로 시그널을 전송한다. 각각 TSTP, INT라는 시그널을 전송하며 이에 따라 잡이 정지 혹은 종료한다.

kill -l 명령어로 시그널의 전체 목록을 확인할 수 있으며, 이때 시그널 이름 앞에 SIG 라는 문자열이 붙어 표시된다.

9) SIGKILL 은 다소 예외적인데, 이 시그널만큼은 프로세스에 전달되지 않고 리눅스 커널이 처리를 한다. 죽, TERM 시그널을 받아도 종료하지 않는 상태에 빠진 프로세스를 종료하기 위해 리눅스 커널은 이 시그널을 받으면 지정한 프로세스를 강제로 종료한다.
이는 마지막 수단이 되어야 한다. 프로그램의 구현에 따라 보통 TERM 시그널을 수신 받으면 현재 상태를 보존하거나 임시 파일을 지우는 등 종료 전에 수행해야 할 작업을 수행하고 종료하도록 하는데, KILL 시그널은 이러한 종료 처리를 수행하지 않고 곧바로 프로세르가 종료된다.

profile
코드를 두드리면 문이 열린다

0개의 댓글