네트워킹과 관련하여 초기화가 어떻게 이루어지는지에 대한 장입니다. 바로 본론으로
Kernel Initialization
출처: Understanding Linux Network Internals
주목해야 할 포인트
do_initcalls()
-> free_init_mem()
run_init_process()
가 init
프로세스를 결정
디바이스가 작동하려면? -> 커널에 인식되어야 하고 올바른 드라이버랑 연결되어야 함
3단계
struct net_device
에는 커널이 디바이스 드라이버와 통신하기 위해 이용하는 function pointer들이 있음 (Chapter2 참고) 이걸 초기화하는 것은 장치의 타입(이더넷 등), 브랜드, 모델에 따라 다름. 로직은 다 비슷하므로 이 책에서는 ethernet만을 다룸.
디바이스 드라이버는 device-kernel 커뮤니케이션을 위해 다음과 같은 리소스를 할당한다.
뻔한 두 가지 방식
디바이스 드라이버는 irq를 요청한 후에 해당 irq에 인터럽트 핸들러를 등록/해제하기 위해 다음 두 함수를 사용함.
int request_irq(
unsigned int irq,
void (*handler)(int, void*, struct pt_regs*),
unsigned long irqflags,
const char * devname,
void *dev_id
)
void free_irq(
unsigned_int irq,
void *dev_id
)
인터럽트를 통해 NIC가 드라이버에 알릴 수 있는 정보 예시
“Interrupt sharing”을 이용하면 하나의 IRQ에 여러 디바이스를 연결시킬 수 있음. 하지만 하나의 shared IRQ에 연결된 디바이스 드라이버가 모두 이 기능을 지원한다고 명시해야만 함.
IRQ 라인은 플랫폼에 따라 정해진 개수만큼만 있기 때문에 여러 디바이스가 하나의 IRQ를 사용하면 리소스를 절약할 수 있는 방법이 된다.
참고: IRQs - X86 Assembly/Programmable Interrupt Controller
Of the 15 usable IRQs, some are universally associated with one type of device:
IRQ 0 ‒ system timer
IRQ 1 — keyboard controller
IRQ 3 — serial port COM2
IRQ 4 — serial port COM1
IRQ 5 — line print terminal 2
IRQ 6 — floppy controller
IRQ 7 — line print terminal 1
IRQ 8 — RTC timer
IRQ 12 — mouse controller
IRQ 13 — math co-processor
IRQ 14 — ATA channel 1
IRQ 15 — ATA channel 2
따라서 디바이스 드라이버가 interrupt sharing을 지원한다는 얘기는 즉, 자신이 등록한 인터럽트 핸들러가 커널에 의해 invoke될 때 무시해도 좋은지 코드를 실행해야 하는지 판단하는 로직이 있다는 얘기임 (디바이스의 레지스트리 값을 보고 판단할 수 있게 되어있다든지)
irqaction
스트럭트로 정의됨.irq_desc
가 맵핑 정보를 다 가지고 있음.request_irq()
가 맵핑을 irq_desc
에 등록(하는 setup_irq()
함수를 호출)irq_desc
, setup_irq()
는 arch 디렉토리 이하에 아키텍처에 맞게 오버라이딩되어있음.
Organization of IRQ handlers
출처: Understanding Linux Network Internals
끊고 갑시다
커널에 박혀있는 것과 모듈로 로드된 컴포넌트 모두 파라미터를 줄 수 있음. 여기에 쓰이는 두 가지 매크로가 있는데
module_param
)모듈을 로드할 때 옵션을 줄 수 있게 한 매크로. 커널에 박아 넣은 컴포넌트에 대해서는 이 옵션의 값들을 부트타임에 줄 수 없음. (참고: /sys
인터페이스)
_ _setup
)부트로더를 통해서 부트타임에 줄 수 있는 옵션들을 정의.
두 방식이 다 사용되기 때문에, 커널 부트타임에 정의된 옵션 이름과 모듈 로드타임에 정의된 옵션 이름이 겹칠 수도 있음. 모듈 로드 시 옵션 이름은 해당 모듈에만 적용되는 것임.
pass
커널 모듈은 파라미터를 어떻게 정의할까? -> module_param
매크로를 사용
...
module_param(multicast_filter_limit, int 0444);
module_param(max_interrupt_work, int, 0444);
module_param(debug, int, 0444);
^ ^ ^
// 이름 자료형 /sys 파일 퍼미션
...
Understanding Linux Networking Internals에 있는 drivers/net/sis900.c의 예
/sys/modules/[module name]/parameters
에 ls 쳐보면 나옴. 파일 퍼미션을 어떻게 줬느냐에 따라서 읽기, 쓰기가 가능한데 퍼미션을 0으로 주면 아예 /sys
에 안나온다. 덮어 쓸 경우 값이 변했다는 노티를 줄 수 있는 방법이 없기 때문에, 모듈이 옵션값의 변화를 감지하는 방법을 만들거나, 중간에 값이 바뀌어도 문제가 없도록 짜야 함.
Traffic Control, per-CPU ingress queues를 포함한 중요한 초기화가 net/core/dev.c
에서 일어남.
static int _ _init net_dev_init(void)
{
...
}
subsys_initcall(net_dev_init);
net_dev_init()
에서 일어나는 일
/proc
에 몇 가지 파일을 추가함/sys/class/net
디렉터리를 생성하고 그 하위에 등록된 네트워크 장치마다 subdirectory를 만듦.랜덤값은 타이머에 주어져서 타이머들이 동시에 실행되어 CPU에 부하를 주는 것을 막거나, 특정 data structure의 구조를 알아내려는 DoS 공격을 방지하는 데 쓰일 수 있음.