[Linux]Process2

공부기록·2023년 10월 20일
0
post-thumbnail

🔊 Process Termination

  • 정상적인 종료

    • return from main
    • exit 호출
    • _exit 또는 _Exit 호출
  • 비정상적인 종료

    • abort 호출
    • signal 수신

🔈 exit( )

#include <stdlib.h>

void exit(int status);
void _Exit(int status);

#include <unistd.h>

void _exit(int status);
  • _달린건 kernel로 즉각 이동한다.
  • exit은 fclose를 이용하여 cleanup 작업을 해준다.
  • exit(0)은 return(0)과 동일하다.

🔈 atexit( )

  • exit handler를 호출로 사용자 지정함수가 cleanup 함수처럼 실행된다.
  • exit()함수 호출시 역순으로 진행한다.
#include <stdlib.h>

int atexit(void (*func)(void));

/* 성공시 0, 실패시 0이아닌 값이 반환 */

🖇️ exit example

    void func1() { printf("print func1\n"); }
    void func2() { printf("print func2\n"); }
    void func3() { printf("print func3\n"); }
    void func4() { printf("print func4\n"); }

    int main(int argc, char** argv){
        pid_t pid;

        atexit(func1);	atexit(func2);
        atexit(func3);	atexit(func4);

        pid=fork();

        if(pid==0){
            printf("CHILD PROCESS : CALLED\n");
            printf("CHILD PROCESS : EXIT CALL\n");
            exit(0);
        }	
        if(pid < 0){
            perror("PARENT ERROR");
            exit(1);
        }
        wait(NULL);
        printf("PARENT PROCESS : EXIT CALL\n");
        exit(0);
    }

🖇️ example의 결과값

[root@localhost unix]# ./a.out

CHILD PROCESS : CALLED
CHILD PROCESS : EXIT CALL
print func4
print func3
print func2
print func1

PARENT PROCESS : EXIT CALL
print func4
print func3
print func2
print func1

