Process

윤강훈·2024년 11월 15일

System Programming

목록 보기
10/12

Process

프로세스는 실행 중인 프로그램을 의미합니다.

여기서 프로그램이란 소스 코드를 바이너리 코드로 컴파일해서 생성한 일련의 기계어 명령어이고, 실행된다는 것은 명령어들을 메모리에 로드하고 CPU가 그것을 하나씩 실행하는 것을 의미합니다.

ps

ps 명령어는 현재 실행 중인 프로세스의 상태를 출력합니다.

  • ps

    1. PID(Process ID): 프로세스마다 고유한 번호 존재
    2. TTY(TeleTYpe, Terminal TYpe): 사용자가 로그인한 콘솔이나 터미널 이름
    3. TIME: 프로세스가 실행된 CPU 시간
    4. CMD: 프로세스를 실행한 명령어 이름
  • ps -a: 다른 사용자 및 다른 터미널에서 실행되는 프로세스도 출력

  • ps -la: long format

    1. F: process Flag(no longer used)
    2. S: 각 프로세스의 상태
    - S: Sleeping
    - R: Running
    3. UID: User ID
    4. PID: Process ID
    5. PPID: Parent Process ID
    6. PRI: PRIority (숫자가 높을 수록 우선 순위가 낮음)
    7. NI: NIceness level
    - -20(not nice to other) ~ 19(nicest)
    - 높은 nice 번호를 가진 프로세스는 CPU 시간을 양보
    8. SZ: process memory SiZe (KB)

  • ps -fa: full-format listing (UID 대신 사용자 이름 표시)

Memory

Unix 시스템의 메모리는 kernel space와 user space로 나뉩니다.

  • Kernel: 하드웨어 관리를 하며, user가 요청하는 서비스를 처리합니다. 또한 이 공간에서 문제가 생기면 시스템 전체에서 문제가 생길 수 있으므로 일반 사용자는 접근할 수 없으며, 함수를 이용해 간접적인 접근만이 가능합니다.

  • User: 제한된 접근만이 가능하며, 문제가 생겨도 시스템에는 영향이 없습니다. 사용자 프로그램을 실행하는 역할을 합니다.

Process는 그 중 user space에서 동작하는데, 작은 단위의 page로 나뉘어서 allocation list를 관리합니다.

Kernel은 프로그램의 기계어 코드와 데이터를 보관할 memory page를 검색하고, 프로세스의 메모리 할당 정보와 속성들을 저장하기 위한 data structure를 설정합니다.

Shell

Shell은 프로세스 관리 및 프로그램을 실행하는 프로그램입니다.

