2012. 3. 21. 02:40
내멋대로 만든 요약판이다.
개인적으로 중요시 여긴것만 모아놓은 자료임..  

==========================================================================================
리눅스 커널 심층분석 2nd 를 요약하기 앞서 난 운영체제 수업이라고는 한번도 들어본적이 없다.
이런 내가 '이 책은 유명 해커가 쓴책이라 재밌다!" 라는 sjh21 말에 넘어가 버렸다.
어려운 커널책을 뒤로하고 이책의 장점은, 커널 프로그래밍보다는 커널이 어떤것 인가? 어떤 용어들이 쓰이는가에
중점을 두고 있었다.

그리고 sjh21 의 요약을 해보라는 말에 요약까지 해보는데, 덕분에 한번더 정독을 할수 있는 계기가 되었고, 도움이 된것같다. 

3번이나 읽으면서 얻어낸거라곤 용어와 커널이란 결국 동시다발적으로 일어나는 하드웨어, 유저 의 입출력을
충돌없이 잘 돌아가게 하는것이 80%라는 생각아래 글을 읽기전에 이걸 한번 언급하고싶었다.

by.k1rha
==============================================================================================




| 응용 프로그램 ||응용프로그램2||응용프로그램3|   <--USER INTERFACE
            | system call interface |
|커널 서브 시스템| |디바이스 드라이버|


일반적으로 인터렙트는 어떤 번호를 가지는데 커널은 이번호를 사용하여 특정 인터럽트 핸들러를 작용시킨다
그리고 그 인터렙트를 처리하고 응답한다.

ex)키보드 컼트롤러가 인터럽트를 발생시켜 시스템으로 하여금 키보드 버퍼에 처리해야할 데이터가 있음을 알린다.
-> 커널은 그 인터렙트 번호를 보고 알맞는 핸들러를 실행한다


리눅스 --> 모노라틱 커널 >> 주소 공간을 갖는 하나의 프로세스 형태 


리눅스와 유닉스의 차이점

리눅스 : 커널 모듈이 동적으로 로드 할수 있따.
SMP(대칭형 멀티프로세싱이 가능하다)
선점형 방식이다
스레드와 프로세스간의 차이가 없다.
리눅스 == GNU C라 인라인 함수들을 제공한다


유닉스 : 모노라틱하고 선점형방식도 아니다. 
자원을 공유하는 프로세스가 따로 존재한다.




태스크 : 프로세스의 다른이름으로 리눅스 커널에서는 프로세스를 종종 태스크라고 부르는 경우가많다.

스레드 (thread) : 프로세스 안에 있는 활동을 가진 객체이다.
저마다 고유한 가상 프로세서를 할당 받지만 가상 메모리는 공유한다.

-->하나의 메모리 주소 공간을 공유하기 때문에 동시 프로그래밍과 다중 프로세서 시스템의 경우 진정한 병렬 컴퓨팅을 가능케 한다.




fork() system call 은 부모프로세스와 자식프로세스에서 리턴되어 총 2번 커널로부터 리턴된다.

fork()->exec->clone->exit 
종료된 프로세스는 좀비 상태에 놓이게 되는데 이것은 종료 되었지만 아직 부모프로세스가 wait 혹은 waitpid를 호출하지 않는
프로세스를 표시하기 위해 사용된다. 

프로세스 서술자의 저장
시스템은 PID라는 값으로 프로세스 식별

프로세스 상태 
task_running: 프로세스 실행가능 
task_uninterretable : ghrdms block(중단) 상태 <<-- 프로세스가 signal을 받더라도 꺠어나지 못함

task_zomie : 태스크가 종료되었지만 아직 부모테스크가 시스템 콜을 호출하지 않은 상태

프로세스 상태조작은 set_task_state(task,state); 에서 한다.

프로세스 생성

