본문 바로가기
리눅스/Part 1 - Learning The Shell

리눅스 기초 | 10-1. 프로세스

by 객잔주인 2024. 5. 31.

해당 포스팅은 William E. Shotts, Jr.의 오픈소스 저서 The Linux Command Line(링크)을 기반으로 작성되었습니다.


현대의 운영체제는 대부분 멀티태스킹을 지원합니다. 멀티태스킹은 하나의 컴퓨터가 동시에 여러 작업을 처리하는 것 처럼 보이게 하는 기능입니다. 실제로는 운영체제가 여러 프로그램간에 매우 빠르게 전환하면서 각 프로그램을 실행시키는 뒷작업(?)이 진행중인 것이죠. 리눅스 커널은 프로세스(process)를 통해 멀티태스킹을 수행합니다. 프로세스는 실행 중인 프로그램을 의미합니다. 커널은 프로세스 스케줄링, 즉 어떤 프로세스를 언제 실행할지 결정하는 역할을 합니다. 커널은 스케줄링 알고리즘을 사용하여 CPU를 여러 프로세스에 할당함으로써 시스템이 원활하고 빠르게 동작하도록 합니다.

 

때로는 컴퓨터가 느려지거나 애플리케이션이 응답하지 않는 경우가 일어납니다. 이번 장에서는 실행중인 프로그램을 모니터링하고 오동작하는 프로세스를 종료하는 커맨드 라인 도구들을 알아보겠습니다.

 

이번 장에서 소개할 명령어들은 다음과 같습니다

  • $\texttt{ps}$ - 실행중인 프로세스의 스냅샷을 보고
  • $\texttt{top}$ - 실행 중인 작업을 실시간으로 표시
  • $\texttt{jobs}$ - 현재 쉘 세션에서 실행 중인 작업의 목록을 표시
  • $\texttt{bg}$ - 특정 작업을 백그라운드로 이동
  • $\texttt{fg}$ - 백그라운드나 일시 중단된 작업을 포어그라운드로 가져옴
  • $\texttt{kill}$ - 특정 프로세스에 시그널을 보내 종료하거나 다른 행동을 취하게 함
  • $\texttt{killall}$ - 이름을 기준으로 프로세스를 종료
  • $\texttt{shutdown}$ - 시스템을 종료하거나 재부팅함

프로세스 작동 방식

컴퓨터가 부팅될 때, 커널이 가장 먼저 메모리에 로드되고 실행됩니다. 커널이 시스템을 시작한 후 $\texttt{init}$ 프로그램을 실행합니다. $\texttt{init}$은 시스템의 첫 번째 프로세스(PID 1)로서, 다른 모든 사용자 프로세스의 부모가 됩니다. $\texttt{init}$은 $\texttt{/etc}$ 디렉토리에 위치한 여러 쉘 스크립트를 실행합니다. 이 스크립트들을 $\texttt{init 스크립트}$라고 하며, 시스템 서비스들을 시작합니다. 예를 들어, 네트워크 설정, 파일 시스템 마운트, 로그인 프로세스 설정 등이 이에 해당합니다. 많은 시스템 서비스들은 데몬(daemon) 프로그램으로 구현됩니다. 데몬은 사용자 인터페이스 없이 백그라운드에서 동작하는 프로그램으로, 시스템 로그 기록, 네트워크 요청 처리, 파일 시스템 감시 등을 수행합니다. 사용자가 로그인하지 않았어도 시스템은 데모과 다른 서비스들이 지속적으로 작업을 수행함으로써 바쁘게 동작하고 있습니다.

 

프로그램은 또 다른 프로그램을 실행시킬 수 있는데 이를 "부모 프로세스(parent process)자식 프로세스(child process)를 생성한다"고 표현합니다.

 

커널은 시스템의 모든 프로세스에 대한 정보를 유지하고 관리하는 역할을 합니다. 예를 들어, 각 프로세스에는 고유한 번호가 할당되며 이 번호를 프로세스 ID (PID)라고 합니다. PID는 오름차순으로 할당되며, 시스템의 첫 번째 프로세스인 $\texttt{init}$은 항상 PID 1을 받습니다. 또한 커널은 각 프로세스에 할당된 메모리를 추적 및 파악하며 각 프로세스의 실행 준비 상태를 추적하여, 프로세스가 언제 스케줄링 될  수 있는지 결정합니다. 파일처럼 프로세스도 소유자가 있으며 UID, eUID 등의 식별자를 가집니다.

