Tuuna Computer Science

[리눅스] 프로세스에 대해서 본문

System programing

[리눅스] 프로세스에 대해서

GuTTe 2018. 6. 9. 20:44
    
흔히 사용하는 프로그램은 하드 디스크에 위치한다. 

이 하드디스크에 위치한 프로그램을 실행하면 

메모리상에 복사된다. 즉, 프로그램이 복사된 이미지가 올라가는 것이다 

-> 서로 완전히 독립적인 프로그램의 실행 가능 
-> 여러개의 이미지가 가능 ( 멀티 프로세스, 멀티스레딩) 

  • 프로세스의 상태 

동시에 프로그램을 실행하면 동시에 프로세스가 작동하는 것이라고 생각할 수 있지만 

사실은 그것이 아니라 A라는 프로그램 실행하다가 중단되면 B 프로그램 C 프로그램으로와 같이 

Switching한다. 이 Switching시간이 매우 짧기 때문에 프로그램들이 동시에 실행하는것으로 착각할 수 있다. 

이를 -> 멀티 태스킹 운영체제라고 한다. (시분할) (time sharing) 

프로세스는 4가지의 상태중 하나를 가지게 된다. 

  • running 상태 : 실행가능한 상태를 말한다. 
  • waiting 상태 : 어떤 조건을 기다리는 상태 
  • stopped 상태 : 실행이 중단된 상태
  • zombie 상태 : 실행이 끝나고, 메모리 상에서 프로세스의 이미지가 제거 되었으나 운영체제의 커널은 여전히 프로세스의 정보를 가지고 있는 상태 

[ 프로세스의 모드 ] 

프로세스는 유저모드와 커널모드의 두가지 모드를 오가면서 실행이 된다. 

[ 프로세스의 실행 ]  

리눅스에서 새로운 프로세스를 실행시키는 유일한 방법 : execl(2)를 이용 

#include <unistd.h>

int execl(const char *path, const char *arg, ...);

path : 실행되는 프로그램의 완전한 경로 
arg : 프로그램이 실행될 때 넘겨주는 인자,  넘겨질 인자가 없다면 NULL를 적자  

ex ) 

#include <unistd.h> 

int main(void){

execl("/home/dansuchang/test_execl",NULL) 
//printf("haha...") 하면 출력되지 않는다 위에 실행 이미지가 덮었기 때문
return 0;

[ 멀티. 다중 - 프로세스 ]

execl함수는 원본 프로세스의 이미지를 덮어써 버린다! 이렇게 되면 운영체제는 동시에 단지 하나의 프로세스만을 가질 수 있게 될것이다.

하지만 fork를 이용해서 이 문제를 해결할 수 있다. 

fork는 원본프로그램의 복사판을 만드는 함수이다. 

execl함수와의 차이는 execl은 다른 프로세스를 생성하지만 fork는 자기자신을 복제한다. 
즉, 유닉스 운영체제에서 새로운 프로세스를 생성시키는 유일한 함수는 execl이다! 

+---------+        +--------------+
  | Process |----+---| Copy Process |
  +---------+    |   +--------------+
                 |   +--------------+
                 +---| Copy Process |
                 |   +--------------+
                 |
                 +--- ...  :: fork( ) 

이때 원본 프로세스를 부모 프로세스라고 한다. 

즉, 부모 프로세스로부터 복사 되어서 새로 생성된 프로세스를 자식 프로세스라고 한다.  :: 

ex) shell에서 ls 프로그램을 실행하고 다시 shell로 돌아온다. 

쉽게 풀이하면 shell이 ls 명령을 받으면 fork함수를 이용해서 자식 프로세스를 만들고 이 자식 프로세스에서 execl을 이용해서 ls를 실행시킨다. 

fork & execl을 이용 -> 멀티 프로세스 환경 

[ fork를 이용한 자식 프로세스 생성 ] 

ex) #include <unistd.h> 

     pid_t fork(void); 

-> fork함수를 사용하면 부모 프로세스와 동일한 정보를 물려 받는다. 

성공시 : 부모프로세스에게 새로 생성된 자식프로세스의 PID리턴 자식 프로세스에게 0이 리턴

