struct notifier_block {
int (*notifier_call)(struct notifier_block *nb, unsigned long action, void *data);
struct notifier_block __rcu *next;
int priority;
};
기능 | 함수 프로토타입 |
---|---|
등록 | int notifier_chain_register(struct notifier_block **list, struct notifier_block *n) Wrappers inetaddr_chain register_inetaddr_notifier inet6addr_chain register_inet6addr_notifier netdev_chain register_netdevice_notifier |
해제 | int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n) Wrappers inetaddr_chain unregister_inetaddr_notifier inet6addr_chain unregister_inet6addr_notifier netdev_chain unregister_netdevice_notifier |
알림 | int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v) |
notifier_block
는 우선순위에 의해 정렬된 리스트에 삽입됨notifier_lock
에 의해 보호됨notifier_call_chain()
이 수행static int notifier_call_chain(struct notifier_block **nl,
unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{
int ret = NOTIFY_DONE;
struct notifier_block *nb, *next_nb;
nb = rcu_dereference_raw(*nl);
while (nb && nr_to_call) {
next_nb = rcu_dereference_raw(nb->next);
ret = nb->notifier_call(nb, val, v);
if (nr_calls)
(*nr_calls)++;
if (ret & NOTIFY_STOP_MASK)
break;
nb = next_nb;
nr_to_call--;
}
return ret;
}
n
: 알림 체인val
: 이벤트 타입v
: Custom Input Parameter. 어떤 데이터도 받을 수 있도록 void *
타입으로 선언NOTIFIER_OK
: 잘 처리됨NOTIFIER_DONE
: 관심 없는 알림NOTIFIER_BAD
: 문제 발생. 콜백 호출 중지.NOTIFIER_STOP
: 잘 처리됨. 콜백 호출 중지.NOTIFIER_STOP_MASK
: 콜백 호출 중지를 나타내는 FlagBlocking Notifier Chain
: 콜백이 프로세스 컨텍스트에서 실행 (Block/Sleep 가능)SRCU Notifier Chain
: Blocking Notifier Chain
과 유사하나 SRCU
(Sleepable Read-Copy Update) 사용을 통해 실행 최적화 (관련 정보)Atomic Notifier Chain
: 콜백이 인터럽트/원자적 컨텍스트에서 실행 (Spinlock 사용; Block/Sleep 불가능)Raw Notifier Chain
: 콜백에서 Lock 없음. Caller 측에 동기화 책임inetaddr_chain
: 로컬 인터페이스 IPv4 주소의 삽입, 삭제, 변경에 대한 알림 제공netdev_chain
: 네트워크 장치의 등록 상태에 대한 알림 제공Blocking Notifier Chain
을 등록해 File I/O 시 이벤트를 발생시키고 커널 메시지를 통해 확인한다.static BLOCKING_NOTIFIER_HEAD(lkm_example_chain);
blocking_notifier_head
구조체를 선언하고 초기화하는 매크로blocking_notifier_head
는 include/linux/notifier.h
에 선언struct blocking_notifier_head {
struct rw_semaphore rwsem;
struct notifier_block __rcu *head;
};
rw_semaphore
와 notifier_block
으로 구성static int lkm_example_event(struct notifier_block *nb,
unsigned long event,
void *ptr)
{
printk(KERN_INFO "Event %lu is fired!\n", event);
return NOTIFY_STOP;
}
NOTIFY_STOP
을 통해 첫 notified
에만 알림을 전달하고 끝냄notifier_block
이 하나뿐이므로 큰 차이는 없음static struct notifier_block lkm_example_notifier = {
.notifier_call = lkm_example_event,
};
notifier_chain_register()
을 통한 notifier block
등록을 위해 콜백을 포함한 구조체 선언static int __init lkm_example_init(void) {
major_num = register_chrdev(0, "lkm_example", &file_ops);
if (major_num < 0) {
printk(KERN_ALERT "Could not register device: %d\n", major_num);
return major_num;
} else {
printk(KERN_INFO "lkm_example module loaded with device major number %d\n", major_num);
blocking_notifier_chain_register(&lkm_example_chain, &lkm_example_notifier);
return 0;
}
}
notification chain
도 함께 등록MAJOR
는 Device 종류, MINOR
는 실제 Device를 나타냄 (같은 종류의 Device가 여러 개 존재할 수 있으므로; 관련 정보)static struct file_operations file_ops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};
static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) {
blocking_notifier_call_chain(&lkm_example_chain, 1, NULL);
return 0;
}
static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) {
blocking_notifier_call_chain(&lkm_example_chain, 2, NULL);
return -EINVAL;
}
sudo apt install build-essential linux-headers-`uname -r`
setproxy git clone https://github.com/super-learners/linux-network.git
git pull
cd linux-network/exercise/ch4
make
build
sudo -i
root
로 변신insmod lkm_example.ko
dmesg
...
[xxxx.xxxx] lkm example module loaded with device major number 236
MAJOR
번호는 뒤에서 필요하므로 잘 적어둠mknod /dev/lkm_example c MAJOR 0
/dev/lkm_example
에 만듬c
는 Character Device를 의미MAJOR
에는 위의 번호를 적음cat /dev/lkm_example
dmesg
[xxxx.xxxx] Event 1 is fired!
echo 1 > /dev/lkm_example
dmesg
[xxxx.xxxx] Event 2 is fired!
rm /dev/lkm_example
rmmod lkm_example