프로세스 확인

프로세스를 확인하는 방법들 중 가장 흔히 사용되는 것은 $\texttt{ps}$입니다. $\texttt{ps}$는 다양한 옵션이 있지만 간단하게는 이렇게 사용됩니다:

위 결과는 두 개의 프로세스를 보여줍니다. 프로세스 5198과 프로세스 10129는 순서대로 $\texttt{bash}$와 $\texttt{ps}$를 나타내죠. $\texttt{ps}$의 출력 결과는 그렇게 많은 것을 보여주는 것 같지 않네요. 더 많은 정보를 보기 위해서는 옵션들을 추가해주면 됩니다. 추가 옵션들을 사용하기 전에 $\texttt{ps}$의 필드들을 살펴보겠습니다. $\texttt{TTY}$는 "teletpye"의 약자로, 프로세스의 제어 터미널(controlling terminal)을 의미합니다. 각 프로세스는 특정 $\texttt{TTY}$에 연결되어 있으며, 이는 해당 프로세스가 어느 터미널 세션에서 실행되고 있는지를 나타냅니다. $\texttt{TIME}$ 필드는 프로세스가 사용한 CPU 시간(CPU time)의 총량을 보여줍니다. 이 시간은 프로세스가 실제로 CPU에서 작업을 수행하는 데 사용된 시간을 의미합니다. 이 예에서는 두 프로세스 모두 컴퓨터에 큰 부하를 주지 않는 것으로 나타나 있습니다.

 

$\texttt{ps}$에 옵션을 추가하면 시스템이 실행하는 작업들에 대한 더 많은 정보를 확인할 수 있습니다.

"x" 옵션(앞에 대시를 사용하지 않습니다)을 추가하면 사용자가 소유한 모든 프로세스를 보여줍니다. 이 옵션은 터미널과 관계없이 실행 중인 모든 프로세스를 나열하도록 합니다. $\texttt{TTY}$ 열에서 "?" 기호가 보인다면 해당 프로세스가 특정 제어 터미널에 속하지 않는다는 의미입니다. 즉, 그 프로세스는 어떤 터미널에서도 제어되지 않고 독립적으로 실행되고 있다는 것을 나타냅니다.

 

시스템은 많은 프로세스가 동시에 실행되고 있어 $\texttt{ps}$ 명령어를 사용하면 긴 프로세스 목록을 출력합니다. $\texttt{ps}$ 명령의 출력을 $\texttt{less}$로 파이프하면 긴 목록을 쉽게 볼 수 있습니다. $\texttt{ps}$ 명령어의 옵션 조합에 따라 출력되는 각 행이 매우 길어질 수 있습니다. 이럴 때는 터미널 에뮬레이터 창을 최대화하여 보는 것이 좋습니다.

 

$\texttt{ps x}$의 출력 결과를 보면 $\texttt{STAT}$이라는 새로운 열이 생겼습니다. $\texttt{STAT}$은 "state"의 약어이며 프로세스의 현재 상태를 보여줍니다.

 

Table 10-1: 프로세스 상태

상태 의미
$\texttt{R}$ 프로세스가 실행중이거나 실행될 준비가 되었음을 의미
$\texttt{S}$ 프로세스가 실행중이 아님. 특정 이벤트(예: 키 입력, 네트워크 패킷 등)를 기다리는 상태. CPU를 사용하지 않음
$\texttt{D}$ 프로세스가 I/O 작업(예: 디스크 드라이브 접근)을 기다리는 상태. 이 상태는 중단할 수 없음
$\texttt{T}$ 프로세스가 중지된 상태. 프로세스를 일시 중지했을 때 발생
$\texttt{Z}$ 좀비 프로세스라고도 하며, 종료되었지만 부모 프로세스가 이를 정리하지 않은 상태. 시스템 리소스를 거의 사용하지 않지만, 프로세스 테이블에서 여전히 자리를 차지
$\texttt{<}$ 높은 우선순위의 프로세스. 이 프로세스는 CPU 시간을 더 많이 할당 받으며, 이는 niceness라는 프로세스 특성에 의해 결정됨. '덜 nice'한 프로세스는 다른 프로세스보다 더 많은 CPU 시간을 차지함
$\texttt{N}$ 낮은 우선순위의 프로세스. 'nice'한 프로세스라고도 하며, 다른 높은 우선순위의 프로세스가 서비스를 받은 후에야 CPU 시간을 할당받게 됨

 