'스폰'메커니즘에 의해 새 프로세스를 새 주소 공간에 할당하고, 실행 파일을 읽어들이고, 프로그램을 실행하는 작업들을 처리한다.

유닉스는 이작업을 fork() execl() 두개로 나누어 fork()는 태스크를 복제하여 자식프로세스를 생성하고, 자식은
부모프로세스 PID 와 PPID 상송되지 않은 자원에서만 차이가 난다.

COW(Copy on write):데이터의 복제를 지연 또는 방지하는 기법이다. 즉, 프로세스 주소 공간을 복제하는 대신 공간을 공유한다.


vfork() 시스템 콜은 부모 프로세스의 페이지 테이블 영역이 복제되지 않는다는 점을 제외하고는 fork() 와 동일하다.
대신 자식 프로세스는 부모의 주소 공간에서 마치 하나의 스레드인 것처럼 동작하고 부모는 자식이 exec() 를 호출하거나 종료될 때까지 대기한다.


부모없는 태스크의 딜레마?

부모없이 종료된 프로세스등은 언제까지고 좀비 상태로 남아 메모리를 소비한다.
해결책은 스레드 그룹의 다른 프로세스를 부모가 하던가 init 프로세스를 부모로 만드는 것이다.
부모를 바꿀경우 do_exit()에서 modify_parent()가 호출 될때 forget_original_parent()를 사용한다.



멀티태스킹 운영체제(협력형[cooperative], 멀티태스킹 선점형)을 대부분 사용한다.

실행중이 ㄴ프로세스가 비자발 적으로 중지되는것을 선점이라고하고 서점되기까지 프로세스가 실행되는 시간을 미리 정해지는데,
이것을 타임 슬라이스라고한다.
타임 슬라이스가 너무 길면 시스템의 인터랙티브한 성능을 떨어뜨려 더이상 응용 프로그램이 동시에 수행하는 것처럼 
느껴지지 않을 것이며 너무 짧으면 스위칭하는데 자원을 너무 소모 할 것이다. 

프로세스 우선수의 구현은 2가지로 나뉘는데 nice 값(-20 ~ 19 중 큰 nice 값은 낮은 우선순위) 과 실시간 우선순위 (0~99)가 있다.

schedule(): 다음 번 수행할 프로세스를 선택하고 그 프로세스에게 실행 권한을 넘기는 작업

effective_prio() 함수는 태스크의 동적 우선 순위를 리턴한다.

task의 sleep 과 wake 
sleep은 어떠한 이벤트를 기달리는데 크게 파일 I/O,  하드웨어 이벤트, 특정 시간, 세마포어 획득이 있따.

휴면은 대기큐(wake_queue_head_t)를 통해 처리된다.
DECLARE_WAIT_QUEUE_HEAD()를 통하여 정적으로 생산
init_wait_queue_head()를 통한 동적 생성이 있따.
race condition을 예방하기 위해서는 이러한 휴면과 깨어남을 구분 지어야 한다

로드 벨런서(load_balance()(call)) 실행큐의 균형을 맞추는 역할을 한다. 
과정 : load_balance()는 find_busiest_queue()를 호출하여 가장 바쁜 실행큐를 찾아낸다.
25% 이상의 프로세스를 갖는 프로세스 가 없는 경우 NULL 을 리턴 load_balance()는 종료된다
어느곳에서 빼올지를 결헌한뒤 가장 높은 우선 선위를 찾는다.
-> 다른 프로세스의 이동이 금지되지 않은 태스크를 찾는다 -> pull_task()로 바쁜 실행큐로부터
현재 실행큐로 프로세서를 이동한다.


선점과 컨텍스트 스위칭? 

컨테스트 스위칭(context switching)이란 하나의 실행 가능 태스크를 다른 태스크로 바꾸는 것을 말하며, kernel / sched.c 에 정의된 context_switch()함수에 의해 호출도ㅓㅣㄴ다.
1.asm/mmu_context.h 에 정의된 switch_mm()함수를 호출, 이전 프로세스의 가상 메모리 매핑을 새 프로세스의 것으로 대체한다.

