유저 모드:
커널 모드:
유저 모드는 시스템 자원 권한 없고
커널 모드는 있다
나누면 안전하니까
유저 스택 (User Stack):
커널 스택 (Kernel Stack):
시스템 콜(System Call)
유저 모드에서 실행 중인 프로그램이 운영체제의 기능을 필요로 할 때, 시스템 콜을 통해 커널 모드로 전환됨. (ex. 파일 읽기, 쓰기, 네트워크 통신, 프로세스 제어 등)
인터럽트(Interrupts)
페이지 폴트(Page Fault)
프로그램이 현재 메모리에 없는 데이터를 접근하려 할 때 발생. 메모리에서 요청한 데이터가 없을 경우, 운영체제는 하드 디스크에서 데이터를 가져와 메모리에 로드해야 하며, 이 과정에서 커널 모드로 전환됨.
예외(Exception) 처리
CPU는 잘못된 명령어나 예외가 발생할 경우, 이를 처리하기 위해 커널 모드로 전환됨.
컨텍스트 스위칭(Context Switching)
운영체제가 여러 프로세스를 스케줄링하기 위해 현재 실행 중인 프로세스를 다른 프로세스로 전환할 때 커널 모드로 진입. 컨텍스트 스위칭은 프로세스의 실행 상태를 저장하고 새로운 프로세스의 상태를 복원하는 과정이 필요하기 때문.
Trap
Exception
Exception은 trap을 포함하는 더 넓은 개념. 동기적으로 발생하며, 주로 소프트웨어의 실행 중에 발생하는 예상치 못한 사건.
동기적 발생은 프로그램이 0으로 나누기를 시도할 때, 해당 명령어를 실행하는 순간 즉시 트랩(예외)이 발생하여 CPU가 그 문제를 처리합니다. 즉, 명령어와 예외 발생이 직접적인 관계를 맺고 있습니다.
반면, 비동기적(asynchronous) 이벤트는 프로그램의 실행 흐름과 관계없이 발생하는 경우를 의미합니다. 예를 들어, 하드웨어 인터럽트는 CPU가 다른 작업을 하고 있을 때도 외부 장치(예: 키보드 입력)로 인해 언제든지 발생할 수 있습니다.
https://therefrom.tistory.com/44
동기적인 방식은 요청과 결과가 순차적으로 일어남.
현재 작업 여부와 무관하게 다음 작업을 실행하고, 완료함.
인터럽트(Interrupt)는 하드웨어 또는 소프트웨어가 CPU에 즉각적인 주의를 요구하는 신호로, 현재 실행 중인 작업을 일시 중단하고 더 긴급한 작업을 처리하도록 하는 메커니즘입니다. 이 방식은 컴퓨터가 여러 작업을 동시에 처리할 수 있도록 돕는 중요한 역할을 합니다.
인터럽트의 종류
마스크 가능 인터럽트, 마스크 불가능 인터럽트
인터럽트 처리 과정
인터럽트와 폴링(Polling)의 차이
세그멘테이션 폴트(Segmentation Fault)는 프로그램이 접근 권한이 없는 메모리 주소를 읽거나 쓸 때 발생하는 오류입니다. 이 오류는 주로 시스템이 메모리 보호 기능을 통해 잘못된 메모리 접근을 감지하고, 이를 방지하기 위해 프로세스를 강제 종료할 때 발생합니다.
세그멘테이션 폴트가 발생하는 주요 원인
운영체제의 역할
세그멘테이션 폴트가 발생하면, 하드웨어는 잘못된 메모리 접근을 감지하고 운영체제에 이를 알립니다. 운영체제는 이 신호를 받아서 해당 프로그램을 종료하고, 추가적인 시스템 손상을 방지합니다. 이때 코어 덤프(Core Dump)라는 파일이 생성될 수 있는데, 이 파일은 프로그램의 메모리 상태를 저장하여 개발자가 오류를 분석하는 데 도움을 줍니다.
세그멘테이션 폴트를 예방하는 방법
malloc
, free
, scanf
등)를 사용할 때 정확한 방식으로 호출해야 하며, 입력 검증을 통해 잘못된 메모리 접근을 방지해야 합니다.근데 왜 segmentation fault라는 이름이 붙었어? access fault가 더 어울리는 이름 같은데. 분할 폴트는 뭔가 이상하잖아
"Segmentation Fault"라는 이름은 메모리 관리 방식 중 하나인 세그멘테이션(Segmentation)에서 유래한 용어입니다. 이 개념을 이해하려면 먼저 메모리 구조와 운영체제가 메모리를 어떻게 관리하는지 살펴볼 필요가 있습니다.
세그멘테이션이란?
세그멘테이션은 프로그램이 사용하는 메모리를 여러 세그먼트(segment)로 나누어 관리하는 방식입니다. 일반적으로 메모리는 여러 개의 구역으로 나뉘어 있으며, 각 구역은 특정 용도로 사용됩니다. 주요 세그먼트는 다음과 같습니다:
malloc
등)으로 사용되는 영역입니다.왜 Segmentation Fault라고 부르나?
"Segmentation Fault"라는 이름은 프로그램이 자신이 할당받지 않은 세그먼트 또는 세그먼트 외부의 메모리를 잘못 참조했을 때 발생하는 오류에서 비롯되었습니다. 즉, 메모리 세그먼트 간의 경계를 침범하여 잘못된 주소에 접근하려고 할 때, 하드웨어와 운영체제가 이를 감지하고 프로그램의 접근을 차단하는 것입니다.
예를 들어:
이러한 접근이 허용되지 않아 세그멘테이션 폴트가 발생합니다.
레지스터랑 메모리는 둘 다 기본적으론 데이터 저장하는 거.
레지스터(Register):
메모리(Memory):
https://plummmm.tistory.com/113
산술 연산 레지스터
AX : 산술 논리 연산에 사용된다. 그리고 함수의 리턴값이 여기 저장됨.
BX : 간접 번지 지정 시에 사용된다. 뭔말?? 배열의 인덱스 값을 저장하는거지.
CX : 반복문의 반복 횟수 지정 시에 사용된다. (counter) 그리고 문자열 처리에도 사용됨.
DX : AX의 보조적인 역할이다. AX와 합쳐져서 확장된 메모리로 사용하는 것. 그리고 I/O 어드레스를 지정할 때 사용됨.
인덱스 레지스터
SI(Source index) : 복사나 비교를 할때 사용되는 소스 문자
DI(Destination index) : 역시나 복사나 비교를 할때 사용되는 목적지 문자. stos, movs를 사용할때 마다 1씩 증가한다.
위와 같은 용도로 쓰인다.
보면 접두사가 R 이 붙었는데.. 64비트일 때 R 접두사가 붙고 32비트 일때는 E 접두사가 붙는다.
리얼 모드 즉 16비트 모드에서는 접두사가 붙지 않는 레지스터를 사용한다고 생각하면 되고,
보호모드도 마찬가지로 E 접두사가 붙는 레지스터를 사용한다고 생각해면 무방하다.
RAX 레지스터의 발전
RAX의 역할
하위 레지스터
RAX는 하위 버전인 EAX(32비트), AX(16비트), AL(8비트)로 나눠 사용할 수 있다. 예를 들어, 64비트 RAX의 하위 32비트에 접근할 때는 EAX를 사용하며, 하위 8비트를 참조할 때는 AL을 사용할 수 있다. 이러한 하위 레지스터는 하위 호환성을 유지하면서 특정 데이터 부분만 접근하거나 수정할 때 유용하다.
프로그램은 시스템 콜을 통해 운영체제에서 제공하는 서비스를 요청할 수 있다.
프로그램이 시스템 자원 직접 못 건드리게 해서 보안과 안정성 유지.
시스템 콜의 동작 방식
시스템 콜의 주요 예
fork()
, exec()
, wait()
와 같은 함수로 프로세스를 생성, 실행, 대기하는 작업을 수행.open()
, read()
, write()
, close()
와 같은 함수로 파일을 열고, 데이터를 읽고 쓰며, 파일을 닫음.ioctl()
같은 시스템 콜이 사용됨.파일 디스크립터는 파일을 읽거나 쓰기 위해 read(), write()와 같은 시스템 콜을 통해 사용됨
운영 체제는 프로세스가 파일을 열 때마다 식별자를 하나 만들고,
무슨 파일을 가리키고 있는지 등의 정보와 함께 테이블에 기록한다.
각 프로세스들도 파일을 열면 식별자를 만들고 자신의 파일 식별자 테이블에 기록한다.
기본적으로 파일 식별자 테이블의 0,1,2는 이미 할당되어 있다.
각각 표준 입력, 표준 출력, 표준 오류이다.
실제로 함수에서 사용하고 싶을 땐
기호 상수 STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO을 사용하는 것이 가독성을 위해 좋다.
자주 사용되는 데이터를 빠르게 접근할 수 있도록 임시로 저장하는 메모리. 데이터 접근 속도를 향상시키기 위해 사용. CPU나 웹 브라우저와 같은 여러 컴퓨팅 시스템 요소가 캐시를 사용하여 성능을 극대화.
캐시는 작고 빠른 메모리로, 메인 메모리(RAM)나 저장 장치보다 훨씬 더 빠르게 데이터를 읽고 쓸 수 있음. 프로그램이 데이터를 요청할 때, 먼저 캐시에 데이터가 있는지 확인함.
만약 데이터가 캐시에 존재한다면(캐시 히트), 바로 데이터를 반환.
캐시에 데이터가 없을 경우(캐시 미스), 더 느린 메모리나 저장 장치에서 데이터를 가져와 캐시에 저장한 후 데이터를 반환.
원자적 연산(Atomic Operation)은 실행되는 동안 다른 작업에 의해 방해받지 않는 연산. 다중 스레드 환경에서 데이터 일관성이 보장됨. 예를 들어, 여러 스레드가 동시에 변수에 접근하여 값을 변경할 때, 원자적 연산을 사용하면 다른 스레드가 그 변수를 사용하는 도중에 값이 변경되지 않도록 보호됨.
대부분의 현대 하드웨어와 프로세서, 운영체제는 이러한 원자적 연산을 위한 특별한 명령어(예: compare-and-swap, fetch-and-add)를 지원.
주로 하드웨어 기반으로 구현해서 단일 명령어가 완료될 때까지 다른 스레드나 프로세스가 해당 메모리 영역에 접근하지 못하게 함.
https://www.geeksforgeeks.org/32-bit-vs-64-bit-operating-systems/
In computing, there are two types of processors existing, i.e., 32-bit and 64-bit processors. These types of processors tell us how much memory a processor can access from a CPU register. For instance,
A 32-bit system can access 232 different memory addresses, i.e. 4 GB of RAM or physical memory ideally, it can access more than 4 GB of RAM also.
A 64-bit system can access 264 different memory addresses, i.e. actually 18-quintillion bytes of RAM. In short, any amount of memory greater than 4 GB can be easily handled by it.
프로세스는 운영체제에서 실행 중인 프로그램의 인스턴스임.
프로세스는 CPU에서 실행되기 위해 필요한 데이터와 코드를 포함하는 여러 자원(예: 메모리, 파일 핸들, 실행 중인 코드 등)을 가지고 있으며, 각각 독립된 주소 공간을 할당받음.
운영체제는 각 프로세스를 관리하기 위해 프로세스 제어 블록(PCB)이라는 구조체를 유지하며, 여기에는 프로세스의 상태, 메모리 관리 정보, CPU 사용 시간 등의 정보가 저장됨.
from collections import deque
T = int(input())
for _ in range(T):
N, K = map(int,input().split())
time = 0
time_table = list(map(int,input().split()))
prev_table = [[] for _ in range(N+1)] # 짓기 전에 지어야 할 건물
next_table = [[] for _ in range(N+1)] # 지은 후에 지어야 할 건물
for _ in range(K):
a, b = map(int,input().split())
prev_table[b].append(a)
next_table[a].append(b)
win = int(input())
cnt = 0
q = deque()
q.append(win)
while q:
b = q.popleft()
for other_b in q:
time_table[other_b] -= time_table[b-1]
cnt += time_table[b-1]
time_table[b-1] = 0
# 최소 건설 시간을 갖고 있는 놈들만 빼내오도록
if b == win:
break
for next_b in next_table[b]: # 다음 건물 탐색
can_build = True
for prev_b in prev_table[next_b]:
if time_table[prev_b] != 0:
can_build = False
break
if can_build:
q.append(next_b)
print(time)
# 건설 시간 제일 큰 놈이 있으면 그 친구 끝날때까지 다른 쪽에선 쭉쭉 진행 가능
# 큐에서 건설 시간 제일 작은 놈 꺼내서 큐에 있는 건물들 전부 해당 건물 시간만큼 뺀 다음에 (cnt도 더해줌)
# 큐에 다음으로 연결된 건물을 넣는데, 이전 건물 테이블을 순회하며 타임 테이블이 전부 0인지 확인
# 0이 아니면 못 넣음
# 만약 최종 건물 만들었으면 끝
최소 건설 시간 작은 놈을 꺼내는 것의 연산 비용이 너무 높아보이고,
거꾸로 시작하는게 아니라 정방향으로 시작하면
처음 시작 건물이 여러개일 가능성 있음
더 생각해봐야할듯. 일단 핀토스 할 것.
N=int(input())
L = [list(input()) for _ in range(N)] # 이거 받는 법 또 까먹었었음
L2 = [[0]*N for _ in range(N)]
for i in range(N):
for j in range(N):
if L[i][j] == 'R' or L[i][j] == 'G':
L2[i][j] = 'R'
else:
L2[i][j] = 'B'
# 정상인
cnt = 0
dx, dy = [1,-1,0,0], [0,0,1,-1]
for i in range(N):
for j in range(N):
if L[i][j] != -1:
a = L[i][j]
stack=[(i,j)]
cnt += 1
while stack:
x, y = stack.pop()
# a = L[x][y] 여기에 넣었더니 -1이 무한반복. 이미 -1로 변한 놈이 스택에서 나올 수 있음.
L[x][y] = -1
for k in range(4):
if (0<= x+dx[k] < N) and (0<= y+dy[k] < N):
if L[x+dx[k]][y+dy[k]] == a:
stack.append((x+dx[k],y+dy[k]))
# 적록색약
cnt2 = 0
for i in range(N):
for j in range(N):
if L2[i][j] != -1:
a = L2[i][j]
stack=[(i,j)]
cnt2 += 1
while stack:
x, y = stack.pop()
L2[x][y] = -1
for k in range(4):
if (0<= x+dx[k] < N) and (0<= y+dy[k] < N):
if L2[x+dx[k]][y+dy[k]] == a: # 여기 L2로 안 바꿔줌
stack.append((x+dx[k],y+dy[k]))
print(cnt, cnt2)
# 어려움 : 적록 색약인 놈이 보는 것도 추가해야 함
# visited 배열 추가했으면 L2 새로 만들 필요 없었음
from collections import deque
def BFS(x,y):
q.append((x,y))
dx = [-1,0,1,0]
dy = [0,1,0,-1]
visited[x][y] = 1
while q:
x, y = q.popleft()
for d in range(4):
nx = x + dx[d]
ny = y + dy[d]
# 인덱스 범위 안에 있으면서, 같은 색이면서, 방문 안한 경우
if 0<=nx<N and 0<=ny<N and a[nx][ny] == a[x][y] and not visited[nx][ny]:
visited[nx][ny] = 1 # 방문체크 후 큐에 넣음
q.append((nx,ny))
N = int(input())
a = [list(input()) for _ in range(N)]
q = deque()
# 적록색약 아닌 경우
visited = [[0] * N for _ in range(N)]
cnt1 = 0
for i in range(N):
for j in range(N):
if not visited[i][j]: # 아직 방문 안한 경우만 체킹
BFS(i,j)
cnt1 += 1
# 적록색약인 경우
for i in range(N):
for j in range(N):
if a[i][j] == 'G':
a[i][j] = 'R'
visited = [[0] * N for _ in range(N)]
cnt2 = 0
for i in range(N):
for j in range(N):
if not visited[i][j]:
BFS(i,j)
cnt2 += 1
print(cnt1, cnt2)
이 사람 코드 좀 잘 짠듯.