프로세스 상태에 따라 추가로 붙은 수 있는 문자들이 있으며, 이는 다양한 특수한 프로세스 특성을 나타냅니다. 자세한 내용은 $\texttt{ps}$의 매뉴얼 페이지를 읽어보세요.

 

많이 사용하는 다른 옵션 세트는 "aux"(a, u, x 각각이 개별 옵션)입니다. 이 옵션은 훨씬 더 많은 정보를 보여줍니다.

이 옵션 세트는 다양한 사용자와 관련된 프로세스를 더욱 상세하게 보여줍니다. 옵션을 대시(-) 없이 사용하면 "BSD 스타일"로 동작합니다. 이는 BSD Unix에서 사용되는 $\texttt{ps}$ 명령어의 동작 방식을 모방한 것입니다. 리눅스의 $\texttt{ps}$ 명령어는 여러 UNIX 구현의 $\texttt{ps}$ 프로그램의 동작을 모방할 수 있습니다. 이 옵션 세트를 사용하면 더 많은 열의 정보가 출력됩니다.

 

Table 10-2: BSD 스타일의 $\texttt{ps}$ 열

헤더 의미
$\texttt{USER}$ 프로세스 소유자의 사용자 ID
$\texttt{%CPU}$ 프로세스의 CPU 사용률
$\texttt{%MEM}$ 프로세스의 메모리 사용률
$\texttt{VSZ}$ 프로세스가 사용 중인 가상 메모리의 크기. 킬로바이트(KB) 단위
$\texttt{RSS}$ 프로세스가 사용 중인 물리적 메모리의 크기. 킬로바이트(KB) 단위
$\texttt{START}$ 프로세스가 시작된 시간. 24시간 이상 경과한 프로세스의 경우, 날짜로 표시됨

 

$\texttt{top}$ - 동적 프로세스 모니터링

$\texttt{ps}$ 명령어는 명령어가 실행되는 시점에 실행 중인 프로세스의 스냅샷을 출력합니다. 반면, $\texttt{top}$ 명령어는 시스템의 활동을 실시간으로 모니터링할 수 있게 해줍니다.

$\texttt{top}$ 프로그램은 실시간으로 시스템의 활동을 모니터링할 수 있게 해주며, 기본적으로 3초마다 시스템의 상태를 업데이트하여 표시합니다. $\texttt{top}$ 프로그램은 시스템의 프로세스를 CPU 활동 순으로 정렬하여 보여줍니다. 가장 많은 CPU 자원을 사용하는 프로세스가 상단에 표시됩니다. $\texttt{top}$의 출력은 크게 두 부분으로 구성됩니다: 화면의 상단에는 시스템 요약 정보가 표시됩니다. 여기에는 시스템의 전반적인 상태를 보여주는 다양한 통계가 포함됩니다; 화면의 하단에는 현재 실행중인 프로세스 목록이 테이블 형태로 표시됩니다.

시스템 요약은 유용한 정보를 잔뜩 가지고 있습니다. 아래 표에서 디테일한 부분을 설명하겠습니다:

 

Table 10-3: $\texttt{top}$ 정보 필드

필드 의미
1 $\texttt{top}$ 프로그램의 이름
  $\texttt{14:59:20}$ 현재 시간
  $\texttt{up 6:30}$ 업타임(uptime). 마지막 부팅 이후 지난 시간. 예시에서는 6시간 30분이 흐름
  $\texttt{2 users}$ 2명의 사용자가 로그인 되어있음
  $\texttt{load average:}$ 로드 평균(load average)은 일정 시간 동안 시스템에서 실행 대기중인 프로세스의 평균 수를 나타냄. 실행 대시 중인 프로세스란 CPU에서 실행되기를 기다리는 프로세스를 의미. 로드 평균은 세 가지 값으로 표시됨: 첫 번째 값은 1분 평균, 두 번째 값은 5분 평균, 세 번째 값은 15분 평균을 나타냄.