asm/system.h에 정의된 switch_to() 함수를 호출 프로세서 상태를 이전 프로세서에서 현재프로세스로 전환한다.
이과정에는 스택 정보와 프로세서 레지스터를 저장하고 복원하는 등의 작업이 포함된다.

USER 선점:커널이 유저공간으로 돌아가기 직전에 일어나는 것으로 이경우 need_resched가 설정됐으므로 스케쥴러가 수행된다.

KERNEL 선점 : 리눅스 커널은 다른 대부분의 유닉스 변종이나 다른 많은 운영체제와는 달리 완벽한 선점형 커널이다. 비선점형 커널에서는 커널 코드가 종료 될때까지 실행한다.
즉 커널 안에 있는 경우 스케줄러가 다른 태스크를 다시 스케줄링하는 것이 불가능 하다.
커널 코드는 협력적(cooperative)으로 선점 되는것이지 선점되지는 않는다는 것이다.


SYSTEM CALL : 대게 함수 호출을 통해사용되며 하나 혹은 그이상의 인자를 받아들여 하나 이상의 사이드 이펙트를 가져 올 수 있다.

시스템 콜번호 : 유저 공간의 프로세스가 시스템콜을 실행할 경우 실행할 이름대신 번호를 사용한다

매게변수의 전달
대부분의 시스템콜은 번호와 함께 매게 변수가 필요하다 가장 쉬운 방법은 레지스터를 이용하는 방법이다
x86 에서는 ebx, ecx,edx,esi edi 레지스터가 순서대로 처음 5개의 매개 변수를 저장한다
시스템 콜을 동작을 위한 한계
시스템콜 테이블에 엔트리를 추가한다--> 지원되는 아키텍처에 대해 (entry.S) 시스템 콜번호를 <asm/unistd.h>에 정의한다-> 커널이미지를 컴파일한다.



인터럽트(interrupt)
하드웨어가 프로세서와 통신 할 수 있도록 해준다
-> 인터럽트 핸들러를 최대한 빨리 수행해야 하지만 한편으로는 많은 추가적인 일을 해야하는 2가지 목표

top half:인터렙터가 활성화되는 즉시 실행 타임크리티컬한 작업들
bottom half:나중에 해도 될 작업들은 밑으로 내려 놓는다.


인터럽트 핸들러등록은 irq(할당할 인터럽트 번호) 와 handler(해당 인터럽트를 제어할 핸들러포인터)로 이뤄진다.


SA_INTERRUPT:이 플래그는 해당 인터럽트 핸들러가 빠른 인터럽트 핸들러임을 가리킨다
SA_SAMPLE_RANDOM:이 플래그는 해당 디바이스에 의해 발생한 인터럽트가 커널 엔트로피 prook기영향을 끼친다.

인터럽트 핸들러 제거:void free_irq로 제거 



메모리를 공유하는 응용프로그램은 항상 동시적인 접근으로부터 공유된 자원을 보호해야한다
-> 다수의 실행중인 스레드가 동시에 데이터를 변경할 경우 다른 스레드가 변경한 부분을 또다른 스레드가 덮을수 있기 떄문이다.

동기화(synchronization) : race condition 상태를 예방하는것!

락킹(rocking):한번에 오직 하나의 스레드만이 해당 자료구조를 조작할 수 있다는 것을 확신할 방법, 혹은 특별한 구역에서 다른 스레드가 동작하고 있을때 해당 자료구조

==> 큐에 대한 사용 스레드는 반드시 lock을 얻어야한다.

lock은 경쟁이 발생하지 않는 원자적 작업들로 구현되어 있따.
해당 key가 점유되었는지 확인하고 그렇지 않은 경우엔 자기가 그 키를 점유하는 작업을 단 하나의 명령으로 수행 할 수 있다. 'o'은 락이 설정되어 있지 않음이다.

