1. 프로세스 프로세스라는 주제는 크게 두 가지 소주제로 나뉩니다. 프로세스 관리 프로세스: 하나의 프로세스에 대한 메모리 레이아웃, 프로세스 계층 스레드와 동시성: 하나의 프로세스는 내부적으로 여러 개의 실행 스레드를 가집니다. CPU 스케줄링: 실행되는 프로세스가 수십~수백개가 생길텐데, 어떤 프로세스를 우선으로 실행(처리)할까요? 🤔 프로세스 동기화 프로세스간 데이터 동기화: 경쟁 상태(race condition), lock-free 알고리즘 데드락: 요새 CPU는 멀티코어기 때문에 여러 프로세스가 병렬로 실행되는데, 이때 프로세스들끼리 서로가 가진 자원을 탐낸다면? 2. 메모리 메모리라는 주제는 크게 두 가지 소주제로 나뉩니다. 메인 메모리(RAM) 메모리 레이아웃: 프로세스마다 독립된 메모리 공..
1. 데드락이란? 데드락이란 둘 이상의 프로세스 혹은 스레드가 서로 상대방의 작업이 끝나기를 기다려 결국 이러지도 저러지도 못하고 무한정 기다리고만 있는 상태를 말합니다. 동시에 접근하면 안되는 공유자원에 접근할때 스레드는 Lock 기법을 이용합니다. 그러고 공유 자원의 사용이 끝났을 때 UnLock을 하는데, UnLock을 하기 전 교착 상태가 발생해 UnLock을 영원히 하지 못하는 상태를 보고 이름을 데드락으로 지은게 아닌가 생각해봅니다.. 🤔 2. 데드락 발생 조건 데드락은 아래 4가지 조건이 모두 만족될 시 발생합니다. 상호 배제 (Mutual Exclusion) - 공유 자원에 여러 프로세스가 동시에 접근할 수 없다. 점유 상태로 대기 (Hold and Wait) - 자원을 가지고 있는 상태에..
시스템 콜 커널에서 제공하는 저수준의 API를 사용하는걸 시스템 콜이라고 하는데, 이 호출이 어떤 식으로 이뤄지는지 생각해본적이 있는가? 시스템 콜은 커널의 코드를 실행하겠다는 뜻인데, 사용자 영역의 앱이 직접적으로 커널의 코드를 실행할 수 있을까? 물론 그럴 수 없다. 그럼 어떻게 ? 사용자 영역의 앱이 시스템 콜을 사용하려한다는 시그널을 커널로 보낼 수 있다. ( 아키텍처마다 약간씩 메커니즘이 다를 수는 있는데 기본 원리는 같음 ) i386 아키텍처에서는 인터럽트 명령에 0x80 값을 넣어 주면 시스템 콜을 사용한다는 뜻. MOV EAX, 5 INT 0x80 위에서 인터럽트 명령 전에 EAX 레지스터에 5를 담았는데, 5는 open() 시스템 콜을 의미한다. ( 각각의 시스템 콜마다 정해진 숫자값이 ..
본 포스팅은 다음의 사이트를 참고하였습니다. http://progtrend.blogspot.com/2018/06/ubuntu-1804-uim.html Ubuntu 18.04 버전의 디폴트 입력기는 IBus입니다. 하지만, 여러 블로그들의 IBus 한글 입력 설정방법들을 보고 따라해봐도 한글은 입력이 되는데 글자가 입력이 되질 않았습니다. 무슨 말이냐면, ㅁㄴㅇㄹ ~/.Xmodmap
프로세스 관리 프로세스는 유닉스 시스템에서 파일 다음으로 중요한 개념입니다. 알다시피 프로세스는 활성화되어 실행 중인 살아있는 코드 객체입니다. 유닉스는 새로운 바이너리 이미지를 메모리에 적재하는 과정에서 프로세스를 생성하는 부분을 분리했습니다. 예를 들어 Windows는 새로운 프로세스를 생성하기 위해 CreateProcess() 함수 1개를 호출하면되지만, 유닉스는 fork(), exec 류의 시스템콜 2가지를 사용해야 합니다. 프로세스 계층 새로운 프로세스를 생성하는 프로세스를 '부모 프로세스'라 하고, 새롭게 생성된 프로세스를 '자식 프로세스'라고 합니다. 리눅스에선 'init 프로세스'를 제외한 모든 프로세스는 부모 프로세스가 있습니다. 자신의 pid 값과 부모의 pid 값을 알고 싶다면 아래의..
메모리 맵 파일 프로그래밍은 데이터를 입력받아 처리하고 출력하는 작업이고, 이 데이터는 파일로써 디스크에 존재합니다. 내가 만약 '회원명단.csv'란 엑셀 파일을 가지고 여러 데이터 처리 작업을 해야한다고 가정해봅시다. 그렇다면 해당 파일에서 '읽기', '쓰기'를 할 때마다 디스크까지 접근해야할텐데 이러면 속도가 너무 느리지 않을까요? 만약 그 파일이 디스크가 아닌 메모리에 있다면 접근 속도는 엄청나게 향상되지않을까요? 이러한 발상은 최소한의 디스크 접근을 위한 버퍼 입출력에서 한 단계 더 나아간 발상입니다. '버퍼'란 개념이 아닌 디스크-메모리간의 '페이지(page)' 개념을 활용한 방식으로, 프로세스의 가상 메모리 주소 공간에 파일을 매핑한 뒤 가상 메모리 주소에 직접 접근하는 것으로 파일 읽기/쓰기..
버퍼 입출력 - 표준 입출력 라이브러리 파일 시스템의 최소 저장 단위는 '블록'이라는 추상 개념입니다. 따라서, 모든 입출력 연산은 블록 크기의 정수배에 맞춰서 일어납니다. 단지 1Byte를 읽고싶다하더라도, 512Byte만큼(1블록 = 512Byte라 가정) 읽어와야한다는 얘기입니다. 또는 내가 단지 2.5블록만큼(대략 1250Byte) '쓰기' 연산을 하고싶다하더라도 3블록에 대해 '쓰기' 연산을 해야한다는 얘기입니다. 그런데 잘 생각해보면 사용자 애플리케이션에서는 512Byte 단위로 입출력 연산이 이루어지지 않는 경우가 대부분입니다. 보통 CSV 파일을 다루기 위해 '필드' 단위의 입출력 연산이 필요하다거나, JSON 파일을 다루기 위해 단순히 '문자열' 단위의 입출력 연산이 필요한 경우가 대부분..
가상 파일 시스템 프로그래밍을 하다보면 빠지지 않는 것이 파일 처리인데요, 혹시 파일 작업을 하기 위해 read(), write()와 같은 시스템콜을 사용하면서 "왜 매개변수로 어떤 파일 시스템인지 넘겨주지않지?"라고 생각해보신적 있으신가요? 사실 조금만 생각해보면 답이 금방나오는 질문입니다. 이유는 파일 시스템이 추상화되어있기 때문입니다. 구조를 표현하면 다음 그림과 같습니다. 파일 시스템간의 공통된 인터페이스를 둠으로써 얻는 장점은 여러 가지가 있습니다. 1. 동일한 방법으로 접근이 가능하다. 2. 파일 시스템간의 이식성이 좋아진다. 3. 새로운 파일 시스템이 추가되더라도 설계를 변경할 필요가 없다.
다중 입출력 - poll() poll() 시스템콜은 유닉스 운영체제의 최초 상용화 버전 중 하나인 'Unix System V'에서 제공하는 다중 입출력 방식이었습니다. 리눅스에서 제공하고 있던 select() 시스템콜보다 더 좋았던 까닭에 리눅스에서도 poll() 시스템콜을 도입하였습니다. select() 시스템콜의 단점을 보완한 poll() 시스템콜이지만, 기존에 select()로 다중 입출력을 구현했던 개발자의 습관과 타 시스템으로의 이식성을 이유로 덜 사용된다고 합니다. #include int poll(struct pollfd* fds, nfds_t nfds, int timeout); struct pollfd { int fd; // 파일 디스크립터 short events; // 감시할 이벤트 sho..
다중 입출력 - select() '다중 입출력'은 프로그램(단일 스레드)에서 여러 개의 파일을 작업하고자 할 때 사용할 수 있는 메커니즘입니다. 사실 '단일 스레드'에서 여러 개의 파일을 작업하고자 할 때 사용할 수 있는 다른 방법으로는 '논블록(Non-Block) 입출력'을 사용하는 방법도 있긴 합니다만, '논블록(Non-Block) 입출력'은 프로그래밍 작업이 까다롭습니다. 이제부터 설명할 'select'는 '블록/비동기적 입출력'에서의 '다중 입출력' 모델입니다. A, B, C, D 4개의 파일을 다루는 작업을 하고싶다고 가정해볼때 다음과 같은 시나리오를 생각해볼 수 있습니다. 1. A 파일에 대한 작업을 합니다. 2. A 파일에 대한 작업을 마칩니다. 3. B 파일에 대한 작업을 합니다. 4. B..
파일 입출력 유닉스 시스템에서는 거의 모든 것을 파일로 표현하므로 '파일 입출력'은 매우 중요한 부분입니다. 알다시피 파일은 '읽기'나 '쓰기' 전에 반드시 '열기(open)'를 해야합니다. 그리고 커널은 '파일 테이블'이라고 하는 프로세스별로 열린 파일 목록을 관리합니다. 각 프로세스에는 기본적으로 0, 1, 2 값을 가지는 파일 디스크립터가 open되어 있습니다. 0 - 표준 입력(stdin) 1 - 표준 출력(stdout) 2 - 표준 에러(stderr) 위의 3가지 파일 디스크립터를 직접 참조하는대신 C 라이브러리는 #define 매크로 정의를 제공합니다. 따라서, 해당 파일 디스크립터를 사용하고싶다면 아래 정의된 매크로로 참조하는게 좋습니다. #define STDIN_FILENO 0 #defin..