2 $\texttt{Tasks:}$ 프로세스의 수와 프로세스 상태를 요약
3 $\texttt{Cpu(s):}$ CPU 사용 상태를 실시간으로 보여줌
  $\texttt{0.7%us}$ 사용자 프로세스(user process)를 위해 0.7%의 CPU가 사용중임을 나타냄. 커널 외부의 프로세스를 의미
  $\texttt{1.0%sy}$ 시스템 (커널) 프로세스가 1.0%의 CPU를 사용중임을 나타냄
  $\texttt{0.0%ni}$ 0.0%의 CPU가 "nice"한(우선순위가 낮은) 프로세스에 사용되고 있음을 나타냄
  $\texttt{98.3%id}$ CPU의 98.3%가 사용되지 않고 남아있는 것을 나타냄
  $\texttt{0.0%wa}$ CPU의 0.0%가 I/O 작업을 기다리고 있음을 나타냄
4 $\texttt{Mem:}$ 사용중인 물리적 램(RAM)을 보여줌
5 $\texttt{Swap:}$ 스왑 공간(가상 메모리) 사용 상태를 보여줌

 

$\texttt{top}$ 프로그램은 여러 키보드 명령어를 입력받을 수 있습니다. 주요 키보드 명령어는 $\texttt{top}$ 프로그램의 도움말을 표시하는 $\texttt{h}$와 $\texttt{top}$을 종료하는 $\texttt{q}$가 있습니다.

 

주요 데스크탑 환경(GNOME, KDE)에서는 $\texttt{top}$과 유사한 정보를 제공하는 그래픽 애플리케이션을 제공합니다. 예를 들어, 윈도우의 작업 관리자와 비슷한 기능을 수행합니다. 그러나 그래픽 애플리케이션은 시스템 리소스틀 더 많이 소비할 수 있습니다. 이는 시스템 성능을 모니터링 할 때 그래픽 애플리케이션이 성능 저하의 원인이 될 수 있음을 의미합니다.

 

프로세스 제어

! 이번 절에서는 리눅스 데스크탑 버전을 요구하는 실습이 진행됩니다.

프로세스를 모니터링하는 방법을 배웠으니 제어하는 방법도 알아보겠습니다. 실습을 위해 $\texttt{xlogo}$라는 작은 프로그램을 사용하겠습니다. $\texttt{xlogo}$ 프로그램은 X Window System에서 제공하는 샘플 프로그램으로, X 로고가 표시된 작은 창을 화면에 나타내는 프로그램입니다. 먼저 우리의 실험 대상 $\texttt{xlogo}$에 대해 알아보겠습니다.

명령어를 입력한 후에는 로고가 그려진 작은 창이 나타날 것입니다. 일부 시스템에서 경고가 출력될 수 있는데, 이는 무시해도 됩니다.

$\texttt{xlogo}$ 쉘 프로그램

Tip: 만약 시스템에 $\texttt{xlogo}$가 없을 경우 $\texttt{xlogo}$ 대신 $\texttt{gedit}$이나 $\texttt{kwrite}$를 사용해보세요.

 

쉘의 프롬프트가 반환되지 않는 것이 보이시나요? 이는 쉘이 현재 실행중인 $\texttt{xlogo}$ 프로세스가 종료될 때까지 기다리고 있기 때문입니다. $\texttt{xlogo}$ 창을 닫으면 프로그램이 종료되고, 쉘 프롬프트가 다시 반환됩니다.

 

프로세스 중단하기

이제 $\texttt{xlogo}$를 다시 실행하면 어떤 일이 일어나는지 보겠습니다. 먼저 $\texttt{xlogo}$ 명령어를 입력하여 프로그램이 실행되면 터미널 윈도우로 돌아와 $\texttt{Ctrl-c}$를 입력하세요

터미널에서 $\texttt{Ctrl-c}$를 입력하면 터미널에서 SIGINT 신호를 보냅니다. SIGINT는 중단 신호로 이는 프로그램에 정중히 종료를 요청하는 신호입니다. $\texttt{Ctrl-c}$를 입력한 후에 $\texttt{xlogo}$ 창이 닫히고 쉘 프롬프트로 전환됩니다.

 

모든 프로그램이 이 신호를 처리하는 것은 아니지만, 많은 프로그램이 SIGINT 신호를 처리하여 정상 종료됩니다.

 

프로세스를 백그라운드로 실행

우리가 $\texttt{xlogo}$ 프로그램을 종료하지 않고 쉘 프롬프트를 반환받고 싶다면 프로그램을 백그라운드에서 실행하면 됩니다. 프로그램이 백그라운드에서 실행되도록 하려면 명령어 뒤에 앰퍼샌드(&) 기호를 추가하면 됩니다.

