[Linux]Process

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

🔊 Program layout

  • int main(int argc, char *argv[])
  • argc : 인자 개수
  • argv[] : 인자의 포인터를 담은 배열

  • exec가 호출되면 kernel에서 program이 실행된다.
  • main에서 반환되는 값을 exit(main(..));을 받아 종료된다.
  • command-line argument, enviroment와 sets



🔊 main Function

  • command-line argument
    #include <stdio.h>

    int main(int argc, char *argv[]) 
    { 
       int i; 
       for (i = 0; i < argc; i++) /* echo all command-line args */ 
          printf("argv[%d]: %s\n", i, argv[i]); 
       exit(0); 
    } 

  • Environment List
    • character pointer를 담은 배열의 리스트
    • extern char **environ;
    • 변수에 접근하기 위해서 getenv 또는 putenv를 호출해야한다.



memory layout

  • TEXT(code)
  • 초기화된 data segment
  • 초기화되지 않은 data segment(bss)
  • Stack : 지역변수
  • Heap : 동적할당 메모리

  • bbs는 disk에 저장되지 않는다.
  • program은 text segment와 초기화된 data만 저장한다.



🔊 Process

  • 실행된 program으로 process와 program은 다르다.
  • 코드와 변수와 register, stack으로 이루어져있다.
  • Process는 pid를 갖는다.
  • process를 생성하고 관리할 수 있다. : fork, exec, wait, exit, ...

🔈 getpid( ) & getppid( )

  • 모든 process는 고유한 process ID를 갖게되는데 한 프로세스 내에서 고유한 값을 가지며 이미 이용한 ID는 재사용되기도 한다.
	#include <unistd.h>

    pid_t getpid(void);
    /* 현재 프로세스의 id를 반환한다. */

    pid_t getppid(void);
    /* 현재 프로세스의 parent process id를 반환한다. */

🔈 fork( )

  • child는 parent의 data space, heap, stack를 공유하지 않고 복사하여 사용하며 text 부분은 공유한다.
    #include <unistd.h>

    pid_t fork(void);

    /* child process는 0, child의 parent process는 process ID, 실패시 -1을 반환 */
    
    
    pid_t pid;
    pid = fork();

🔈exec( )

  • 새 프로그램을 초기화할 때 이용된다.
  • exec사용시에 program 생성이 되는 것이 아니기 때문에 process ID는 변하지않는다.
    #include <unistd.h>

    int execl(const char *pathname, const char *arg0,/*NULL*/ );

    int execv(const char *pathname, char *const argv[]);

    int execle(const char *pathname, const char *arg0,/*NULL*/,char *const envp[]);

    int execve(const char *pathname, char *const argv[], char *const envp[]);

    int execlp(const char *filename, const char *arg0,.../*NULL*/ );

    int execvp(const char *filename, char *const argv[]);

     /* All six return: -1 on error, no return on success */
  • p : file 이름이 인자, 나머지는 pathname
  • e : environ 인자 이용
  • l : 인자를 하나하나 적고 NULL로 끝낸다.
  • v : 배열을 이용한다.

  • ls 수행을 위해선 execl 이 수행된다.
    #include <unistd.h>
    main(){
        printf("executing ls\n");

        execl("/bin/ls", "ls", "-l", (char *)0);

        /* 만일 execl이 복귀하면, 호출은 실패한 것이다..*/

        perror("execl failed to run ls");
        exit(1);
    }
  • 첫번째 인자 실행, argv[0] - ls(앞 인자와 같아서 무시), argv[1] - optional, NULL

  • ls 수행을 위해선 execv 이 수행된다.
    #include <unistd.h>

    main()
    {
        char *const av[]={"ls", "-l", (char *)0};

        execv("/bin/ls", av);

        /* 반복하거니와 여기까지 수행이 진행되면 오류를 의미 */
        perror("execv failed");
        exit(1);
    }



🔊 exec와 fork 사용

  • 부모 프로세스에서 ls를 수행한다.
    #include <unistd.h>

    int fatal(char *s){
       perror(s);
       exit(1);
    }

    main(){
       pid_t pid;
       switch (pid = fork()){
       case -1:
          fatal("fork failed");
       case 0:
          /*자식이 exec을 호출 */
          execl("/bin/ls", "ls", "-l", (char*)0);
          fatal("exec failed");
          break;
       default:
          /* 부모가 자식이 끝날 때까지 수행이 일시 중단하기 위해 wait을 호출 */
          wait((int*)0);
          printf("ls completed\n");
          exit(0);
       }	
    }


- shell command를 실행한다.
    int docommand(char *command)
    {
        pit_t pid;
        if((pid = fork()) < 0 )
            return (-1);

        if(pid==0) /* child */
        {
            execl("/bin/sh", "sh", "-c", command, (char *)0);
            perror("execl");
            exit(1);
        }

        /* code for parent */
        /* wait until child exits */
        wait((int *)0);
        return(0);
    }



🔊 데이터와 file descriptor 상속

  • parent가 접근 가능한 file은 child 또한 접근이 가능하다.
  • child가 write하면 parent의 offset은 변경된다.
    main(){
       int fd;
       pid_t pid;
       char buf[10]; 
       
       if((fd = open("data", O_RDONLY)) == -1)
          fatal("open failed");
		
       /* buf에 data 저장 */
       read(fd, buf, 10);
       printpos("Before fork", fd); //10

       switch(pid = fork()) {
       case -1:    /* 오류 */
          fatal("fork failed");
          break;
       case 0:     /* 자식 */
          printpos("Child before read", fd);  //10
          read(fd, buf, 10);
          printpos("Child after read", fd); //20
          break;
       default:    /* 부모 */
          wait((int *)0);
          printpos("Parent after wait", fd); //20
       }
    }

🏷️ parent와 child의 차이

  • fork()의 반환값
  • process ID
  • time관련 정보
  • file locks set

🔈 exec 와 open files

  • fork이후 exec 호출시 부모와 같은 fd를 써서 문제가 발생할 수 있음
  • close-on-exec flag : parent의 fd가 자동으로 close하게한다.
    #inlucde <fcntl.h>
    .
    .
    int fd;
    fd = open(“file”, O_RDONLY);
    .
    .
    fcntl(fd, F_SETFD, 1);	/* close-on-exec 플래그를 on으로 설정 */
    fcntl(fd, F_SETFD, 0);	/* close-on-exec 플래그를 off으로 설정 */
    res = fcntl(fd, F_GETFD, 0);	/* res==1:on, res==0:off */

🖇️ example

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

      fd = open(“test”, O_RDONLY);

      pid = fork();

      if(pid == 0){
        execl(“exec",“child",0);
      }
      if(pid<0){
        perror("MAIN PROCESS");
      }

      wait(NULL);
      printf("MAIN PROCESS EXIT\n");
      close(fd);

      return 0;
    }
    
    /* child, exec.c */
    int main(int argc, char** argv)
    {
      if( read(3, buff, 512) < 0){
        perror("EXEC PROCESS");
        exit(1);
      }

      printf("EXEC PROCESS EXIT\n”);

      return 0;
    }

0개의 댓글

관련 채용 정보