3개의 주요한 기능이 있는데, 이는 다음과 같습니다.

  1. 프로그램 실행 (./out)

    • 프로그램을 메모리에 로드하고 실행
    • 동작 과정
      1. 사용자가 'a.out'을 입력
      2. shell은 프로그램을 실행을 위해 프로세스 생성
      3. shell은 디스크에서 프로세스 프로그램을 load
      4. 프로그램은 완료될 때까지 프로세스 내에서 실행
  2. 입출력 관리

    • 입출력 기호(redirection) 사용: >, <, |
  3. 프로그래밍(Shell programming)

    $ for((num=0;num<3;num++)
    > do
    > echo "num: $num"
    > done
    num: 0
    num: 1
    num: 2

    위와 같은 프로그래밍이 가능

Program Run a Program

프로그램을 실행하고 그 프로그램에서 또 다른 프로그램을 실행해야하는 경우가 발생할 수 있다. 그럴 때 우리는 어떠한 방법을 사용할 수 있을까?

  • execvp()
    • 가변길이의 인자를 사용하여 프로그램을 실행합니다.

      usage: execvp(cont char *file, cont char *argv[])

    • 특징
      1. 프로그램 내에서 실행시키면 원래 동작하고 있던 프로그램이 종료되고 실행됨. (execvp 이후의 코드는 실행되지 않음)
      2. 프로그램은 종료되지만 프로세스는 같은 프로세스를 사용함.
  • How to Unix system run program
    1. 동작 중인 프로세스가 execvp(program_name, arglist) 호출
    2. 커널은 디스크에 저장된 프로그램을 프로세스로 load
    3. 커널은 arglist를 프로세스에 전달
    4. 커널은 main(argc, argv) 함수 호출

Get a New Process

  • fork()

    • 이미 실행 중인 프로세스를 복사해서 동일한 새로운 프로세스를 생성

    • kernel 동작 과정

      1. 새로운 메모리와 데이터 구조 할당
      2. 원래 process의 code와 data를 새로운 process에 복사
      3. 새로운 process를 실행 중인 프로세스의 리스트에 추가
      4. 새로운 process에게 제어권을 넘김
    • 예시 코드

      #include <stdio.h>
      #include <stdlib.h>
      #include <unistd.h>
      
      int main(){
          pid_t mypid = getpid();
          printf("Before: my pid is %d\n", mypid);
      
          pid_t ret_from_fork = fork();
      
          sleep(1);
          printf("After: my pid is %d, fork() said %d\n",
                  getpid(), ret_from_fork);
      
          return 0;
      }

    • fork() 함수가 실행된 뒤에는 실행된 그 자리에서부터 남은 코드가 시작된다.
      위의 예제 코드에서 Before: ~~ 가 두 번 출력되지 않는 것도 그 이유 때문이다.
      만약 코드의 처음부터 다시 시작하는 구조였다면 fork()는 무한히 자기 자신을 호출하는 재귀함수가 될 것이다.

wait

  • wait(statusptr)
    • 자식 process가 return(exit)한 값을 저장

    • wait() 함수에는 두 가지 기능이 있음

      1. 자식 프로세스가 종료될 때까지 부모 프로세스를 blocking
      2. 자식 프로세스(terminated process)의 pid를 리턴
    • Kernel은 자식 프로세스의 종료 상태 정보를 2바이트(16비트) 정수 값으로 저장

      1. exit value: 8bits
      2. core dump bit: 1bit
      3. signal number: 7bits

Summary

Shell이 프로그램을 실행하는 과정을 정리해보면 이렇습니다.

  1. 새로운 process 생성을 위해 fork() 함수 호출
  2. 프로그램을 실행하기 위해 exec() 함수 사용
  3. wait()를 사용하여 실행을 마칠 때까지 대기

또한 하나의 프로세스에서 복제된 프로세스 내부의 프로그램이 실행되고 있을 때, SIGINT signal이 발생하면 모든 프로세스가 종료된다.

이는 signal과 tty의 특성인데, tty에서 발생한 signal은 해당 tty를 사용하는 모든 프로세스에게 발생하기 때문에 다 종료되는 것이다.

덧붙여서 서로 다른 2개의 tty에서 같은 프로그램을 실행 중일 때, 어떤 하나의 tty에서 signal을 발생시킨다고 해서 나머지 하나의 tty에는 영향을 주지 않는다.

exec family

UsageExample
execv(fullpath, arglist)char *arg[4]={“ls”, “-l”, “./demodir”, NULL};
execvp("/bins/ls", arg); 와 동일한 기능
execvp(file, arglist)char *arg[4]={“ls”, “-l”, “./demodir”, NULL};
execve(fullpath, arglist, envlist)char *args[] = {"/show_envp" , "HTTP", NULL};
char *env[] = {"HTTP=knu.ac.kr", NULL};
execl(fullpath, arg0, arg1, …, NULL)execl(“/bin/ls”, “ls”, “-l”, “./demodir”, NULL)
execlp(file, arg0, arg1, …, NULL)execlp(“ls”, “ls”, “-l”, “./demodir”, NULL)
  • p: PATH(설정된 경로에서 명령어 검색)
  • e: environment
  • l: list(하나씩 나열)
  • v: vector(배열 형태)
profile
Just do it.

0개의 댓글