자격증 정복/임베디드기사(기능사)

임베디드기사 실기 07 — 디바이스 드라이버 작성 (module_init·mknod·printk·Makefile·EXPORT_SYMBOL)

올드 IT직장인 2026. 10. 29. 02:42

임베디드기사 실기 07 — 디바이스 드라이버 작성 (module_init·mknod·printk·Makefile·EXPORT_SYMBOL)

출제기준 2-3: 디바이스 드라이버 작성하기
module_init, mknod, Makefile 자동변수, 메모리 존은 2~3번씩 나온 단골이에요.
명령어와 함수 이름을 정확히 쓰는 게 승부처입니다!


📎 필기 시리즈의 디바이스 드라이버 편과 함께 보면 좋아요. (링크 추가 예정)


실기 출제 포인트 ⭐⭐⭐

키워드 핵심 내용 빈도 기출
module_init / module_exit 모듈 적재·제거 시 호출할 함수 등록 ⭐⭐⭐ 2018 Q3, 2022 Q3, 2025 Q2
Makefile 자동변수 $@ $< $^, 매크로 참조 $ ⭐⭐⭐ 2016 Q3, 2019 Q2, 2022 Q16,
2025 Q3
mknod /dev에 디바이스 노드 생성 ⭐⭐ 2019 Q3, 2024 Q10
insmod / rmmod / modprobe / lsmod 모듈 적재·제거·의존성 처리·목록 ⭐⭐ 2021 Q16
printk 커널 메시지 출력 ⭐⭐ 2017 Q10, 2021 Q7
커널 메모리 존 ZONE_DMA / NORMAL / HIGHMEM ⭐⭐ 2022 Q14, 2025 Q11
워치독 드라이버 kick 주기, 타임아웃 리셋 ⭐⭐ 2022 Q13, 2025 Q5
request_irq 인터럽트 핸들러 등록 ⭐ 2018 Q19
ioctl 특수 제어 명령 전달 ⭐ 2019 Q12
Completion 변수 태스크 간 이벤트 동기화 ⭐ 2023 Q19
EXPORT_SYMBOL / _GPL 커널 심볼 공개 범위 ⭐ 2025 Q10
레지스터 맵·표준 API 정의 파일 연산 구조체 등 기출 없음 보강

 

 

키워드 박스 — 답안에 반드시 들어가야 할 단어

module_init() : 모듈 적재 시 실행할 초기화 함수 등록 (짝: module_exit)
mknod         : 디바이스 특수 파일(노드) 생성, 타입 c/b + 주번호 + 부번호
insmod        : 모듈을 커널에 적재 / rmmod : 제거 / modprobe : 의존성 자동 처리
printk()      : 커널 공간 메시지 출력 (printf 대신)
request_irq() : 인터럽트 핸들러 등록 (해제: free_irq)
ioctl()       : read/write로 처리 못 하는 특수 제어 명령 전달

모듈 초기화 — module_init ⭐⭐⭐

기출 (2018 Q3 = 2022 Q3 = 2025 Q2 동일) 디바이스 드라이버에서 어떤 함수에 대한 설명인가?

register_chrdev() 함수로 커널에 등록하고, 커널 메모리를 request_region() 함수로 할당받는다.

→ module_init()

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ioport.h>

static int __init my_driver_init(void)
{
    register_chrdev(MY_MAJOR, "my_device", &fops);      // 문자 디바이스 등록
    request_region(MY_BASE_ADDR, MY_NUM_PORTS, "my_device"); // I/O 영역 확보
    printk(KERN_INFO "my_driver loaded\n");
    return 0;
}

static void __exit my_driver_exit(void)
{
    release_region(MY_BASE_ADDR, MY_NUM_PORTS);
    unregister_chrdev(MY_MAJOR, "my_device");
}

module_init(my_driver_init);   // 적재 시 호출될 함수 등록
module_exit(my_driver_exit);   // 제거 시 호출될 함수 등록
MODULE_LICENSE("GPL");

위 코드는 구조를 이해하기 위해 직접 정리한 예시예요. (MY_MAJOR, fops 등은 가상의 값)

 

⚠️ 함정: module_init vs init_module

module_init() : 초기화 함수를 커널에 "연결해주는" 매크로
init_module() : 구버전 관례상 초기화 함수 이름 (실제 등록·할당 로직이 담기는 함수)
→ 국내 기출 정답은 module_init()
→ 안전하게 "module_init (init_module)" 처럼 병기하면 좋아요

모듈 명령어와 노드 생성 ⭐⭐

명령어 기능
insmod 모듈(.ko)을 커널에 적재 (경로 직접 지정)
rmmod 적재된 모듈 제거
modprobe 의존성을 확인해 자동으로 함께 적재
lsmod 현재 적재된 모듈 목록
mknod /dev 아래 디바이스 노드(특수 파일) 생성

 

기출 (2021 Q16) 커널 드라이버 로드 순서 중 insmod 명령어가 수행하는 동작은?
→ 모듈을 커널에 로드(적재)

 

