리눅스 스터디 - 7

2018. 2. 5. 00:40

 

1. 프로세스 생성

 - 사용자(user process가) 프로세스 생성을 위해 syscall을 사용해 (쉘을 거쳐)os에 요청(fork, exec등)

 - 프로세스 생성시, os에서는 프로세스 테이블의 엔트리(file descriptor) 생성

 

 - fork와 exec

    fork

     - 리눅스의 fork()는 시스템콜을 이용해 구현되며, 공유할 자원을 지정할 수 있는 플래그를 사용한다

     - 프로세스 생성은 대부분 do_fork()에서 일어난다(fork()를 사용해 syscall, clone()[vfork()등 여러가지로 표현된다]을 통해 do_fork()호출, do_fork()는 copy_process()[여기서 자식 프로세스 생성]호출)

     - 자식프로세스를 생성하는 함수(유닉스는 모든섹션을 복사하고, 리눅스는 text는 공유하고 나머지를 복사한다)

     - (<=> 스레드는 stack만 분리, 나머지는 공유)

     - 부모와 자식 프로세스가 동시에 수행된다. 만일 부모가 다 수행한 이후에도 자식이 아직 동작중이면, 부모는 자식이 완료될 때까지 대기한다(자식이 리턴될 때까지 대기)

 

    ※ copy_process()에서 일어나는 과정(순서대로 일어남)

     - 프로세스 개수 제한 체크

     - task_struct 구조체 생성

     - TASK_UNINTERRUPTIBLE 설정

     - Flag 맴버 갱신

     - pid부여

     - 자원복제 및 공유

     - (스케줄링이 가능하게)타임 슬라스 분배

     - 새자식 프로세스 포인터 반환

 

    exec

     - 새 프로세스를 현재 프로세스 위에 덮어 쓰는 방식, 이러한 방식으로 원래코드로 돌아오지 않고 바로 빠져나온다.

     - 보통 홀로 사용하지않는다(fork를 사용한후 사용)

     - 원 프로세스의 PID, PPID 번호와 동일하다

     - exec계열의 함수들

        execl: argv인자를 char *로 하나씩 넘겨줄 때 사용
        execv: argv인자를 char *[]배열로 한번에 넘겨줄 때 사용
        그밖에 execlp,execle,execvp, execve가 있는데, 끝에 붙는 e는 '환경변수를 넘겨줄때'를, p는 '절대경로를 입력하지 않는(환경변수 PATH를 참조) 경우'를 나타낸다

 

     - exec계열 함수 원형에 대한 정보
        http://forum.falinux.com/zbxe/?mid=C_LIB&page=3&document_srl=408563
     - exec계열 함수 사용법
        http://bbolmin.tistory.com/35
        https://blog.naver.com/youtoo2/20011337681

 

 - 참조

    fork와 exec의 차이: http://channelofchaos.tistory.com/55

 

 

 

 

2. 프로세스 종료

 - 프로세스 생성과 마찬가지로 syscall을 통해 os에 요청한다(exit, abort)

 - 부모는 자식이 종료될 때까지 기다린다(wait)

 - 자식프로세스의 종료이유

    자식이 할당된 자원을 초과 사용할때

    자식의 task가 더이상 필요 없을때

    부모가 종료될 때(부모가 종료될 때 자식을 먼저 종료시키고 부모를 종료시켜야 한다. 아니면 좀비, 고아가 발생할 수 있다)

 

 - exit()함수

    정상적인 종료

 - abort()함수

    강제적인 종료, 즉시 종료된다

    SIGABRT 시그널에 의해서 종료된다

 

 

 - wait()함수

    부모 프로세스가 자식프로세스가 종료될때까지 기다리는 과정에서 사용하는 함수이다.(즉, 일반적으로 fork와 같이 사용한다)

 

 - 참조

    wait함수 응용: http://forum.falinux.com/zbxe/index.php?document_srl=408545&mid=C_LIB

    abort

     http://forum.falinux.com/zbxe/index.php?document_srl=408379&mid=C_LIB

     https://msdn.microsoft.com/ko-kr/library/tcwt7yw6.aspx

 

 

 

 

3. fork 동작방식

 - 전통적인 fork

    공유자원을 포함한 모든 자원을 복제하는 방식

    비효율적임

    최악의 경우 새로 생성된 프로세스가 바로 새이미지를 실행하는 것으로 복제 자체가 무의미해짐

 - copy-on-write

    리눅스에서 fork()를 구한하는데 사용

    데이터의 복제를 지연 또는 방지하는 기법

    프로세스 공간을 복제하기보다는 공유(데이터를 쓸(w)경우에만 공간을 복제한다)

    복제를 방지(복제를 안하니)로 성능개선효과가 있다

 

 

 

