시스템 프로그래밍 9-2(Exceptional Control Flow)

TonyHan·2021년 5월 25일
0

=== Processes ===

1. Processes

작동중인 프로그램의 instance이다.
program : 우리가 짠 텍스트 파일 -> obj -> memory load(loader) -> 실행 -> 프로그램은 active instance가 되어서 process라고 한다.
processor은 cpu를 말한다.

즉 프로그램은 우리가 짠 텍스트 파일이고
프로세스는 메모리에 올라간 프로그램을 이야기 한다.
프로세서는 cpu이고

  • Definition: A process is an instance of a running program.
    • One of the most profound ideas in computer science
    • Not the same as “program” or “processor”

각 프로그램은 두가지의 중요한 개념이 있다.

  • Process provides each program with two key abstractions:
    • Logical control flow : 각 프로그램은 cpu를 배타적으로 혼자 사용하는 것처럼 실행을 한다.
      • Each program seems to have exclusive use of the CPU
      • Provided by kernel mechanism called context switching
    • Private address space
      - Each program seems to have exclusive use of main memory.
      - Provided by kernel mechanism called virtual memory
  • Private address space : cpu안에는 register가 있는데 register set을 사용해서 프로그램이 실행된다. 레지스터는 현재 실행하는 프로그램의 context이다.
  • 이 프로그램을 address space 관점에서 보면 Data와 Code 부분 그리고 Stack과 Heap은 run time시 사용되는 공간이다. 프로그램을 loader에 의해 메모리에 적재시 Data와 Code부분이 적재되고 OS에 의해서 실행이 되면 Stack, Heap에 동적으로 할당해서 사용하게 된다. 이게 마치 자기만의 private space가 있는것처럼 작동한다.
  • 실재로는 프로그램이 전체 공간을 사용하는 것이 아니다.

2. Multiprocessing: The Illusion

  • 마치 각 프로세스가 내가 CPU, 메모리를 독점해서 사용하는 것처럼 환상을 가지고 돌아가게 된다. 각 프로그램이 실행이 되면 CPU는 한번에 하나밖에 돌아갈 수 밖에 없지만 프로세스들은 각자 독점해서 사용하는 것이라는 환상을 가지고 돌아가게 된다. 굉장히 빠르게 time sharing하면서 switching을 하기 떄문에 프로세스들은 이를 느끼지 못한다.
  • Computer runs many processes simultaneously
    • Applications for one or more users
      • Web browsers, email clients, editors, …
    • Background tasks
      • Monitoring network & I/O devices

Multiprocessing Example

모든 프로세스는 ID가 존재한다. 이것을 PID라고 한다. cpu 사용률도 있다.

  • Running program “top” on Mac
    • System has 123 processes, 5 of which are active
    • Identified by Process ID (PID)

Multiprocessing: The (Traditional) Reality

  • single processor을 가지고 이해하면 cpu가 메모리에 적재된 프로그램을 실행하게 되는 것이다. time sharing을 하기 때문에 하나의 프로그램만을 돌릴 수가 없다.

  • cpu 안의 register은 프로그램 실행을 위해 cpu가 사용하기 위한 메모리이다. 여기에는 명령어, 계산값, PC, SP 값 등등이 들어가 있다.

  • interrupt가 발생했다면 interrupt handler가 발생한다. 그때 handler는 context switching을 하고 그 안에서는 또 schedule이라는 함수를 호출해서 그 다음에 수행한 프로그램을 선택하고 실행하는데 cpu안의 값을 저장해야 한다. 그때 저장공간이 각 프로세스 별로 Saved register이 있어서 거기에 저장을 한다. 그리고 다음 프로세스로 가면 saved register에 저장된 값들을 cpu 레지스터에 넘기어 준다. 그리고 프로그램을 실행시킨다.

  • Single processor executes multiple processes concurrently

    • Process executions interleaved (multitasking)
    • Address spaces managed by virtual memory system
    • Register values for nonexecuting processes saved in memory

  • Save current registers in memory

  • Schedule next process for execution

  • Load saved registers and switch address space (context switch)