대칭형 멀티프로세싱 시스템을 사용한다면 두 프로세스는 정확히 같은 시간에 하나의 위험지역에 진입할 수 있다.--> 진정한 동시성

커널에서의 동시성 원리
*인터럽트
*soft tirp 와 태스크릿 -->커널은 현재 동작중인 코드를 중단하기 위해 거의 언제라도 softrirp나 태스크릿을 스캐줄 할수 있다.
데드락(dead lock):하나 이상의 스레드와 하나 이상의 자원이 있을때 발생하는 것으로 각 스레드가 어떤 자원을 얻으려 대기 하지만 이미 모든 자원의 락이 잡혀 있을때 발생
ex) 사거리에서 서로 눈치보며 서있는 차들과 같다



커널 동기화 방법중 원자적 동작(atomic poeration)이란 중단됨이 없이 한번에 실행되는 명령을 뜻한다.

원자적 정수연산:atomic_t를 사용하는 자료구조의 일부분이다.
atomic_t 의 크기는 24bit를 넘어서는 안된다 그 이유는 원자적 동작을 조금 이상한 방법으로 구현하고 있는 sparc 아키댁처 떄문이다 sparc는 락을 32비트 int의 하위 비트에 포함시켜 구현한다.

원자적 정수 연산은 보통 카운터(counter)를 구현 하기 위해 사용한다.
단순한 카운터를 복잡한 락을 사용하여 보호하는 것이 비효율적이므로
훨씬 가벼운 atomic_inc() 나 Atomic_dec()을 사용하여 구현하는 것이 보통이다. 

워드(혹은 이와 동일한 크기의 객체)에 대한 읽기는 언제나 원자적으로 발생한다.
읽기는 절대 동일 워드에 대한 쓰기 작업과 서로 끼어들지 않는다.
코드를 작성 할때는 가능하면 복잡한 락 대신 간단한 원자적 동작을 사용하는것이 좋다.
아키텍처에서 1개 또는 2개의 원자적 동작은 복잡한 하나의 동기화 방법보다 가벼울 뿐만 아니라 캐시 스레싱을 덜 발생시킨다.