================================================
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main()
{
        int pid;
        int i;
        i = 1000;
        pid = fork();
        if (pid == -1)
        {
               perror("fork error ");
               exit(0);
        }
        // 자식프로세스가 실행시키는 코드
        else if (pid == 0)
        {
               printf("자식 : 내 PID는 %d\n", getpid());
               while (1)
               {
                       printf("-->%d\n", i);
                       i++;
                       sleep(1);
               }
        }
        // 부모프로세스가 실행시키는 코드
        else
        {
               printf("부모 : 내가 낳은 자식의 PID는 %d\n", pid);
               while (1)
               {
                       printf("==>%d\n", i);
                       i += 4;
                       sleep(1);
               }
        }
=========================================
[ shell 제작 ]

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#define chop(str) str[strlen(str)-1] = 0x00; //단지 배열의 끝에 널문자를 심기 위해?
int main(int argc, char **argv)
{
        char buf[256];
        printf("My Shell\n");
        int pid; //pid설정
        while (1) //아 무한 루프였네
        {
               // 사용자 입력을 기다린다.
               printf("# ");
               fgets(buf, 255, stdin); //배열에 입력
               chop(buf); //끝에 널문자를 넣음
               // 입력이 quit 라면, 프로그램을 종료한다.
               if (strncmp(buf, "quit", 4) == 0) //buf의 앞에 4바이트를 비교
               {
                       exit(0);
               }
               // 입력한 명령이 실행가능한 프로그램이라면
               // fork 한후 execl을 이용해서 실행한다.
               //먼저 자식프로세서를 만들고 자식프로세서를 실행한다.
               
               /*
               int access(const char *pathname, int mode); 파일의 이름 및 경로, 검사할 내용
               R_OK : 읽기가 가능?
               W_OK : 쓰기가 가능?
               X_OK : 실행이 가능?
               F_OK : 파일이 존재?
               return : 0 -> 가능 or 존재 :: -1 -> X or 거절  내용은 errno에 저장
               */
               if (access(buf, X_OK) == 0) //파일의 권한을 검사한다. -> 가능할 시
               {
                       pid = fork(); //자식프로세서 생성
                       if (pid < 0)
                       {
                              fprintf(stderr, "Fork Error"); //fprintf -> FILE* stream에 문자열을
                                                            //사용한다.
                       }
                       if (pid == 0) //자식프로세서가 성공적으로 만들어지면 0이 반환된다는 것인가
                       {
                              if (execl(buf, buf, NULL) == -1) //일단 실행을 해보고 인가 ?
                                      fprintf(stderr, "Command Exec Error\n\n");
                              exit(0);
                       }
                       if (pid > 0)
                       {
                              // 부모 프로세스는 자식프로세스가 종료되길 기다린다.
                              int status;
                              waitpid(pid, &status, WUNTRACED);
                                              //즉, pid가 지목하는 특정 프로세서가 종료될때까지 기다린다.
                              //이것이 wait함수와의 차이점
                              /*
                              pid_t waitpid(pid_t pid, int *status, int options);
                              pid_t pid : 감시할 자식 프로세서 ID
                              int* status : 자식프로세서의 종료 상태 정보
                              int options : 대기를 위한 옵션   
                              return : 정상 -> 종료된 자식프로세서의 ID
                                       실패 -> -1
                              */      
                       }
               }
               else // 만약 실행가능한 프로그램이 아니라면, 에러메시지를 출력
               {
                       fprintf(stderr, "Command Not Found\n\n");
               }
        }

모든 프로세스는 init로 부터 fork & exec 되어서 생성이 된다. pstree명령을 이용하면, 프로세스의 관계를 확인할 수 있다.

1.name : 프로세스의 이름이다. 
2.PID : Process ID로 운영체제가 각 각의 프로세스에 부여하는 유일한 일련번호이다. 

1.PPID : 부모프로세스의 ID로 어떤 프로세스로 부터 생성이 되었는지 알려준다. 
2.PGID : 프로세스는 여러개의 자식프로세스 제작 가능.  부모 -> {자식,자식}가능 그룹의 일련번호 

$ ps -efjc | grep forktest : forktest 프로세스의 정보 

$ ps -efjc | grep 8557 : 특정 프로세스의 정보 확인 

고아 프로세스 : 부모 프로세스가 뻗었을 경우 자식프로세스는 뻗지 않음 

그럼 즉 고아 프로세스는 init의 자식프로세스가 된다 PPID : 1


데몬 프로세스 

현재 화면과 프로세스에서 떨어져 나가서 독립적으로 실행되는 프로세스

조건 1. 고아프로세스여야 한다. 
        2. 표준입력, 표준출력, 표준에러를 닫는다. 

이런 데몬 프로세스와의 상호작용은 IPC 또는 로그 파일 등을 통해 다루어 진다. 

IPC : 프로개름관의 내부 통신을 위한 설비 

        3. 터미널을 가지지 않는다 (터미널은 키보드와 모니터로 연결되기 때문) 



'System programing' 카테고리의 다른 글

리눅스 메모리에 대해  (0) 2018.08.10
system call  (0) 2018.06.11
[ 공유 메모리 ]  (0) 2018.06.09
[ 좀비 프로세스 ]  (0) 2018.06.09
Comments