2019. 6. 19. 12:29ㆍIT
리눅스 커널 내부 구조
** Bus(버스) 란? 컴퓨팅 시스템의 각 모듈에서 발생한 신호를 공유해서 사용할 수 있도록 만든 신호의 집합..
address bus, control bus , data bus 가 있다.
Chapter 1.
Structure of Linux Kernel
what is the OS?
OS is a resource manager.
then, What is the resource?
1. Physical resource - CPU, Memory, Disk, Terminal, Network etc.
2. Abstract resource - task, segment, page, Protocol, packet etc.
In the Linux kernel, there are 5 parts of managements.
First of all, Kernel is a resource manager, and Remember existing ,
1. Task management
2. Memory Management
3. File System
4. Network Management
5. Device Driver Management
Chapter 2.
How to make Linux kernel (3 steps)
1. Configuration Kernel - It is stored in .config & autoconf.h (make menuconfig)
2. Compile - Make (command of text editor called 'vim')
3. Installation - Copying kernel image on the Root file system and modifying BootLoader.
Chapter 3.
- Task: Unit of resource ownership ( == Thread) - 자원 소유권의 단위
- Process: Program of operating - 동작중인 프로그램.
- Thead: Unit of Performance - 수행의 단위.
- Program: form of executable files. -> compile 과정을 거쳐 얻어진 binary 수행(실행)에 필요한 자료들의 집합.
- Scheduling: 여러개의 프로세스 중 CPU자원을 어느 Process 에 할당해 줄 것인가를 결정하는 작업.
ex) ps 해서 보이는건 process, ls 해서 보이는건 프로그램(실행파일->그저 Disk에 저장되어있는 수동적인 존재에 불과하다.).
=> 프로그램이 수행되기 위해서는 리눅스 커널로부터 CPU 등의 자원을 할당 받을 수 있는 동적인 개체가 되어야한다.
이 동적인 객체가 process이다.
- 프로세스와 스레드의 생성과 수행.
Fork Example Source
Result:
global 변수의 g=2가 있고, 지역변수 l=3 이 있다. 여기서 fork 를 해서 자식 pid 17572가 생겼고, g++, l++이 각각 실행되어 3,4 가 출력되었다. 그리고 마지막 pid 의 g, l 값을 보면 자식 프로세스와 별개라는 것을 알수 있다.
*****
이를 통해, 프로세스가 생성되면 주소 공간을 포함하여 이 프로세스를 위한 모든 자원들이 새로이 할당됨을 알 수 있다.
따라서, 자식 프로세스의 연산 결과는 자식 프로세스 주소 공간의 변수에만 영향을 줄 뿐, 부모 프로세스 주소 공간의 변수에는 영향이 없으며, 결국 지역 변수, 전역 변수 등의 값이 다르게 출력된다.
*****
- 새로운 프로세스의 수행 ( execl() )
리눅스는 Task의 수행을 위해 execl() 이라는 시스템 호출을 제공한다.
Result:
우선 , fork() 로 새로운 프로세스를 생성하고,
생성된 프로세스에서 "Before execl" 문자열 출력한 뒤,
execl() 을 호출하여 앞에서 만든 fork 바이너리 파일을 실행한다. 그 결과 앞에서 출력되었던 결과내용이 출력된다.
그런데 "After execl" 은 출력되지 않았다.
이는 execl() 이 성공적으로 수행되면, 프로세스의 수행이미지( text, data, stack 등 ) 가 기존의 것 (기존의 프로세스) 에서 새로운 프로세스 (앞의 fork 프로세스) 로 바뀌며, 이 때문에 "After execl" 를 출력하는 printf() 는 수행되지 않게 된다! ( 실행하는 프로세스의 전환? 이라고 생각하면 될듯. )
+++
execl() 은 기존에 사용하던 프로세스의 주소공간을 모두 없애고, 요청된 binary 를 기반으로 새로운 주소 공간을 생성한다. fork() 이후 바로 execl() 이 요청되었다면 결국 ... fork() 때 수행했던 부모 프로세스의 주소 공간을 복사하여 자식 프로세스의 주소공간을 따로 만들어 주었던 작업이 불 필요한 작업이 되고 만다. (?????????????)
+++
++ 추가 예제 : execl () 인자 넘겨주기.
** 아래는 my_cat.c 이다. gcc my_cat.c -o my_cat 으로 compile 해 실행 binary 이름을 my_cat 으로 해주었다.
./my_cat 을 실행하는데, my_cat 이 매개변수 argv 3개를 입력& 출력 하도록 되어잇다. execl 을 통해 AAAA, BBB, CCC 를 넘겨주면 ./my_cat 을 실행함과 3개의 매개변수를 넘겨준다.
Result :
++ 추가 예제2 : execl (), gets() 사용한 예제.
Result:
기존소스에서 argument를 2개 받는다. my_cat 은 argv[1] 에는 파일 명이 들어가 이것을 cat 하는 기능으로,
execl 의 첫번째 cmd 가 실행 binary 인 my_cat 을 입력한것이고, 두번째 cmd 에는 cat 할 파일의 이름을 입력한다. 그럼 이 2개의 cmd 매개변수가 전달되어 " ./my_cat test " 라는 bash 를 실행하게 되는것이다. 그리고 my_cat.c 에서 return 3을 해주도록 되어있는데,
execl.c 에서
printf("$? : %d\n", (status>>8)&0xFF );
로 status 의 하위 bit 값을 찍어주도록 했다.
** status : 자식이 exit 로 종료될 때의 상태정보
- 정상종료일 경우: 하위 8bits 는 0, 상위 8 bits 는 exit status(자식 프로세스가 exit 명령으로 전달한 값)
- signal 로 종료일 경우 : 하위 8bits는 signal 번호, 상위 8bit 는 0.
0000 0000 | 0000 0000
exit code | signal
execl.c 에서, pid_t pid = fork(); 바로 윗 줄에
if(strcmp(cmd, "exit") == 0) break;
를 넣어주면 , exit 을 입력하면 빠져나온다. 이런식으로 ftp 나 tftp 에서 bye 나 quit 을 입력할 때 어떻게 프로세스가 종료되는지 이해할 수 있다.
*** fork() , vfork(), COW (Copy on Write) ***
1. fork()란?
- 유닉스/리눅스에서는 새로운 프로세스를 생성하기 위해서 fork() 함수를 호출합니다.
호출을 통해 새로 생성된 프로세스를 흔히 자식 프로세스(Child Process)라고 호출한 프로세스를 부모 프로세스(Parent Process)라고 합니다.
- fork() 함수는 특성상 한번 호출을 통해 부모 프로세스와 자식 프로세스에게 두번 리턴되어지게 됩니다.
부모 프로세스에게는 자식 프로세스의 ID를 리턴하고, 자식 프로세스에게는 0을 리턴합니다.
자식 프로세스는 부모의 PID를 알고 싶을때는 getpid()를 호출할 수 있습니다.
2. vfork()란?
- 보통 fork() 함수의 호출을 통해 자식 프로세스의 데이터 부분은 부모 프로세스의 데이터 부분의 복사본을 갖게 됩니다.
fork()의 일반적인 사용형태를 보면 fork() 이후에 exec() 의 호출을 통해 새로운 바이너리로 교체하여 수행을 합니다.
따라서 fork()를 통해 부모 프로세스의 복사본을 만드는 작업은 불필요한 오버헤드가 되며,
이런 경우에 사용하는 시스템 호출이 바로 vfork()라고 합니다.
3. 개선된 fork()
- 하지만, fork 가 항상 메모리를 복사한다는 것은 예전 방식이고,
현재 리눅스에서 fork 는 COW(copy on write) 기법을 사용하여 프로세스 생성시 모든 자원을 복사하는 것이 아니고, 변경사항이 생길 경우에만 복사하도록 구현되어 있다고 합니다.
따라서 현재 fork 가 갖는 단점은 부모 프로세스의 페이지 테이블을 복사하는 것과 자식 프로세스를 기술하기 위한 프로세스 구조체를 할당받는 시간과 메모리 뿐이라고 합니다.
4.COW(Copy On Write)?
- vfork()처럼 부모 프로세스의 데이터 부분에 대한 참조만 소유하고 있다가 실제 변경이 발생하는 시점에 복사를 하여 사용하는 방식이라고 합니다.
- 쓰레드 생성
사용자 입장에서 프로세스 구조
User 입장에서 프로세스의 생김새를 논할 때는, 가상 주소 공간에서의 모양을 이야기 한다. 사용자 프로세스가 수행되기 위해서는 여러가지 자원들을 커널로부터 할당받아야 한다.
각각의 프로세스별로 주어지는 가상 공간 역시 이러한 자원 중 하나이다.
32bit CPU의 경우, 운영체제는 각 Process 에게 총 4GB 크기의 가상공간을 할당한다.
Linux 는 이 중에서 0GB ~ 3GB의 공간을 사용자 공간으로 사용하고, 나머지 3GB ~ 4GB를 커널 공간으로 사용한다.
Text : command, function 등으로 구성되는 영역. 프로세스중 가장 하위 공간 차지.
Data: 전역변수 등을 담아놓는 데이터 영역.
Stack : 함수의 지역변수 등을 담는 영역. 이 부분은, User 와 Kernel 공간의 경계 위치 부터 아래 방향으로 공간을 차지.
이 부분은 프로그램이 수행 됨에 따라 동적으로 변한다. 즉, 함수를 호출할 때 인자나 호출 된 함수의 지역 변수를 저장하기 위해서 아래 방향으로 크기가 커진다.
Heap : malloc()/free() 등의 함수를 사용해 동적으로 메모리 공간을 할당 받는다. 이때 메모리가 할당되는 공간이다.
이 때, 각 영역을 Segment 라고 하며, 또는 가상 메모리 객체 라고도 부른다.
Task 관리 구조
Process 는 자신이 사용하는 자원과 그 자원에서 수행되는 수행 흐름(쓰레드)으로 구성됨. (Process 안에 있는 물결무늬가 Thread)
리눅스에서는 이를 관리하기 위해 각 Process 마다 task_struct 라는 자료 구조를 생성.
결국 리눅스에서는, Process가 생성되든 Thread가 생성되던, task_struct라는 동일한 자료구조를 생성하여 관리한다.
결국!
Linux Kernel 은 Process 또는 Thread 중에서 어떤 것이 요청될 지라도, 모두 task_struct 자료 구조로 동일하게 관리한다.
단지 task_struct 자료 구조 중에서 수행 이미지를 공유하는가, 같은 Thread 그룹에 속해 있는가 등의 여부에 따라 Process, 또는 Thread로 사용자에게 해석되는 차이가 있을 뿐이다.
Linux 는 1대 1 모델을 기반으로 한다.
즉, User Level 에서 Thread를 생성하면, 그 Thread의 존재를 Kernel 도 안다는 것이다.
Process가 수행되려면 !
자원(Resource)과 수행 흐름(Flow of control) 이 필요하다.
그 중, 자원을 Task로, 제어 흐름을 Thread로 정의하였다.
반면!!!!!
Linux에서는 Process던 Thread이던, Kernel 내부에서는 Task라는 객체로 관리된다.
그렇다면, User들은 어떤 조건이 만족 될 때 Task를 Process라고 부르며,
Task를 Thread라고 부를까?
System에 존재하는 모든 Task는 유일하게 구분이 가능해야 한다.
Task 별로 유일한 이 값은 task_struct 구조체 내의 pid Field에 담겨있다.
' 한 Process 내의 Thread 는 동일한 PID를 공유해야한다.' 라고 명시되어있는데,
이를 위해 tgid(Thread Group ID)라는 개념을 도입했다.
Task 문맥 (66 page)
Task 는 자원(Resource), 제어 흐름은 Thread!
Thread나 Process나 다 Task 에 속해있다.
우리는 쓰레드 마다 Task_struct 라는 Task를 관리하기 위해 자료구조가 필요함을 배웠다.
LInux Kernel 은 Task마다 이러한 정보를 관리하고 있다.
Context-Task와 관련된 모든것. 3가지로 구분된다.
1. System Context:
Task의 정보를 유지하기 위해, Kernel이 할당한 자료구조.
Ex) 대표적인 자료구조(Structure)로는, Task-structure, fd, file table, segment table, page table이 있다.
2. Memory Context:
.text, .data, .stack, .heap, .swap 공간 등이 포함됨.
3. HW Context:
Context Switch 시 Task의 현재 실행 위치에 대한 정보 유지. Thread 구조 또는 HW Register context 라고 불림.
%% 이 부분은, 실행중이던 Task 가 대기 상태나 준비 상태로 전이할 때, 이 Task 가 어디까지 실행했는지 기억해 두는 공간으로, 이 후 이 Task 가 다시 실행될 때 기억해 뒀던 곳 부터 다시 시작한다. (CPSR)
3.6 상태전이 . (70 ~ 75 페이지) 한번 쭉 읽어봄. 중요하니 2-3번 다시 읽어보기....3.7 RunQueue 와 Scheduling
여러개의 task 중에서 다음번 수행 시킬 Task 를 선택하여 CPU 라는 자원을 할당 하는 과정 : Scheduling !
- 리눅스의 Task : 실시간 Task 와 일반 Task로 구분. 항상 실시간 Task 가 우선 실행..
** 스케줄링과 관련된 전체 구조
- 일반적으로, 운영체제는 스케줄링 작업을 위해, 수행 가능한 상태의 Task를 자료구조를 통해 관리.
리눅스에서는 이 자료구조를 RunQueue라고 부른다.
OS의 구현에 따라, RunQueue는 1개 또는 여러개 존재 가능.
부팅 이후 각 CPU 별로 하나씩의 RunQueue가 유지됨. (CPU 1개 == RunQueue 1개)
스케줄러가 수행되면 해당 CPU의 RunQueue 에서 다음번 수행시킬 Task를 골라냄.
이때, 2가지 고려 사항이 있다.
1) 어떤 task를 선택 할 것인가?
- 이를 위해, Linux 는 일반 Task를 위해 CFS(Completely Fair Schedule)를 사용하며,
실시간 Task 를 위해서는 FIFO, RR, DEADLINE 정책을 제공한다.
2) RunQueue 간의 부하가 균등하지 않은 경우에 어떻게 할 것인가?
- 이를 위해 Load Balancing 을 제공. 이 Function 에서는 특정 CPU가 많은 작업을 수행하고 있느라 매우 바쁘고, 다른 CPu들은 한가하다면!
다른 CPU로 Task 를 이주(migration) 시켜서 System 의 전반적인 향상을 시도한다.
반면,
NUMA(Non-Union Memory Access) 구조에서는,
load_balance() 에서 CPU 부하 뿐만이 아니라 , 메모리 접근 시간의 차이 등도 고려하여 시도.
3.7-2 실시간 Task-Scheduling (FIFO, RR, DEADLINE)
** Scheduling 의 동작 원리 (*** 여기서 Scheduling 을 하는 이유??? 가장 높은 처리율( Throughput) 을 위해 효율적으로 CPU 를 할당 하도록 하는것.
- 리눅스의 Task는 실시간 Task, 일반 Task 로 나뉘며, 각각 3개, 3개 총 6개의 스케줄링 정책을 갖고 있다.
(79 페이지부터)
Chapter 4.
32bit = 2^32 = 4GB(CPU.. 가상메모리가 4GB)
64bit = 2^64 = 16EB
- Bank = 접근속도가 같은 memeory 집합. ( UMA: 1개의 bank, NUMA: 여러개 bank )
- Node = Bank 를 표현하는 자료구조... / node를 통해 물리 memory 접근 가능
- Zone = Node에 존재하는 물리 memory 중 16MB이하 부분을 관리하는 자료구조.
- Page Frame = 각각의 zone 은 자신에 속해있는 물리 memory 를 관리. 이 물리 memory 의 최소단위이다. 모든 page frame 당 하나씩 page 구조체가 존재. (★ Page System Booting 순간에 구축되어 특정 물리 Memory 위치에 저장됨.)
- 가상주소 : 사용자를 완전히 분리해 관리하기위해 만들어진 것.. 프로세스간에 상호 간섭을 막는 역할
4.3 Buddy 와 Slab
- 물리 memory 할당 & 해제 방법.
★ Physical memory 의 최소단위인 page frame 단위로 할당. 4KB가 최소 할당 단위.
if (1) 4KB보다 작은 크기를 요청시? 내부 단편화 발생 -> Slab 할당자
if (2) 4KB보다 큰 크기를 요청시? 외부 단편화 발생 -> Buddy 할당자
'IT' 카테고리의 다른 글
md5sum (0) | 2021.08.02 |
---|---|
Mount 란 (0) | 2019.06.26 |
ㅠㅜ리눅스에서 원격 접속 프로그램.. (0) | 2018.05.14 |
install Window on VMWare in linux (0) | 2018.05.14 |
create package in openwrt (0) | 2018.05.11 |