[시스템 프로그래밍] 프로세스

2023. 10. 30. 13:30CS/시스템 프로그래밍

process, task 혼용

  • 살아 움직이는 프로그램을 의미
  • 디스크에서만 존재하던 프로그램 및 데이터가 메모리로 올라오면서 생성

 


프로세스와 관련된 시스템 콜

 

  • fork(), clone() : 프로세스 생성 task_struct 생성
  • execve() : excute a new program(loading) → 디스크에서 메모리로 데이터를 옮기는 과정
    • 컴파일 된 프로그램을 실행할 때 사용되는 시스템의 구성요소 중 하나인 loader의 역할과 유사
    • 해당 함수는 새로운 프로그램의 실행 가능한 파일을 메모리로 로드하고, 그 파일을 실행할 새로운 프로세스 생성
    • execve()를 호출하면 현재 프로세스의 메모리 이미지(txt, data, stack,heap..)가 새로운 프로그램의 코드와 데이터로 대체 → 로더가 하는 일과 유사한 작업

    • 해당 시스템 콜은 로더보다 더 높은 수준에서 작동
      • 로더는 실행파일을 로드하고 초기화하는데 필요한 모든 단계를 수행하지만 execve()의 경우 이미 로드된 프로그램 이미지에 새로운 프로그램의 이미지를 덮어쓰는 역할
      • 프로세스 내에 메모리 이미지의 완전한 대체를 의미하는데, 로더의 경우 프로그램을 초기화하고 시작하기 전에 현재 프로세스의 상태를 보존하고 다시 복구하는 작업도 포함함
    • loader의 역할을 포함하며, 기존 프로세스를 새로운 프로그램으로 교체하고 실행할 수 있게해줌
  • exit() : 프로세스 종료 및 부모 프로세스에게 자식 프로세스의 상태값을 알려줌
  • wait(), waitpid() : 진행 중이던 프로세스를 지정된, 혹은 다른 프로세스가 종료가 될 때까지 대기 상태로 변환
  • getpid() : process ID를 가져옴
    • getppid() : parent의 pid를 가져옴

 

ex:

write와 printf

  • 해당 함수를 통해 데이터를 터미널에 출력하면 두 함수 모두 터미널에 내용이 작성
  • 터미널 화면에 텍스트나 데이터를 표시
  • 차이점 (출력형식, 표준출력, 편의성)
    • write
      • 데이터를 바이트 단위로 출력
      • 주로 이진 데이터 혹은 형식화되지 않은 텍스트 데이터를 출력하는데 사용

      • 파일 디스크립터를 직접 지정(ex : STDOUT_FILENO)를 사용하여 표준 출력 스트림에 데이터를 쓸 수 있다
    • printf
      • 형식화 된 문자열을 출력할 때 사용
      • 형식 지정자를 사용하여 출력 형식 지정 → 변수나 상수 값을 포맷에 따라 문자열로 변환하여 출력

      • 표준 출력 스트림(stdout)에 자동으로 출력→별도의 파일 디스크립터 지정할 필요 x
  • write 더 낮은 수준의 출력을 다루며, printf 더 높은 수준의 형식화된 출력을 다루는데 사용

 


동작 원리

 

  • fork()
    • 메모리 이미지가 동일한 프로세스를 하나 더 만든다
    • 기존의 부모 프로세스는 유지하며 새로운 자식 프로세스를 만든다.
      • 그렇기에 프로그램의 제어의 흐름이 나눠지게 된다
        • 부모의 흐름과 자식의 흐름(시스템 관점)
        • return 값이 두개가 된다.
          • fork의 경우 부모와 자식이 다른 리턴값을 가지고 있음
            • (부모는 자식의 pid를 리턴 값으로 가지며, 자식은 0을 반환)
            • 대체로 자식의 pid는 부모보다 크다
      • 프로세스는 독립적 개체이므로 서로의 자원을 공유할 수 없다.
        • 전역, 지역변수와 상관없이 부모와 자식 프로세스간에도 자원의 공유는 불가하기에 자식에서 변화된 변수의 값은 부모에서는 간섭하지 못한다.,
          • 수정이 되는 data와 stack의 경우 복사가 되서 자식 프로세스에게 할당이 된다
          • 그렇지만 수정이 불가능한 text 부분은 부모와 자식이 같이 사용한다.
            • 이때 보여지는 memory 부분의 이미지 나열에서 기존에 배운 text - data - heap - stack으로 나열된 부분들은 가상의 이미지이며 실제로는 여러 프로세스의 이미지들이 섞여있는 형태로 존재한다
        •  

     

 

  • execve()
    • 현재 프로세스의 메모리 이미지(text, data, stack, heap)을 새로운 바이너리 코드로 변경
    int execlp(const char *filename, const char *arg0, ..., const char argn, (char *) 0);
    int execvp(const char *filename, char *const argvl 1); 30147146 int execl(const char *pathname, const char *arg0, .., const char *argn, (char *) 0);
    int execv(const char *pathname, char *const argv[ ]);
    int execle(const char *pathname, const char *arg0, ..., const char *argn, (char *) 0,
    char *const envp[ ]);
    
    int execve(const char *pathname, char *const argv[ ], char *const envp[ ]);
    • filename : 파일의 이름만을 나타냄
    • pathname : 파일의 정확한 경로만 나타냄
    • envp : 환경변수를 나타내는 배열
      • 프로세스가 실행될 때 사용할 수 있는 정보를 담고 있는 변수들의 집합
      • 프로세스가 실행될 때 환경을 설정하고 커맨드 라인 프로그램이 이 정보를 사용할 수 있게 함
      • 각각의 문자열을 “이름 = 값” 형식을 가짐 (ex : "PATH=/bin:/usr/bin”)
      • 만약 해당 변수가 없는 메서드의 경우 호출되는 프로세스의 환경변수를 상속받아 사용
    • -l : list를 의미
    • -v : vector를 의미
    • list의 형태를 가진 인자들을 모아서 argv[]에 모아서 받음, 표현의 차이이며 실제로 결과는 동일하다.

  • 기존의 execve계열의 시스템 콜이 실행이 되면 후에 진행되라고 작성이 되어있던 코드가 아닌 불러와서 새로 연결이 된 코드가 실행이 된다.