기출 (2019 Q3 = 2024 Q10 동일) 디바이스 드라이버 모듈을 커널에 적재한 후 응용 프로그램이 드라이버에 접근할 수 있도록 파일을 생성하는 명령어는?
→ mknod

insmod ./my_driver.ko                # 1. 모듈 적재 (주번호 부여)
mknod /dev/my_device c 240 0         # 2. 노드 생성: [파일명] [타입] [주번호] [부번호]
mknod 인자 의미
c 문자 디바이스 (Character)
b 블록 디바이스 (Block)
240 주번호 (Major) — 어떤 드라이버인지
0 부번호 (Minor) — 같은 드라이버의 몇 번째 장치인지

 

⚠️ 함정: 로드 순서가 정리본마다 달라요

2019 Q3 해설 : insmod(적재, 주번호 확인) → mknod(노드 생성)
2021 Q16 정리: mknod → insmod 로 적힌 곳도 있음
→ 주번호를 드라이버가 동적으로 받는 경우는 insmod가 먼저, 고정 주번호면 순서 무관
→ 문제 지문에 순서가 주어지면 그 지문을 따르고, 직접 쓰라면 "insmod → mknod"로 설명

 

ioctl (2019 Q12) — read()/write()로 처리하기 어려운 특수 제어 명령(보드레이트 변경, 센서 설정 등)을 드라이버에 전달하는 시스템 콜이에요.

int ioctl(int fd, unsigned long request, ...);

printk와 request_irq ⭐⭐

기출 (2017 Q10 = 2021 Q7 동일) printf() 대신 리눅스 커널 내부에서 메시지를 출력할 때 사용하는 함수는?
→ printk()

printk(KERN_INFO "Embedded System Kernel Log: %d\n", value);
// 로그 레벨: KERN_EMERG, KERN_ERR, KERN_INFO, KERN_DEBUG …
// 확인 방법: dmesg 또는 /var/log/messages

 

 

기출 (2018 Q19) 디바이스 코드의 빈칸 ( A ) → request_irq

int request_irq(unsigned int irq,        // 인터럽트 번호
                irq_handler_t handler,   // ISR 함수 포인터
                unsigned long flags,     // 처리 속성 플래그
                const char *name,        // /proc/interrupts 에 표시될 이름
                void *dev);              // 공유 인터럽트 구분용
// 모듈 해제 시 free_irq(irq, dev) 로 반환

커널 메모리 존 ⭐⭐

기출 (2022 Q14 = 2025 Q11 동일) 리눅스 커널이 물리 주소와 1:1로 매핑하고 있어 주소 변환 없이 접근 가능하며, 가장 빠른 메모리 할당이 가능한 영역은?
→ ZONE_NORMAL (선형 매핑 / 직접 매핑 영역)

 

Zone 물리 주소 범위 (32비트 기준) 특징
ZONE_DMA 0 ~ 16MB DMA가 가능한 영역
ZONE_NORMAL 16MB ~ 896MB 커널이 직접 매핑해 접근, 할당이 가장 빠름
ZONE_HIGHMEM 896MB 이상 페이지 테이블 매핑 필요 → 상대적으로 느림

 

⚠️ 주소 범위는 32비트 리눅스 기준이에요. 시험에서는 "1:1 매핑·가장 빠름 → ZONE_NORMAL" 이 핵심입니다.


Makefile ⭐⭐⭐

기출 (2016 Q3) Makefile에서 매크로를 정의한 뒤 사용하고자 할 때 사용하는 기호는? → $

CC = gcc
main.o: main.c header.h
    $(CC) -c $< -o $@     # 매크로는 $(이름) 으로 참조

 

규칙 설명
매크로 정의 =를 포함하는 문장
참조 $(이름) 또는 ${이름} — 앞에 $
주석 #
여러 줄 \ 로 이어쓰기
정의 안 된 매크로 빈 문자열로 치환

 

자동 변수 ⭐⭐⭐

자동 변수 의미 기출
$@ 현재 규칙의 타깃 파일명 2019 Q2
$< 의존성 중 첫 번째 파일  
$^ 의존성 전체 목록 (중복 제외) 2022 Q16, 2025 Q3
$? 타깃보다 최신인 의존성  
program: main.o lib.o utils.o
    gcc -o $@ $^          # gcc -o program main.o lib.o utils.o

 

⚠️ 함정: $< 는 첫 번째 하나만, $^ 는 전부.

      질문 문장이 "모든 의존성 파일의 전체 목록"이면 $^, "타깃 파일"이면 $@ 입니다.


커널 동기화·심볼 공개 ⭐

Completion 변수 (2023 Q19)

커널의 두 태스크를 동기화시키기 위해 사용하며, 한 태스크가 변수를 통해 다른 태스크에 이벤트를 알려주는 방식.
세마포어를 대신해 간단히 쓰도록 제공된다.

 

→ Completion Variable (완료 변수, struct completion)

