ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 운영체제 7: 코드를 통한 프로세스 작동 방식의 이해
    CS기초/OS,HW 2022. 5. 30. 23:59
    728x90
    반응형

    다음 C코드를 통해 코드를 실행하면 프로세스가 어떻게 동작하는지 이해해보자.

     

    //모듈 임포트
    #include <unistd.h>
    #include <sys.types.h>
    #include <sys.stat.h>
    #include <fcntl.h>
    
    //main함수 시작
    int main()
    {
    	int fd:
    	fd = open("data.txt".O_RDONLY): //Read only로 data.txt파일을 연 호출값을 fd라는 변수에 담음
    	if(fd == -1) //파일이 정상적으로 열리지 않았다면
    	{
    		printf("Error: can not open file\n"); //다음 문장을 출력하고 함수 종료
    		return 1;
    	}
    	else
    	{
    		printf("File opened and now close_\n"); //다음 문장을 출력하고 파일을 닫은 뒤 함수 종료
    		close(fd);
    		return ();
    	}
    }

     해당 코드를 컴파일해서 생성된 실행 파일은 다음과 같이 구성된다. (프로세스 구조 포스팅 참고) 

    • Stack영역 (가변 사이즈) : 해당 실행파일(프로그램)이 실행되면(프로세스) 포인터 변수들이 저장될 공간
    • Heap영역 (가변 사이즈) : 지역 변수의 값이 저장될 공간
    • BSS영역 (고정 사이즈) : 전역 변수 또는 정적 변수가 저장되는 공간
    • DATA영역 (고정 사이즈) : 전역 변수 또는 정적 변수가 저장되는 공간
    • Text영역 (고정 사이즈) : 코드가 저장되는 공간

    (일단 위 코드에서 main함수 밖에 선언한 변수는 없으므로 Data나 BSS영역에 들어가는 변수는 없다.)

     

     먼저 프로그램이 실행되면 해당 프로세스는 코드를 Text영역에 담고 상태를 ready로 변경해서 프로세스가 실행가능한 상태임을 스케줄러에 알리고, CPU에 들어갈 때까지 스케줄러의 부름을 기다린다.

     선점형 스케줄러로 일정 시간마다 타이머 인터럽트를 통해 프로세스를 바꿔준다고 가정하자. 현재 A라는 프로세스가 CPU에서 실행이 시작되었고 0.01초 마다 타이머 인터럽트가 발생한다고 할 때 위의 C코드 프로세스로의 컨텍스트 스위칭을 위해 다음과 같은 과정이 진행된다 (자세한 동작 방식은 인터럽트 포스팅 참고).

    1. CPU가 사용자 모드를 커널 모드로 변경한다
    2. IDT(Interrupt Descriptor Table - OS가 부팅할 때 만들어짐. 번호마다 실행해야할 커널 함수 주소가 적혀있는 테이블)에서  타이머 인터럽트 번호에 해당하는 함수 주소를 찾고 해당 함수 주소에 맞는 함수를 찾아서 실행한다.
    3. 해당 함수에서 타이머 인터럽트가 5번 호출되면 컨텍스트 스위칭이 일어나도록 스케줄링이 되어 있다고 한다면 1~2번의 과정이 4번 더 반복된다.
    4. 다섯 번째로 타이머 인터럽트가 호출되었을 때, 타이머 인터럽트 함수가 스케줄러 함수를 호출해서 현재 실행되던 A 프로세스의 PCB정보를 메인 메모리에 저장하고 running상태에서 ready상태로 변경한 후 대기하고 있던 위의 C 프로그램을 running상태로 변경하여 실행하면서 PCB정보를 메인메모리에서 로드한다.
    5. fd 지역 변수를 stack영역에 넣는다.
    6. open()함수를 호출하기 위해 헤더파일을 찾아간다. 헤더파일의 open함수는 다음과 같이 작성되어 있다.
    mov eax, 1
    mov ebx, 0
    int 0x80

    위의 코드는 다음과 같이 해석할 수 있다.

    • eax 뒤에 오는 숫자는 시스템 콜 번호이다.
    • ebx 뒤에 오는 숫자는 시스템 콜 인자이다.
    • int는 시스템이 제공하는 op code (CPU 명령어)이다. int는 interrupt를 의미한다.
    • int뒤는 인터럽트의 번호이다.

    인터럽트가 호출되었으므로 다음과 같은 과정이 일어난다.

    1. CPU가 사용자 모드를 커널 모드로 바꾼다.
    2. IDT에서 0x80에 해당하는 주소의 함수를 찾아서 실행한다. 리눅스에서 0x80은 system_call()함수이다.
    3. system_call() 함수에서 eax로부터 시스템 콜 번호를 찾아서 해당 번호에 맞는 시스템콜 함수인 sys_open()으로 이동하여 실행한다.
    4. CPU에서 DMA에 저장매체에서 파일을 불러오라고 요청하면서 해당 프로그램의 상태를 running에서 waiting으로 변경한다.
    5. DMA가 system bus를 통해 해당 저장매체에 접근을 해 data.txt파일을 open하고 CPU에 데이터가 다  처리가 되었음을 알린다.
    6. CPU가 프로그램의 상태를 다시 변경하기 위한 인터럽트가 발생하고, 프로그램의 상태가 waiting에서 ready로 변경되며, 다시 위에서 가정한 컨텍스트 스위칭 정책에 기반하여 0.05초가 지나면 running으로 변경되어 다음 코드를 실행한다.

     

     위를 통해 하나의 프로그램이 실행될 때 사용자 모드와 커널 모드의 전환과 시스템 콜의 호출이 굉장히 빈번하게 일어난다는 것을 이해할 수 있어야 한다. 또한 IO에는 시간이 굉장히 오래 걸리므로 빈번한 IO는 오버헤드를 초래할 수 있기 때문에 이를 최소화하여 한번의 IO로 메모리에서 처리할 수 있도록 프로그램 구조를 설계하는 것이 좋다.

    728x90
    반응형

    댓글