4. 프로세스 생성과 종료 함수들의 상세 설명

 - fork
    필요한 헤더파일
     - <unistd.h>
     - <sys/types.h> //pid_t라는 자료형때문에 필요(#define pid_t int이기때문에 그냥 int를 써도 무리는 없다)

    원형
     - pid_t fork();

    들어가는 인자 의미
     - 보통 안넣는듯(없는것 같기도하고)

    반환되는 값 의미
     - fork생성을 실패하면 -1을. 부모에게는 새로 생성된 자식 프로세스 PID, 자식 프로세스에는 0이 반환된다.

    특징
     - 부모, 자식의 PC(program count)값은 같지만 pid의 값은다르다
     - 리눅스에서는 text영역은 공유, 나머지는 복사하여 분리동작
     - 부모와 자식은 개별로 동작하고 각 프로세스의 동작은 서로에게 영향을 주지 않는다
     - 즉 자식 생성이후, 부모와 자식은 os의 스케줄러에 의해 개별적으로 동작한다

    참조
     - vfork란
        http://forum.falinux.com/zbxe/index.php?mid=C_LIB&page=5&document_srl=408536
     - fork예
        http://forum.falinux.com/zbxe/index.php?document_srl=412814&mid=C_LIB
     - fork와 exec의 차이
        http://channelofchaos.tistory.com/55

 


 - execl
    필요한 헤더파일
     - <unistd.h>
    원형
     - int execl( const char *path, const char *arg, ...);
    들어가는 인자 의미
     - char *path  실행 파일의 디레토리 포함 전체 파일 명 
     - const char *arg 넣을 인수들
    반환되는 값 의미
     - int형을 반환(실패일 때만 -1)
    특징
     - 새로운 프로그램을 기존 프로그램에 덮어쓴다(=다른 프로그램을 실행하고 자신은 종료한다)
     - execv와는 달리 인자를 하나하나 넣는다

     - 인자를 모두 넣었으면 맨마지막 인자로 널포인터(NULL, (char *)0)를 넣어 인자의 나열이 끝났음을 알린다
    기타설명
     - ① execl( "/bin/ls", "/bin/ls", "-al", "/tmp", NULL);
        이런식으로 사용하는데, 두번째 인자는 그냥 실행파일(혹은 명령)만 넣어도 잘된다
        execl( "/bin/ls", "ls", "-al", "/tmp", NULL); //이렇게

     - ② path경로 입력후 그다음은 인자를 넣어야하는데 "/bin/ls"를 한번더 넣는다(혹은 "ls")
        개인적으로 생각한 이유는 다음과 같다
         - c언어에서 공부 했듯이(16장의 6.) 명령행 인수를 받을때 실행파일명을 포함하여 명령행 인수를 센다
         - 즉 리눅스의 명령행에서 "ls -al"이라고 입력했으면 ls와 al 두개의 인수를 받는것이다.
         - (그렇기때문에 /bin/ls를 하던 ls를 하던 문제가없다. 이미 경로를 지정하였기 때문에..)

         - 널값(널포인터)을 넣는것도 같은이유이다

    참조
     - http://forum.falinux.com/zbxe/index.php?document_srl=408554&mid=C_LIB

 

 

 - execv
    필요한 헤더파일
     - <unistd.h>
    원형
     - int execv( const char *path, char *const argv[]);
    들어가는 인자 의미
     - char *path  실행 파일의 디레토리 포함 전체 파일 명 
     - char *argv[] 인수 목록  
    반환되는 값 의미
     - int형을 반환(실패일 때만 -1)
    특징
     - 새로운 프로그램을 기존 프로그램에 덮어쓴다(=다른 프로그램을 실행하고 자신은 종료한다)
     - execl와는 달리 포인터배열을 이용해 한번에 넣는다

     - 인자를 모두 넣었으면 맨마지막 인자로 널포인터(NULL, (char *)0)를 넣어 인자의 나열이 끝났음을 알린다
    사용예
        char *argv[] ={ "/bin/ls", "-al", "/tmp", NULL};
        execv( "/bin/ls", argv);
    참조
     - http://forum.falinux.com/zbxe/index.php?mid=C_LIB&page=4&document_srl=408563

 

 

 - exit
    필요한 헤더파일
     - <stdlib.h>
    원형
     - void exit(int status);
    들어가는 인자 의미
     - 해당 프로세스에게 알려 줄 종료 값
     - 일반적으로 0은 정상종료, 1은 에러를 의미
    반환되는 값 의미
     - 없음
    특징
     - 주로 에러가 났을 때 강제 종료시키기 위해 if문 속에서 사용한다
     - atexit(함수명);을 통해 종료될때 실행될 함수를 지정할 수 있다
    참조
     - http://ehpub.co.kr/exit-%ED%95%A8%EC%88%98/

 