CPU는 하나인데 돌아가는 프로세스가 너무 많아서 OS가 복잡해진것이다.

  • Multicore processors
    • Multiple CPUs on single chip
    • Share main memory (and some of the caches)
    • Each can execute a separate process
      • Scheduling of processes onto cores done by kernel

3. Concurrent Processes

  • 두 개의 프로세스가 함께 실행되고 있으면 concurrent하다고 하고 그렇지 못한것을 sequential하다고 한다.

  • Each process is a logical control flow.

  • Two processes run concurrently (are concurrent) if their flows overlap in time

  • Otherwise, they are sequential

  • Examples (running on single core):

    • Concurrent: A & B, A & C
    • Sequential: B & C

    위와 같은 경우를 보고 sequential하다고 한다. 동시에 실행되는게 없으니.

User View of Concurrent Processes

  • Control flows for concurrent processes are physically disjoint in time
  • However, we can think of concurrent processes as running in parallel with each other


반대로 위와 같이 A, B가 겹치는 것을 보고 concurrent하게 실행된다고 이야기 한다. 그래서 이런 것에 대해 우리는 병렬적으로 처리가 도니다고 생각해 볼 수 있다.

Context Switching

context switching 이란 현재 진행하고 있는 Task(Process, Thread)의 상태를 저장하고 다음 진행할 Task의 상태 값을 읽어 적용하는 과정을 말합니다

  • Processes are managed by a shared chunk of memory-resident OS code called the kernel
    • Important: the kernel is not a separate process, but rather runs as part of some existing process.
  • Control flow passes from one process to another via a context switch


processA가 실행되다가 timer interrupt가 발생하면 메모리에 processA값을 저장하고 processB로 넘어가며 Context Switch가 발생한다. 이것은 kernel이 수행하게 된다.
kernel은 process에서 일부처럼 사용하는 함수호출이라고 생각하면 된다.

=== Process Control ===

1. System Call Error Handling

  • On error, Linux system-level functions typically return -1 and set global variable errno to indicate cause.

  • Hard and fast rule:

    • You must check the return status of every system-level function
    • Only exception is the handful of functions that return void
  • Example:

  • fork에 해당하는 system call handler는 kernel code이다. 보통 system call은 에러발생시 -1이 반환되고 errno에 에러를 발생시킨 원인들을 기록한다.

  • status를 보고 항상 error를 handling해주어야 한다. void를 return 하는 경우는 굉장히 적다.

Error-reporting functions

  • Can simplify somewhat using an error-reporting function:
void unix_error(char *msg) /* Unix-style error */
{
	fprintf(stderr, "%s: %s\n", msg, strerror(errno));
	exit(0);
}
if ((pid = fork()) < 0)
	unix_error("fork error");

위와 같은 방식으로 error reporting을 보다 간단하게 구현가능

Error-handling Wrappers

  • We simplify the code we present to you even further by using Stevens-style error-handling wrappers:
pid_t Fork(void)
{
	pid_t pid;
	if ((pid = fork()) < 0)
		unix_error("Fork error");
	return pid;
}
pid = Fork();

위의 방식으로 더더욱 간단하게 만들 수 있다. Fork를 띄어서 pid를 반화난다.

Obtaining Process IDs

  • pid_t getpid(void) : 현재 프로세스의 id를 반환해준다.
    • Returns PID of current process
  • pid_t getppid(void) : 프로세스의 부모의 pid를 반환해준다.
    • Returns PID of parent process

2. Creating and Terminating Processes

From a programmer’s perspective, we can think of a process as being in one of three states

