728x90
간단한 정리
8. 예외적인 제어흐름
- 시스템들은 또한 내부 프로그램 변수에 의해 표시되지 않으며, 프로그램의 실행과는 반드시 관련되어 있지 않은 시스템의 상태변화에도 반응할 수 있어야 한다.
- 현대의 시스템들은 제어흐름의 갑작스러운 변화를 만드는 방법으로 이런 상황에 반응한다.
- 일반적으로 이와 같은 급격한 변화를 예외적인 제어흐름(ECF : Exceptional Control Flow)라고 한다.
8.1 예외상황
- 예외상황은 부분적으로 하드웨어와 운영체제에 의해 구현된 예외적인 제어흐름의 한 가지 형태이다.
- 예외상황은 어떤 프로세서 상태의 변화에 대한 대응이다.
- 상태의 변화 → 이벤트라고 한다.
- 프로세서가 이벤트가 발생했다는 것을 감지하면, 예외테이블이라고 하는 점프테이블을 통해서 이 특정 종류의 이벤트를 처리하기 위해 특별히 설계된 운영체제 서브루틴(예외처리 핸들러)으로 간접 프로시저 콜을 하게 된다.
8.1.1 예외처리
- 한 시스템 내에서 가능한 예외상황의 종류마다 중복되지 않은 양의 정수를 예외번호로 할당하고 있다. 시스템 부팅시 운영체제는 예외 테이블을 할당하고 초기화 한다. 엔트리 k가 예외상황 k에 대한 핸들러를 갖는다.
- 런타임에 프로세서가 이벤트를 감지하고 대응되는 예외번호를 결정한다. 프로세서는 그 후 예외 테이블의 엔트리 k를 통해 간접 프로시저 콜을 하는 방법으로 예외상황을 발생시킨다.
- 예외상황과 프로시저의 중요한 차이
- 제어가 사용자 프로그램에서 커널로 전환하고 있을 때 모든 아이템은 사용자 스택 위가 아니라 커널 스택위로 푸시된다.
- 예외 핸들러는 커널모드에서 돌아간다.
- 예외의 종류
- 비동기 : 인터럽트
- 동기 : 트랩, 오류, 중단
인터럽트
- 프로세서 외부에 있는 입출력 디바이스로 부터의 시그널의 결과로 비동기적으로 발생한다.
- ex) ctrl+c, ctrl + alt + f4, 컴퓨터 리셋
트랩과 시스템 콜
- 의도적인 예외상황, 어떤 인스트럭션을 실행한 결과로 발생한다.
- 트랩의 가장 중요한 사용은 시스템 콜이라고 알려진 사용자 프로그램과 커널 사이의 프로시저와 유사한 인터페이스를 제공한다.
- 시스템 콜은 커널모드에서 돌아간다.
- ex) system calls, break point traps
- 시스템 콜
- 운영체제의 커널이 제공하는 서비스에 대하여, 응용 프로그램의 요청에 따라 커널에 접근하기 위한 인터페이스다.
- 각 시스템 콜은 오프셋에 대응되는 유일한 정수를 가진다.
오류(fault)
- 핸들러가 정정할 수 있을 가능성이 있는 에러조건으로부터 발생한다.
- 핸들러가 에러를 정정할 수 있다면, 제어를 오류를 발생시킨 인스트럭션으로 돌려주어서 거기서부터 재실행한다. 그렇지 않다면 핸들러는 커널 내부의 abort 루틴으로 리턴해서 오류를 발생시킨 응용 프로그램을 종료한다.
중단(abort)
- 하드웨어 오류 같이 복구 불가능한 치명적인 에러에서 발생한다.
- 핸들러는 응용 프로그램으로 제어를 리턴하지않고 종료하는 루틴으로 반환한다.
8.2 프로세스
- 프로세스는 운영체제가 만들어주는 실행 프로그램의 인스턴스다.
- 시스템 내의 프로그램은 어떤 프로세스의 문맥(context)에서 돌아간다.
- 문맥은 프로그램이 정확하게 돌아가기 위해 필요한 상태로 구성된다.
- 사용자가 실행 목적 파일의 이름을 쉘에 입력해서 프로그램을 돌릴 때마다 쉘은 새로운 프로세스를 생성하고, 실행 목적 파일을 새로운 프로세스의 문맥에서 실행한다.
프로세스에서 제공하는 추상화
- 논리적 제어흐름 : 우리의 프로그램이 프로세서를 혼자서 사용한다는 착각이 들게하는 독립적인 제어흐름
- 사적인 주소공간 : 프로그램이 혼자서 메모리를 사용한다는 착각을 제공하는 사적 주소공간
논리적 제어흐름
- 하나의 프로세서를 사용해서 여러 프로세스들이 교대로 돌아간다.
- 각 프로세스는 자신의 흐름이 일 부분을 실행하고 나서, 다른 프로세스들로 순서를 바꾸어 실행하는 동안 일시적으로 정지 된다.
동시성 흐름
- 자신의 실행시간이 다른 흐름과 겹치는 논리흐름을 동시성 흐름이라고 부른다.
- 그들의 실행시간이 중첩되면 동시에 실행된다고 한다.
문맥전환
- 한 개의 프로세스에서 다른 프로세스로 제어흐름이 넘어가는 것
- 운영체제 커널은 예외적인 제어흐름의 상위 수준 형태인 문맥전환을 사용하여 멀티태스킹을 구현한다. 커널은 프로세스가 실행되는 동안의 어떤 시점에 현재 프로세스를 선점(일시적으로 정지)하고, 이전에 선점된 프로세스를 다시 시작할 것을 결정할 수 있다.
- 이런 결정을 스케줄링이라고 하며, 스케줄링은 스케줄러라고 부르는 커널 내부 코드에 의해 처리된다.
- 문맥전환은 커널이 사용자 대신해서 시스템 콜을 실행하고 있을 때 일어날 수 있다. 또한 문맥전환은 인터럽트의 결과로 발생할 수 있다.
프로세스의 제어
- 프로세서를 제어하기 위한 많은 시스템 콜
- 프로세스 ID 가져오기
- 프로세스 생성 & 종료
- 자식 프로세스 제거
- 프로그램의 로딩과 실행
프로세스 ID가져오기
- 각각의 프로세스는 고유한 ID(PID)를 갖는다.
- getpid 함수는 호출하는 함수의 PID를 리턴한다.
프로그램 생성 & 종료
- 생성 - fork
- 부모 프로세스는 fork 함수를 불러서 자식 프로세스를 생성한다. 완벽하게는 아니지만 부모와 거의 동일하다. 가장 중요한 차이는 서로 다른 PID를 가진다
- fork 함수는 두 번 리턴한다
- 호출한 부모에게 자식의 PID를 리턴한다. 자식에게는 0을 리턴한다.
- 종료 - exit
좀비 프로세스
- 종료 되었지만 아직 처리되지 않은 프로세스
- 자식 프로세스가 부모 프로세스보다 먼저 종료되는 경우 해당 자식 프로세스는 좀비 프로세스가 된다.
- wait함수를 통해 처리해줘야 한다.
고아 프로세스
- 부모 프로세스가 자식 프로세스보다 먼저 종료되는 경우, 해당 자식 프로세스는 고아 프로세스라고 한다. 이 경우 커널에 의해 init 프로세스에게 입양된다.
- init프로세스는 PID 1번이며, 결코 종료되지 않는다.
- 만일 어떤 부모 프로세스가 자신의 좀비 자식들을 소거하지 않고 종료하려면, 커널은 이 init 프로세스가 이들을 소거하도록 한다.
새 프로그램의 로딩과 실행 - execve
- 현재 프로그램의 컨텍스트 내에서 새로운 프로그램을 로드하고 실행한다.
- 파일 이름을 찾을 수 없는 에러가 있는 경우에만 호출하는 프로그램으로 리턴한다.
- 이외의 경우 execve는 절대 리턴하지 않는다.
프로그램 vs 프로세스
- 프로그램은 코드와 데이터가 합쳐진 것으로, 디스크 상에 목적파일이나 주소공간에 세그먼트로 존재 할 수있다.
- 프로세스는 실행 중에 있는 프로그램의 특정 사례로서 프로그램은 항상 어떤 프로세스의 컨텍스트 내에서 돌아간다.
- ex)fork 함수는 부모의 복제인 새로운 자식 프로세스에서 동일한 프로그램을 실행시킨다.
- ex)evecve는 새로운 프로그램을 현재 프로세스의 컨텍스트 내에서 로드하고 실행한다. 따라서 execve로 실행되는 새로운 프로그램은 기존 프로세스와 동일한 PID를 가진다.
시그널
- 이벤트가 시스템에 발생했다는 것을 프로세스에게 알려주는 짧은 메세지
- 시그널은 예외상황과 인터럽트를 커널에서 추상화한 개념으로써 대부분의 경우 커널이 프로세스에게로 시그널을 보내주는 형태를 가진다.
시그널 송신
- 커널은 목적지 프로세스의 컨텍스트 내에 있는 일부 상태를 갱신해서 시그널을 목적지 프로세스로 보낸다.
시그널 수신
- 목적지 프로세스는 전달된 신호에 대해서 커널이 어떤 방식으로든 반응하여야 할 때 시그널을 수신한다고 한다.
Pending
- 송신 했지만 아직 수신되지 않은 시그널을 펜딩 시그널이라고 한다.
- 어느 특정한 시그널에 대해서는 최대 한 개의 시그널 만이 존재할 수 있다.
프로세스 그룹
- 모든 프로세스는 정확히 한 개의 프로세스 그룹에 속하며, 이것은 양수 pgID로 식별한다.
- 기본적으로 자식은 부모와 같은 프로세스 그룹에 속하며, 쉘은 각각 job마다 별도의 프로세스 그룹을 만든다.
시그널 받기
- 커널은 프로세스 p에 대해 블록되지 않은 펜딩 시그널 집합을 체크한다.
- 만일 하나라도 존재하는 경우 p가 시그널 k를 수신해서 시그널 처리 작업을 수행한다.
- 1-2 반복 집합이 0이 될 때 까지
- 집합이 빈다면 커널은 제어를 p의 다음 인스트럭션에게 전달한다.
핸들러의 중단
- 핸들러는 다른 핸들러에 의해 중단될 수 있다.
- 핸들러 실행 도중 다른 핸들러가 실행되더라도, 모든 작업을 마친 후에는 되돌아와서 나머지 작업을 수행한다.
- 마지막으로 응용 수준에서 C프로그램은 정상적인 콜/리턴 스택 방식과 분기를 통과해서 하나의 함수에서 다른 함수로 직접 분기하기 위해 비지역성 점프를 사용할 수 있다.
백준
10813
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
int n, m, temp;
cin >> n >> m;
vector<int> a(n);
for (int i = 0; i < n; ++i)
{
a[i] = i + 1;
}
for (int x = 0; x < m; ++x)
{
int i, j;
cin >> i >> j;
temp = a[i-1];
a[i-1] = a[j-1];
a[j-1] = temp;
}
for (int i = 0; i < a.size(); ++i)
{
cout << a.at(i)<<" ";
}
return 0;
}
10811
#include <bits/stdc++.h>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
int n, m, temp;
cin >> n >> m;
vector<int> a(n);
for (int i = 0; i < n; ++i)
{
a[i] = i + 1;
}
for (int x = 0; x < m; ++x)
{
int i, j;
cin >> i >> j;
for (i; i < j; ++i)
{
temp = a[i - 1];
a[i - 1] = a[j - 1];
a[j - 1] = temp;
--j;
}
}
for (int i = 0; i < a.size(); ++i)
{
cout << a.at(i)<<" ";
}
return 0;
}
728x90
'Study > TIL(Today I Learned)' 카테고리의 다른 글
24.02.17 묵시적 리스트, 백준, C++ (1) | 2024.02.18 |
---|---|
24.02.16 간단한 정리, 코드리뷰에 대해, 백준, C++ (0) | 2024.02.17 |
24.02.14 간단한 정리, 백준, C++, Keyword (2) | 2024.02.14 |
24.02.13 CSAPP 9 (3) | 2024.02.14 |
24.02.12 CSAPP 8, Stack & Queue 구현 (3) | 2024.02.13 |