원자적 비트연산
이함수 역시 아키텍처에 종속적이며 <asm/bitops.h>에 정의돼 있다.
이 함수들의 특징은 바로 일반 메모리 주소를 가지고 연산을 한다는 점이다.
0번 비트는 주어진 주소에서 가장 아래쪽(least signification)(비트를 의미하고 32비트기기의 32비트는 다음 워드의 가장 아래쪽 비트가 된다.



우리가 구조체를 갱신하기 전에 다른 누군가 같은 구조체를 읽으면 안된다는 것이다.
이를 위해 보다 복잡한 동기화 방법 즉, 락이 필요하게 된다.

락의 종류는 스핀락, 세마포머, 큰커널락(Big kernel lock) 이 있는데,

스핀락이리눅스 커널에서 가장 일반적으로 사용되는 락이다. 스핀락은 최대한 하나의 스레드에 의해 잠길수 있는 락을 말한다.
만약 어떤 스레드가 이미 잠겨진 스핀락을 다시 잠그려  시도한다면 그 스레드는 루프를 돌면서 락을 잠글 수 있을 때 까지 기다린다.<asm/spinlock.h>

세마포어 : 휴면하는 락이라 생각할 수 있다 어떤 태스크가 이미 잠겨진 세마포머를 잠그려 하는 경우 세마포어는 그 태스크를 대기큐로 삽입하고 해당 태스크를 휴면 상태로 만든다.

큰커널락(BKL)은 태스크가 휴면할 경우 자동으로 해제되고 다시 실행될경우 자동으로 잠긴다. 때문에 BKL은 재귀적인 락인것이다.
BKL을 잡고 있는 프로세스는 몇번이라도 BKL을 다시 잠글수 있다. 이 경우도 데드락에 빠지지는 않는다.




타이머와 시간관리
 
하드웨어에 고정되어 일정간격으로 체크를 해주는것을 커널은 틱이라고 한다. 하지만 이러한 틱 인터렙터가 없어도 되지 않을까? 라는 말이 있다면 그대답은 yes 이다.
하지만  그 설계가 깔끔치는 않을것이다. 대신에 커널은 대기중인 이벤트 각각에 대해 동적으로 프로그램된 타이머를 사용해야하는데, 상당한 타이머 부하를생각해야한다.
이런식으로 하면 주기적인 타이머 인터럽트가 필요없고 따라서 Hz 또한 굳이 존재핮 ㅣ않아도 될것이다.

지피? 란 전역 변수 jiffies는 시스템이 부팅된 이후 발생한 틱의 수를 저장하는 변수이다. 


실행지연 : 때로 커널 코드(특히 드라이버의 경우)는 타이머나 보톰하프 메커니즘을 사용하지 않고서도 실행을 일정 시간 동안 지연 할 수 있는 방법이 필요하다.
이것은 대개 주어진 작업을 하드웨어에서 처리하기 위한 시간을 보유하기 위해 필요하다.





메모리관리

커널 내부에서 메모리할당은 커널 외부에서의 할당처럼 쉽지많은 않다.
커널에서의 메모리 할당을 어렵게 하는 데에는 여러가지 요소가 존재한다. 우선 커널에는 유저 공간에서 제공되는 것과 같은 사치스러운 환경ㅇ ㅣ존재하지 않고 유저공간과는 달리 커널에서는 쉽게 메모리를 할당하기 위한 편리한 기능이 제공되지 않는다.
예를들면 대부분의 경우 커널은 휴면(sleep)을 할 수가 없다. 게다가 커널은 메모리 오류를 쉽게 처리하지 못한다.

페이지 : 커널은 물리적인 페이지를 메모리 관리의 기본 단위로한다.
프로세서의 가장 작은 주소 할당 단위는 보통 워드(word) 이지만 메모리 관리 유닛(MMU)는 일반적으로 페이지 단위로 동작한다.

커널은 시스템에 있는 모든 물리적 페이지를 struct page 구조체로 표현한다.이 구조체는 <linux/mm.h>에 정의돼 있다.

struct page{
page_flags_t flags;
atomic_t _count;
atomic_t _mapcount;
unsigned log private;
strct address_space *mapping;
pgoff_t index;
struct list_head lru;
void *virtual;

};


우선 flags 필드는 페이지 상태를 저장한다 여기에 저장되는 플래그에는 페이지가 변경되었는지 (dirty) 혹은 메모리가 잠겨졌는지(lock) emddmf skxksosms dufjrkwlrk dlTEk.
_count 필드는 페이지 사용 횟수 즉 해당 페이지에 대한 참조가 얼마나 되는지를 저장한다 이횟수가 0이면 누구도 이페이지를 사용하지 않은것이고, 이페이지를 다른 누군가에게 할당해 줄 수 있게 된다.
커널 코드에서는 이 필드를 직접 접근하는 대신 page_count()함수에 page 구조체를 넘겨주어 그 값을 얻으면 된다.
virtual 필드는 페이지의 가상 주소이다. 일반적으로 이 값은 단지 가상 메모리에서의 페이지의 주소를 나타낸다. 한편 상위메모리(high memory)라 불리는 메모리는 커널의 주소 공간으로 영구적으로 매핑되지 않는데, 이러한 메모리를 나타낼 경우 이 필드에 NULL을 사용한다.
그리고 이 경우 페이지는 필요할 떄마다 동적으로 매핑 돼야 한다.

여기서 핵심은 page 구조체가 가상 페이지가 아닌 물리적인 페이지와 연계되어 있다는 사실이다. 따라서 이 구조체가 기술하는 내용은 일시적일 수 밖에없다.
커널은 이 자료구조를 이용하여 시스템 상의 모든 페이지를 관리하며 따라서 이 자료구조를 통해 어떤 페이지가 사용 가능한지(free 한지)를 알수 있게된다.


페이지 얻기!!

커널에는 메모리를 요청하기 위한 하나의 저수준 (low-level)메커니즘과 이것을 사용하는 여러 가지 인터페이스가 제공되는데 이들은 모두 페이지를 기본 단위로 삼아 메모리를 할당하며 <linux/gfp.h>에 선언돼있따.

0으로 페이지가 초기화 되어있으면 페이지를 할당해줄수 있는데, unsigned log get_zeroed_page(unsigned int gfp_mask) 함수로 얻을 수 있따.
이 함수는 __get_free_page()와 동일한 역할을 하지만 페이지를 할당된 후 0으로 채운다는 점이 다르다.

페이지 해제하기
할당된 페이지를 해제하려면 다음과같은 함수를 쓴다
void __free_pages(struct page *page, unsigned int order)


Kmalloc()
kmalloc()함수는 유저 공간에서 많이 쓰이는 malloc()루틴과 비슷하지만 대신 추가적인 플래그를 매개변수로 가진다.
반대는 kfree 이다.
vmalloc()은 kmalloc()과 비슷하지만 가상적으로는 연속이지만 물리적으로는 꼭 연속이 아닐 수도 있는 메모리를 할당 한 다는 점이 다르다.


스택에 정적으로 할당하기? 

프로세스마다 할당되는 커널 스택의 크기는 아키텍처와 컴파일시 옵션에 따라 다르다. 전통적으로 커널 스택의 크기는 프로세스당 두페이지씩이다.
즉 32비트 아키텍처에서는 대개 8KB 64비트에는 16kb 인데 이들은 각각 4, 8kb 의 페이지를 갖기 떄문이다.


물리적인 메모리가 단편화될수록 새 프로세스를 할당하기 위해 VM이 들여야 하는 비용은 증가한다.


이중에 어떤 할당 방법을 사용하는지가 결정하는것은 보통 물리적으로 연속적인 페이지가 필요하면 저수준 페이지 할당자나 kmalloc()을 사용한다.
이것은 커널내에서 메모리를 할당하는 표준적인 방법이므로, 아마도 여러분은 거의 이 방법을 사용하게 될것이다.
이들 함수에 주어지는 두 개의 공통 플래그가 GFP_ATOMIC과 GFP_KERNEL 이라는 사실을 떠올린다.
높은 우선순위의 휴면하지 않는 할당을 위에서는 GFP_ATOMIC플래그를 사용한다.
상위 메모리에서 할당하고자 한다면 alloc_pages()를 사용한다. 이 함수는 논리적 주소가 아닌 struct page 구조체의 포인터를 리턴한다.
이 상위 메모리는 자동으로 매핑되지 않으므로 여기에 접근하는 유일한 방법은 struct page 구조체를 사용하는것이다.


프로세스 주소공간

프로세스 주소공간은 각프로세스에 제공되는 선형주소범위와(더욱 중요한) 이 주소 공간 안에서 프로세스가 사용할 수 있는 실제 공간으로 나누어진다. 각각의 프로세스는 32비트 또는 64비트의 평평한 주소공간을 갖게되는데 그 크기는 아키텍처에 따라 다르다.
보통 주소공간은 각프로세스에게 유일하게 할당된다 즉 어떤 프로세스가 가진 주소공간의 메모리 주소를 알고 있더라도ㅡ 다른 프로세스가 가진 주소 공간의 메모리 주소에 대해서는 아무것도 알아 낼 수 없다.

하지만 프로세스가 자신의 주소공간을 다른 메모리 프로세스와 공유할수 있는데 이를 스레드 라 한다.

메모리주소는 4021f000 과 같은 주소 공간 안의 어떤 값으로 주어진다.
이러한 값은 프로세스의 32비트 주소공간에서 어떤 특정한 바이트를 식별 할 수 있도록 도와준다.
 흥미로운것은 08048000-0804ㅊ000 과 같이 프로세스가 접근권한을 갖는 메모리 주소들간의 간격이다.
이러한 합법적인 주소 간격을 메모리 공간이라한다.


페이지 테이블

비록 으용프로그램이 물리적 주소에 매핑된 가상 메모리상에서 동작 하더라도 프로세스는 직접 물리적인 메모리에 대해 동작한다
그러므로 으용프로그램이 가상메모리 주소에 접근할 경우 프로세스가 해당 요청을 처리하기 위해서는 가상 주소를 물리주소로 변환 해야 한다. 이러한 변환 작업은 페이지테이블을 이용해 이뤄진다.


하드웨어 캐시 TLB(translation lookaside buffer) 페이지 테이블을 도와 준다 이는 가상 주소에 접근하는 경우 프로세서는 먼저 해당 매핑이 TLB에 캐시 돼 있는가를 검사한다.

페이지캐시
PAGE CACHE 는 일반적으로 파일 시스템의 파일이나 블록 디바이스 파일 메모리맵파일등부터 생성된다. 이러한 식으로 페이지캐시는 최근에 파일을 접하면서 얻은 파일 모두를 저장한다.
한데 이 페이지는 비연속적인 다수의 물리블록이기 떄문에 이 사이에서 데이터를 찾기란 쉽지 않다
이러한 범용성을 유지하기위해 리눅스의 페이지캐시는 캐시 안의 페이지를 구별하기위해 address_space구조체를 사용한다.

커널은 페이지I/O를 하기전에 항상 페이지 캐시에서 원하는 페이지가 있는가를 검사해야하므로(flags 값이겠지?)이러한 검사 동작은 빠르게 이뤄져야 한다.



쓰기동작은 페이지 캐시를 통해 지연된다
페이지 캐시에 속한 데이터가 저장공간의 것보다 더 최신것이되면 해당 데이터는 dirty 해졌다고한다.
메모리에 쌓인 dirty페이지들은 언젠가 디스크로 다시 쓰여야하는데 이러한 과정을 라이트백(write back)이라 한다.

write back을 위해 커널 2.4 이전버젼에는 각각 2개의 독립적인스레드에서 처리하였으나 2.6부터는 커널 스레드 갱(gang)인 pdfflush 백그라운드 라이트백 데몬, 혹은 pdflush 스레드들이 이러한 작업을 한다.



모듈?

로딩가능한 커널 오브젝트로 묶을수 있으며 이것을 모듈 이라고한다
모듈은 지원 함으로써 시스템은 커널에 기본적으로 필요한 최소 기능만넣고 나머지의 부가기능과 드라이버는 모듈로 제공할수 있게된다
일반적으로 모듈이 로드되면 커널 안으로 동적으로 링크된다. 유저공간에서와 마찬가지로 동적으로 링크된 바이너리들은 명시적으로 익스포트된 함수들만 호출할수 있다.

(그럼 RTL은?)



OOPS --> 함수로 메모리가 표기된다
ksymoops --> 역추적 경로 상의 주소는 기호적 이름으로 변해야 한다.

커널 디버깅 TIPS
UID를 조건으로사용한다. 기존 알고리즘을 두고 새알고리즘을 추가하여 실행한다

if(current->uid!=7777){
/*이전 알고리즘 */
}

else{
/*새알고리즘*/
}

1초에도 수천번씩 호출되는 함수를 막기위해 빈도제한(rate limiting) 을 건다.
이렇게하면 콘솔에 정보가 넘처나는것을 막을 수 있고 컴퓨터는 정상 유지된다.
 
Posted by k1rha