※ exit와 return의 차이
 C 표준에 따르면,
 ①재귀적으로 불리지 않은) 처음 시작한 main()이 "return X"를 실행하는 것은, exit(X)를 부르는 것과 완전히 같다.
 ②(재귀적으로 불리지 않은) 처음 시작한 main()이 블럭의 끝을 알리는 "}"에 다다르면 "return 0" 즉 exit(0)을 부른 것과 완전히 같다.
 예외는 main()에서 만든 local variable이 파괴되는 시점이다. exit()를 부르면, exit()가 atexit()로 등록한 함수들을 실행할 때까지

 local variable은 남아 있지만, return으로 끝냈을 경우, local variable이 다 파괴된 다음에 exit()가 호출된다. 따라서 atexit()로

 등록한 함수가 없다면, return이나 exit()를 부르는 것이나 같다고 볼 수 있다

    => 즉 return은 변수등의 메모리 삭제후 exit호출, exit로 끝낼경우는 exit의 과정이 다끝나고 변수등의 메모리삭제
    => 자세한 내용을 알고자 할때는 컴파일쪽을 알아야한다고 한다
     - 추가적으로 exit는 블록 중간에 삽입이 가능하지만, return은 블록 끝에만 위치해야하는 것 같다
        참조: https://kldp.org/node/47649

 


 - abort
    필요한 헤더파일
     - <stdlib.h>
    원형
     - void abort(void); 
    들어가는 인자 의미
     - 없음
    반환되는 값 의미
     - 없음

    특징
     - abort함수를 호출하면 SIGABRT 시그널이 발생하여 강제종료된다(즉 비정상 종료를 의미한다)
     - exit() 함수와 마찬가지로 abort() 함수는 프로그램을 종료하기 전에 버퍼를 삭제하고 열린 파일을 닫는다. 
     - 프로그램이 종료되고 오류메세지가 뜬다(리눅스에서는 'Aborted (core dumped)'이런식으로 끈다)
    참조
     - https://www.ibm.com/support/knowledgecenter/ko/ssw_ibm_i_73/rtref/abort.htm

 


 - wait
    필요한 헤더파일
     - <wait.h>
     - <sys/types.h> //pid_t라는 자료형때문에 필요(#define pid_t int이기때문에 그냥 int를 써도 무리는 없다)

    원형
     - pid_t wait(int *status)
    들어가는 인자 의미
     - int status  자식 프로세스 종료 상태
     - (포인터로 넘겼으니 해당변수는 부모프로세스에 있고, 부모프로세스는 자식프로세스가 종료되면 그 상태값을 받아올 수 있다)
    반환되는 값 의미
     - pid_t 종료된 자식 프로세스의 PID
    특징
     - 자식 프로세스 작업이 끝날 때 까지 대기하며, 자식 프로세스가 종료한 상태를 구한다.
     - 자식프로세스의 종료시 그 상태값은 status에 저장되고, 종료된 자식프로세스의 pid값이 반환된다 
    기타설명

     - ① status는
        정상종료의 경우 상위8비트에 자식이 반환한(return, exit)값이 있고, 하위 8비트는 0값
        비정상종료의 경우 상위8비트는 0, 하위 8비트에 종료시킨 시그널의 번호가 저장된다

     - ② status를 통해 정상종료인지, 비정상인지 구분하는 법은 다음과 같다
        status &= 0xFF; //상위8비트는 무조건 0이되고 하위8비트는 그 값이 그대로 남으니, 정상종료의 경우 0, 비정상종료의 경우 시그널 번호가 남는다

     - ③ status를 통해 정상종료인것을 확인하면( if(status &= 0xFF==0) ) 상위비트의 값을 구한다
        status>>8; //8번 비트시프트하여 값을 구한다


    사용예
        pid_t pid;
        pid_t child_pid;
        int status;
        pid   = fork();
        switch( pid)
        {
              case -1  :
              {
                 printf( "자식 프로세스 생성 실패\n");
                 return -1;
              }

              case 0   :
              {
                 printf( "이프로세스는 자식이다\n");
                 return 99;
              }
              default  :
              {
                 printf( "이 프로세스는 부모이다\n");
                 child_pid = wait(&status);

                 printf( "자식의 PID: %d\n,", child_pid);
                 if ( 0 == ( status & 0xff))
                 {
                    printf( "정상적으로 종료되었고 반환값은 %d입니다\n", status >> 8);
                 }
                 else
                 {
                    printf( "비 정상으로 종료되었고 종료 시그널 번호는 %d입니다\n", status);
                 }

        }

 

    참조
     - http://forum.falinux.com/zbxe/index.php?document_srl=408545&mid=C_LIB
     - waitpid같이 자식 프로세스 종료를 확인하는 함수도 있다
        http://forum.falinux.com/zbxe/index.php?document_srl=408548&mid=C_LIB

 

 

       

+ Recent posts