wait_for_completion(&comp);   // 이벤트가 올 때까지 대기
complete(&comp);              // 이벤트 발생을 알려 대기 태스크를 깨움

 

EXPORT_SYMBOL / EXPORT_SYMBOL_GPL (2025 Q10)

커널 함수를 모듈이 쓸 수 있게 공개하는 방법이에요.

선언 Non-GPL 모듈에서 호출 GPL 모듈에서 호출
(EXPORT 안 함) ❌ 불가 ❌ 불가
EXPORT_SYMBOL(f) ✅ 가능 ✅ 가능
EXPORT_SYMBOL_GPL(f) ❌ 불가 ✅ 가능
2025 Q10 정리본의 정답: ④, ⑤, ⑦
  - EXPORT되지 않은 함수를 모듈에서 호출 → 오류
  - EXPORT_SYMBOL_GPL 함수를 Non-GPL 모듈에서 호출 → 오류

 

⚠️ 정리본에는 이 문제의 그림 배치가 불분명하게 옮겨져 있어서 호출 위치별 번호는 원문 확인이 필요해요.

"Non-GPL 모듈은 EXPORT_SYMBOL만 쓸 수 있다" 는 규칙을 먼저 잡고 풀면 됩니다.

 

sys_ 시스템 콜 함수 시그니처 (2021 Q20)

정리본에 정답으로 kmemcpy(void *des, void *src, int size) 형태의 함수 선언이 적혀 있어요.

원문 지문이 불명확하지만, 반환형·인자(목적지, 원본, 크기) 순서로 시그니처를 쓰는 유형이라는 점만 기억해두세요.


기출 없음 — 개념 보강 📌

표준 API(file_operations) : 드라이버가 구현하는 open / read / write / ioctl / release 를
                            구조체 하나에 모아 register_chrdev()로 커널에 등록
레지스터·메모리맵        : 장치의 레지스터 주소와 비트 의미를 표로 정리해 두고,
                            드라이버는 이 주소에 접근해 장치를 제어

기출 문제 모음

Q. register_chrdev()로 커널에 등록하고 request_region()으로 메모리를 할당받는 함수는? (2018·2022·2025)
A. module_init() (init_module 병기)

 

Q. 모듈 적재 후 /dev에 접근용 파일을 만드는 명령어는? (2019·2024)
A. mknod

 

Q. printf 대신 커널 내부에서 메시지를 출력하는 함수는? (2017·2021)
A. printk()

 

Q. Makefile에서 타깃이 의존하는 모든 파일 목록을 나타내는 자동 변수는? (2022·2025)
A. $^

 

Q. Makefile에서 타깃 파일을 나타내는 매크로 심볼은? (2019 Q2)
A. $@

 

Q. 물리 주소와 1:1 매핑되어 가장 빠르게 할당 가능한 영역은? (2022·2025)
A. ZONE_NORMAL

 

Q. 커널의 두 태스크를 이벤트로 동기화하는 변수는? (2023 Q19)
A. Completion Variable (완료 변수)


💡 실무에서는?

  • 모듈 관리 — 서버에서 lsmod, modprobe, dmesg로 드라이버 상태를 확인하는 건 장애 분석의 기본이에요. NIC·HBA 드라이버를 올리고 내릴 때 그대로 쓰는 명령이죠.
  • mknod/udev — 요즘은 udev가 /dev 노드를 자동 생성하지만, 임베디드·복구 환경에서는 mknod를 직접 쓰는 일이 있어요.
  • printk/dmesg — 하드웨어 오류, 드라이버 로드 실패는 커널 로그(dmesg, /var/log/messages)에서 먼저 확인합니다.
  • EXPORT_SYMBOL_GPL — 상용 모듈(Non-GPL)이 커널 내부 심볼을 못 쓰는 이유가 이 라이선스 정책이에요. 벤더 드라이버 호환성 이슈를 만날 때 배경이 됩니다.

핵심 정리

✅ module_init() = 적재 시 호출할 초기화 함수 등록 (짝: module_exit) / init_module 병기
✅ insmod = 모듈 적재 / rmmod = 제거 / modprobe = 의존성 자동 / lsmod = 목록
✅ mknod [파일명] [c|b] [주번호] [부번호] = /dev 노드 생성
✅ printk() = 커널 메시지 출력 (dmesg로 확인) / request_irq() = 인터럽트 핸들러 등록
✅ ioctl() = 특수 제어 명령 전달
✅ ZONE_NORMAL = 1:1 매핑, 가장 빠른 할당
✅ Makefile: 매크로 참조 $ / $@ 타깃 / $< 첫 의존성 / $^ 모든 의존성
✅ Completion = wait_for_completion() + complete() / Non-GPL 모듈은 EXPORT_SYMBOL만 사용 가능

 

다음 편에서는 임베디드 OS 이해(출제기준 3-1)로 이어갑니다!

 

궁금한 점은 댓글로 남겨주세요!

반응형
개인정보처리방침  |  블로그 소개