🔊 프로세스 동기화

  • 프로세스가 종료되면
    • kernel이 모든 open descriptor를 닫고, 사용된 memory를 해제한다.
    • proc entry (pid, termination status-exit(status, CPU time)은 남겨둔다.
      • 이는 parent process가 종료된 후에 지운다.
      • parent의 wait() 이후
    • parent는 wait()을 이용하여 종료를 확인한다.

🔈 wait( )

  • child process가 실행중일 때 parent를 잠시 중지시킨다.
  • child의 exit()인자값이 wait()의 인자값 status로 들어간다.
#include <sys/wait.h>

pid_t wait(int *status);

/* 성공시 child의 process ID를, 실패시 -1을 반환 */

🔖 errno : ECHILD - child process가 존재하지 않을 때 발생하는 오류

    int status;
    pid_t cpid;

    cpid = fork();  /* create new process */
    if(cpid ==0){
       /* child */
       /* do something ... */
    } else {
       /* parent, so wait for child */  
       cpid = wait(&status);
       printf(“The child %d is dead\n”, cpid);
    }
    ------------------------------------------------------------------
    pid_t pid;
    int status;

    pid = wait(NULL)      /* ignore status information */

    pid = wait(&status);  /* status will contain status information */

  • 자식의 종료 상태 확인
    main() {	 
       pid_t pid;
       int status, exit_status;

       if((pid=fork()) < 0) fatal("fork failed");

       if(pid==0){	/*자식*/
          /* 이제 수행을 4초동안 중단시키기 위해 라이브러리 루틴 sleep을 호출 한다 */
          sleep(4);
          exit(5); /* 0이 아닌값을 가지고 퇴장 */
       }

       /* 여기까지 수행이 진전된 바 이것은 부모임. 자식을 기다린다. */
       if((pid=wait(&status)) == -1){
          perror("wait failed");
          exit(2);
       }

       /* 자식이 어떻게 죽었는지 알기 위해 테스트한다 */
       if (WIFEXITED(status)){ //WIFEXITED : 정상종료인가 아닌가
          exit_status = WEXITSTATUS(status); //status의 뒤 5bit를 추출하는 매크로
          printf("Exit status from %d was %d\n", pid,exit_status); 
          //여기서 exit_status는 5이다.
       }
       exit(0);
    }

- exit status
Macro설명
WIFEXITED(status)정상종료시 0이아닌 값을 출력한다.
WIFSIGNALED(status)signal로 인한 종료면 0이아닌 값을 반환하고 WTERMSIG를 이용하여 종료를 일으킨 SIGNAL의 종류를 알 수 있다.
WIFSTOPPED(status)child가 중단되면 0이아닌 값을 반환한다.
WIFCONTINUED(status)child가 실행중이면 0이아닌 값을 반환한다.

  • exit status 확인
    #include <sys/wait.h>

    void pr_exit(int status)
    {
       if (WIFEXITED(status)) //program의 정상종료를 확인한다.
          printf("normal termination, exit status = %d\n",
             WEXITSTATUS(status));
       else if (WIFSIGNALED(status))
          printf("abnormal termination, signal number = %d%s\n",
             WTERMSIG(status),
       else if (WIFSTOPPED(status))
          printf("child stopped, signal number = %d\n",
             WSTOPSIG(status));
    }

🔈 waitpid( )

  • child가 끝날 때까지 기다리지않는다.
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *statloc, int options); 

/* 성공시 child process ID, 실패시 -1을 반환 */
  • pid
    • pid == -1 : 아무 child 기다림, wait과 같다.
    • pid > 0 : 입력된 pid를 가진 child가 끝나는걸 기다린다.
    • pid == 0 : 호출한 프로세스의 group ID와 같은 child process를 기다린다.
    • pid < 0 : child가 속한 group의 groupID와 같은 프로세스 대기.
  • option
    • WNOHANG : child가 실행중이지만 exit(0)을 반환시켜 그대로 진행하게한다.

  • waitpid를 호출하여 자식이 어떻게 종료되었는지 확인하는 코드
    main(){
       pit_t pid;
       int status, exit_status;

       if((pid=fork()) < 0) fatal("fork failed");

       if(pid==0){  /* 자식 */
          /* 이제 4초동안 중단시키기 위해 라이브러리 루틴 sleep을 호출한다 */
          sleep(4);
          exit(5);  /* 0이 아닌 값을 가지고 퇴장한다 */
       }
       /* 여기까지 수행이 진전된 바 이것은 부모임. 따라서 자식이 퇴장했는지 확인한다.
          퇴장하지 않았으면, 1초 동안 수면한 후 다시 검사한다. */
       while(waitpid(pid, &status, WNOHANG) == 0){  
          printf("Still waiting... \n");
          sleep(1);
       }

       /* 자식이 어떻게 죽었는지 알기 위해 테스트한다 */
       if (WIFEXITED(status)){
          exit_status = WEXITSTATUS(status);
          printf("Exit status from %d was %d\n", pid,exit_status);
       }
       exit(0);
    }



🔊 Zombie

🔈Zombie process

  • parent가 wait()중인데 child가 그냥 끝나버린 상태로 wait()을 계속 호출하는 상태이다.
    while(pid=fork()){
    	count = 0;
        /* 3개의 child를 생성한다. */
       if(++count==3) break;
    }

    if (pid == 0){
       sleep(5);
       printf("I will be back %d\n", getpid());
       exit(0);	/* 자식은 먼저 죽는다 */
    }
    else if(pid > 0){
       printf("Im parent %d\n", getpid());
       printf("Press any key\n");
       getchar();	/* 키보드 입력을 기다리는 함수로 child가 죽을 때까지 기다린다. */
    }

🔈Orphan process

  • child process의 종료 전에 parent process가 종료된 상황.
  • 이 child process는 init process(pid=1)의 자식이 된다.
  • init process는 주기적으로 문제가 발생한 process가 있는지를 확인한다.
    while(pid=fork()){
       if(++count==3) break;
    }

    if (pid == 0){
       printf("I will be back %d\n", getpid());
       sleep(500);
       exit(0);
    }
    else if(pid > 0){
       printf("Im parent %d\n", getpid());
       printf("Press any key\n");
       getchar();	/* 먼저 엔터를 치고 부모는 종료한다 */
    }



🔊 Process Attributes

  • process id
    #include <unistd.h>

    pid_t getpid(void);
                      Returns: process ID of calling process

    pid_t getppid(void);
                   Returns: parent process ID of calling process

    uid_t getuid(void);
                    Returns: real user ID of calling process

    uid_t geteuid(void);
                   Returns: effective user ID of calling process

    gid_t getgid(void);
                   Returns: real group ID of calling process

    gid_t getegid(void);
                  Returns: effective group ID of calling process
  • process ID 0 : program에 실행중인 process가 없다
  • process ID 1 : init process, /sbin/init에 있으며 죽지않는다.
    static int num = 0;
    static char namebuf[20];
    static char prefix[] = "/tmp/tmp";

    char *gentemp(void){
       int length;
       pid_t pid;

       pid = getpid();	/* 프로세스 식별번호를 얻는다. */

       /* 표준 문자열처리 루틴들  <string.h> */
       strcpy (namebuf, prefix);
       length = strlen(namebuf);

       /* 파일 이름에 프로세스 식별번호(pid)를 추가한다. */
       itoa (pid, &namebuf[length]);	/* filename : /tmp/tmp#pid */

       strcat (namebuf, ".");
       length = strlen (namebuf);
       do{
        itoa (num++, &namebuf[length]); 	/* 접미 번호를 추가한다. */
       } while (access(namebuf, F_OK) != -1); /* test for existence of file */

       return (namebuf); 		/* filename : /tmp/tmp#pid.#num */
    }

Process groups and process group-ids

  • gid와 egid는 user 차원이고 process들의 집합인 group에서의 pgid는 다르다.
  • process group leader : pid == pgid
#include <unistd.h>

pid_t getpgrp(void);

/* 현재 process의 pgid 반환*/

pid_t getpgid(pid_t pid);	/* getpgid(0) == getpgrp() */

/* 성공시 pgid, 실패시 -1 반환 */

Changeing process group

#include <unistd.h>

int setpgid(pid_t pid, pid_t pgid);
 
/* 성공시 0, 실패시 -1 반환 */
  • pid == pgid : process group leader가 된다.
  • pid == 0 : 자신의 pgid값을 두번째 인자로 바꾼다.
  • pgid == 0 :
  • process는 자기자신 혹은 child의 pgid를 바꿀 수 있다.
  • child가 exec 호출시 child의 pgid는 바꿀 수 없다.

  • parent(102)가 child(103)을 독립시키려한다.
    • setpgid(103, 0)
  • child(105)가 스스로 독립하고자 한다.
    • setpgid(0,0)
  • pid = 107이 session 독립을 하려한다.
    • setsid()

🔊 Session과 session-id

  • process group을 묶은 집합
  • 같은 shell에서 생성된 process들은 같은 session이다.
  • &을 이용하여 오래걸리는 process를 background 실행이 가능하여 여러개의 background process와 하나의 foreground process로 이루어진다.

🔈 getsid( )

    #include <unistd.h>

    pid_t getsid(pid_t pid);
    
    /* 성공시 session leader의 pid를, 실패시 -1을 반환*/
  • pid == 0 : session leader를 출력한다.
  • 같은 session은 같은 terminal을 공유한다.
  • Daemon process : 새로운 session을 만들어 독립하였을 때, terminal을 소유하지 않는 process이다. exit 실행시에도 죽지않는다.

🔈 setsid( )

    #include <unistd.h>

    pid_t setsid(void);

    /* 성공시 pgid, 실패시 -1 반환 */
  • 만약 process group leader가 아닌 process가 이 함수를 호출하면 새로운 session이 생성된다.

🔊 current working & root directory

  • current working directory
    • fork나 exec 호출시 자동으로 상속된다.
    • child가 chdir를 호출하여 위치를 바꾸어도 parent의 current working directory는 변하지않는다.
  • current root directory
    • file system 계층구조의 시작점

🔈 chroot( )

#include <unistd.h>
int chroot(const char *path); 

/* 성공시 0, 실패시 -1을 반환 */

  • 현재 프로세스에서만 해당 프로세스의 root를 변경할 수 있다.
    int main() {
        int pid;

        if (chroot("/home/mydir") != 0){	/* ‘/’ == ‘/home/mydir’ */
            perror("chroot");
            exit(0);
        }

        if (execl("/bin/bash","bash", NULL) == -1) /* ‘/home/mydir/bin/bash’ */         		perror("error");
    }

Changin User id and Group id

    #include <unistd.h>

    int setuid(uid_t uid);
    int setgid(gid_t gid);

    /* 성공시 0, 실패시 -1을 반환 */

0개의 댓글

관련 채용 정보