명령을 입력하면 $\texttt{xlogo}$ 창이 나타나고 쉘 프롬프트가 반환될 것입니다. 그런데 몇가지 숫자들도 같이 출력되네요. 이는 작업 제어(job control)이라는 쉘 기능의 일환으로 우리가 작업 번호 1([1])이고 PID 28236인 작업을 시작했다는 것을 말해줍니다. $\texttt{ps}$를 실행하면 우리가 실행한 프로세스를 볼 수 있습니다.

쉘의 작업 제어 기능에는 터미널에서 시작된 작업의 목록을 보여주는 기능도 있습니다. $\texttt{jobs}$ 명령어를 사용하면 이런 목록을 볼 수 있습니다:

출력된 결과로부터 현재 실행 중인 작업이 한 개 있고 작업 번호는 1번이며 $\texttt{xlogo &}$라는 명령어로 실행됐음을 보여줍니다.

 

프로세스를 포어그라운드로 반환

백그라운드에 있는 프로세스는 $\texttt{Ctrl-c}$를 포함해 어떠한 키보드 입력에도 영향을 받지 않습니다. 프로세스를 포어그라운드로 반환하려면 $\texttt{fg}$ 명령어를 아래와 같이 사용합니다:

$\texttt{fg}$ 커맨드 뒤에 퍼센트 기호와 작업 번호(작업 식별자(jobspec)라고 부름)를 입력하면 프로세스를 포어그라운드로 반환할 수 있습니다. 백그라운드 작업이 한 개만 있다면 작업 식별자를 생략해도 됩니다. 이제 $\texttt{xlogo}$를 종료하기 위해 $\texttt{Ctrl-c}$를 사용할 수 있습니다.

 

프로세스 일시중지하기

작업을 중단하지 않고 잠시 정지만 해두고 싶은 경우가 있습니다. 포어그라운드 프로세스를 백그라운드로 보내고 싶을 때 일시중지를 하는 경우가 종종 있죠. 키보드로 $\texttt{Ctrl-z}$를 입력하면 포어그라운드 프로세스를 중지하고 백그라운드로 보낼 수 있습니다. 직접 해보죠. 명령 프롬프트에 $\texttt{xlogo}$를 입력한 후 엔터를 눌러 실행시킨 후에 $\texttt{Ctrl-z}$를 입력해보세요:

$\texttt{xlogo}$가 정지된 후에 $\texttt{xlogo}$ 창 크기를 조절하려고 시도하면 되지 않은 것을 확인할 수 있을 것입니다. 우리는 $\texttt{fg}$ 명령을 이용해서 프로그램을 포어그라운드에서 다시 실행하거나, $\texttt{bg}$ 명령어를 이용해 백그라운드에서 계속 실행시킬 수 있습니다:

작업이 한 개 뿐이라면 $\texttt{fg}$ 명령어에 작업 식별자를 추가할 필요는 없습니다.

 

그래픽 프로그램을 커맨드 라인에서 실행할 때 $\texttt{&}$를 추가하지 않고 실행하여 포어그라운드에서 실행될 경우, 프로그램을 포어그라운드에서 백그라운드로 이동하는 것이 좋습니다.

 

왜 그래픽 프로그램을 커맨드 라인에서 실행하는 것이 좋을까요? 두 가지 이유가 있습니다.

  • 메뉴에 없는 프로그램 실행: 윈도우 매니저의 메뉴에 등록되어있지 않은 프로그램을 실행할 때, 커맨드 라인을 사용하면 프로그램을 쉽게 실행할 수 있습니다. 예를 들어, $\texttt{xlogo}$와 같은 프로그램이 이에 해당합니다.
  • 오류 메시지 확인: 프로그램을 커맨드 라인에서 실행하면 그래픽 메뉴에서 실행할 때는 보이지 않는 오류 메시지를 확인할 수 있습니다. 프로그램이 그래픽 메뉴에서 실행되지 않을 때, 커맨드 라인에서 실행하여 발생하는 오류 메시지를 통해 문제의 원인을 파악할 수 있습니다. 또한, 일부 그래픽 프로그램은 커맨드 라인 옵션을 통해 추가적인 기능을 제공하거나, 디버깅 정보를 출력할 수 있습니다.