프로세스는 다음 3가지 상태가 존재한다.

  • Running : executing, waiting하고 있는 상태를 이야기 한다. scheduling이 되기 위해 기다리는 상태

    • Process is either executing, or waiting to be executed and will eventually be scheduled (i.e., chosen to execute) by the kernel
  • Stopped : suspended 되어 있는 상태를 이야기 한다. signal을 받기 전까지 기다리는 상태이다.

    • Process execution is suspended and will not be scheduled until further notice (next lecture when we study signals)
  • Terminated : main함수에서 종료, exit 받은 경우 terminate를 받은 경우

    • Process is stopped permanently

Terminating Processes

  • Process becomes terminated for one of three reasons:
    • Receiving a signal whose default action is to terminate (next lecture)
    • Returning from the main routine
    • Calling the exit function

main함수에서 종료, exit 받은 경우 terminate를 받은 경우

  • void exit(int status)
    • Terminates with an exit status of status
    • Convention: normal return status is 0, nonzero on error
    • Another way to explicitly set the exit status is to return an integer value from the main routine

exit status를 가지고 종료. 보통은 0 일반적이지 않으면 error값

  • exit is called once but never returns.

exit은 한 번 불리고 절때 return 안한다.

Creating Processes

  • Parent process creates a new running child process by calling fork

(중요)fork를 하면 자식 프로세스를 만들고 자식은 0을 반환 부모는 자식의 pid를 반환한다.

  • int fork(void)
    • Returns 0 to the child process, child’s PID to parent process
    • Child is almost identical to parent:
      • Child get an identical (but separate) copy of the parent’s virtual address space.
      • Child gets identical copies of the parent’s open file descriptors
      • Child has a different PID than the parent

child는 부모와 동일한 코드를 가지게 된다. stack도 동일. 그래서 자식은 부모의 가상 주소 space를 복사해온다. 또한 parent의 file descriptor들도 그대로 복제한다.

child는 부모와 다른 ID를 가지게 된다.

  • fork is interesting (and often confusing) because it is called once but returns twice

fork Example

int main()
{
	pid_t pid;
	int x = 1;
    
	pid = Fork(); 
	if (pid == 0) { /* Child */
		printf("child : x=%d\n", ++x); 
		exit(0);
	}
    
	/* Parent */
	printf("parent: x=%d\n", --x); 
	exit(0);
}

Fork()를 하면 동일한 프로세스가 복제된다. 부모는 자식의 return 값을 받고 자식은 0을 반환한다.

그래서 부모는 아래 부모 부분 코드가 실행되고 자식 프로세스는 child부분 코드가 실행되어 아래와 같은 결과가 된다.

fork는 한번 호출했지만 return은 2개, 두 개의 프로세스가 concurrent하다. 그리고 address space 분리된게 복제가 된다. file descriptor은 동일하게 share하고 있다.

  • Call once, return twice
  • Concurrent execution
    • Can’t predict execution order of parent and child
  • Duplicate but separate address space
    • x has a value of 1 when fork returns in parent and child
    • Subsequent changes to xare independent
  • Shared open files
    • stdout is the same in both parent and child

Modeling fork with Process Graphs

  • A process graph is a useful tool for capturing the partial
    ordering of statements in a concurrent program:

    • Each vertex is the execution of a statement
    • a -> b means a happens before b
    • Edges can be labeled with current value of variables
    • printf vertices can be labeled with output
    • Each graph begins with a vertex with no inedges
  • Any topological sort of the graph corresponds to a feasible total ordering.

    • Total ordering of vertices where all edges point from left to right

Process Graph Example

Interpreting Process Graphs


갑자기 끝으로 가는게 아닌한 b에서는 인접한 c나 e로 갈 수 있다.

fork Example: Two consecutive forks

void fork2()
{
	printf("L0\n");
	fork();
	printf("L1\n");
	fork();
	printf("Bye\n");
}


왼쪽 출력결과는 L1을 반드시 찍어야 하는데 바로 Bye로 갔기 떄문에 실행 불가능하다고 생각할 수 있다.

fork Example: Nested forks in parent

fork Example: Nested forks in children

profile
신촌거지출신개발자(시리즈 부분에 목차가 나옵니다.)

0개의 댓글