Kernel Module Programming Guide
|
KRUG › KTS › KUIP › KUIP/JoinUs › Kernel 2.6 Bridge Firwall + PPTP(VPN) ϱ Part 1. › KernelModuleProgrammingGuide Ŀ α ̵
Peter Jay Salzman
Ori Pomerantz
Copyright © 2001 Peter Jay Salzman
2003-04-04 ver 2.4.0
cloudshape (at) hanafos.com
2003-07-11 ver 2.4.0-trans-0.1
1.1 縻 ¶Ori Pomerantz Yoav Weiss ߸ κе ã Ϳ Ѵ. ״ Frodo Looijaard, Stehpen Judd, Ahltorp, ij Emmanuel PapirakisԵ Ѵ.
ֵ Ori Pomerantz Ѵ. ־ ̰ ̾. ۾ װ ϱ ٶ.
http://www.tldp.org/LDP/lkmpg/www.kernelnewbies.org 鿡Ե 縦 Ѵ. Ư, kernelnewbies.org 峪 ʺ ġ ܿ ô и Mark McLoughlin John Levon 縦 . If this guide teaches you anything, they are partially to blame. ſ ǹ̰ ٸ 鿡Ե å ִ(: ŭ ߿ ).
Ori Richard M. Stallman Linus Tovalds OS ϰ Ͱ װ ۵ϴ ڼ ִ ȸ Ѵ. Linus , ε , ״ λ ȭ ־.
ϰų ̴. Ignacio Martin and David Porter
1.2 Nota Bene ¶Ori's original document was good about supporting earlier versions of Linux, going all the way back to the 2.0 days. I had originally intended to keep with the program, but after thinking about it, opted out. My main reason to keep with the compatibility was for Linux distributions like LEAF, which tended to use older kernels. However, even LEAF uses 2.2 and 2.4 kernels these days.
Both Ori and I use the x86 platform. For the most part, the source code and discussions should apply to other architectures, but I can't promise anything. One exception is Chapter 12, Interrupt Handlers, which should not work on any architecture except for x86.
2.1 Ŀ ̶ ¶ Ŀ ۼϷ Ѵ. C ˰ μ ۵ϴ Ϲ α ۼ ð ۾ , ϳ ϵ Ͱ ý ְ ھ ý ǹѴٴ ˰ ִ.
Ŀ ̶ Ȯ ΰ? ̶ 䱸 Ŀο ǰų ִ ڵ. ý 簡 Ŀ Ȯ ϴ ̴. ̽ ̹. װ ýۿ ϵ Ŀ ֵ ش. ٸ 츮 ƽ Ŀ ٽ ؾ ϸ, Ŀ ̹ ο ߰ Ѿ Ѵ. Ը Ŀο, 츮 ϴ ο ߰ ϱ ؼ Ź Ŀ ٽ ؾ ϰ, ٽ ؾϴ ȴ.
2.2 Ŀο ΰ? ¶lsmod ν ̹ Ŀο ִ , lsmod /proc/modules о ´.
Ŀο ڽ ġ ãƳ? Ŀ ο ʴ Ư¡ Ŀ ʿ䰡 , Ŀ kmod( kerneld ˷ ִ.) ε Ű modprobe Ų. modprobe · ڿ ȴ.
alias char-major-10-30 softdogϹ ̵Ƽ̾ softdog.o Ѵٴ ˰ ȴ. modprobe /lib/modules/version/modules.dep ؼ ٸ 䱸Ǵ DZ Ǿ ϴ° . depmod -a Ǹ, ִ. msdos.o fat.o Ŀο ¸ 䱸Ѵ. û ϴ ɺ( Լ) ٸ ߴ°, ٸ ´.
modprobe Ŀο ϱ insmod ϰ, 䱸 Ѵ. modprobe insmod ǥ 丮 /lib/modules/version/ ϵ Ѵ. insmod ġ ִ. ݸ鿡 modprobe ⺻ ġ ˰ ִ. msdos ϱ Ѵٸ ؾ Ѵ.
insmod /lib/modules/2.5.1/kernel/fs/fat/fat.o insmod /lib/modules/2.5.1/kernel/fs/msdos/msdos.o Ȥ "modprobe -a msdos" .
modprobe, insmode, depmod modutilsȤ mod-utils Ҹ Ű Ѵ
ġ /etc/modules.conf 캸.
#This file is automatically generated by update-modules path[misc]=/lib/modules/2.4.?/local keep path[net]=~p/mymodules options mydriver irq=10 alias eth0 eepro# ϴ ̸ּ, õȴ. path[misc] modprobe /lib/modules/2.4.?/local 丮 misc θ ã üϵ Ѵ. , Ÿ ijͰ ϴ.
path[net] modprobe Ͽ net ~p/modules/ 丮 ã Ѵ. pathnet ٷ Ǵ keep modprobe misc ó ǥ ˻ θ ü ʰ, ش 丮 net ã ǥ ˻ η ߰ϵ Ѵ.
kmod Ϲ ̵Ƽ̾ eth0 ϶ 䱸 , eepro.o ϶ Ѵ.
/etc/modules.conf "alias block-major-2 floppy" ߰ ̴. ֳ, modprobe κ ýۿ Ǵ ǥ ̹鿡 ˰ ֱ ̴.
Ŀο Ǵ ˾ ̴. stacking modules θ ۼѴٸ ͵ ִ. ̰ ̷. ٷ ٷ κ ִ.
2.2.1.1 ¶CONFIG_MODVERSIONS Ŀο Ȱȭ Ű ʾҴٸ, Ư Ŀο ٸ Ŀη ýۿ ʴ´. ̹ Ĺݺ ٷ ̴. modversioning Ȱȭ Ŀ Ѵٸ ۵ ̴. κ Ŀ Ȱȭ Ų · ´. ٸ modversioning Ȱȭ Ų Ŀ ٽ ؾ ̴.
2.2.1.2 X ϱ ¶ Ÿϰ ϰ غ⸦ ϴ. console ۾ϱ Ѵ. X ȯ濡 ۾ .
printf()ó ȭ鿡 ϰ α ַܼθ Ѵ. xterm Ѵٸ, α Ͽ ϵ ̴. α Ȯ ϱ װ ̴. ﰢ ؼ ۾ ֿܼ ϶.
2.2.1.3 Ŀ ¶ ǥ ƴ پϰ ġǼ ǰ װ ߱Ѵ.
ǵ ҿ Ŀ ȴٴ ̴. ڵ带 Ŀ پ ؼ ؾ ̴. Ģ ۵ϱ ʿ ִٰ Ѵ.(䷷ ũ-_- – ڰ )
̷ ϱ ؼ Ŀ ̷ Ʈ ٿ ϰ ý ý Ѵ. ڼ Linux Kernel HOWTO ϱ ٶ.
̰ . ⺻, ýۿ ִ GCC ġ Ŀ ã ƴϰ ⺻ ġƴ Ŀ ( /usr/src/) ã ̴. ̰ gcc -l ɼ ؼ ִ.
3.1 Hello, World (part 1): The Simplest Module ¶ αӰ ó ǻͿ ù α ־ , װ Hello World ڸ 綼 α̾. θ α å Salut Mundi(hello world θ .. J) Ѵ. ̷ ߸ Ͼ , Ű ʾƵ ϴ. Ŀ ۼϴ ⺻ Ǵ ٸ 鿡 Hello World α غ.( ...)
̴. ; 忡 ϴ Ϳ ٷ ̴.
Example 2-1. hello-1.c
/* hello-1.c - The simplest kernel module. */ #include <linux/module.h> /* ¸ðµç ¸ðµâ¿¡ ÇÊ¿ä */ #include <linux/kernel.h> /* KERN_ALERT¿¡ ÇÊ¿ä */ int init_module(void) { printk("<1>Hello world 1.\n"); // 0ÀÌ ¾Æ´Ñ °ªÀ» ¸®ÅÏÇÏ´Â °ÍÀº init_moduleÀÌ ½ÇÆÐÇÑ °ÍÀ» ÀǹÌÇÑ´Ù. °í·Î ¸ðµâÀº ·ÎµåµÇÁö ¸øÇÑ´Ù. return 0; } void cleanup_module(void) { printk(KERN_ALERT "Goodbye world 1.\n"); } Ŀ ּ Լ ؾ Ѵ: Ŀο ȣǴ (ʱȭ) Լ init_module() DZ (rmmod) ȣǴ () Լ cleanup_module() ٷ װ̴. , Ŀ 2.3.13 ̷ κп ȭ ־. ϰų , ̷ ͵ Section 2.3 ̴. ο . ۰ init_module() cleanup_module()Լ Ѵ.
init_module()Լ Ŀο 鷯 ϰų, Ŀ ִ Լ ڵ带 üѴ. Ϲ Ư ۾ , Լ ٽ ȣѴ.). cleanup_module()Լ init_module()Լ ͽ ϰ (unload) ְ Ѵ.
Ŀ linux/module.h ؾ Ѵ. linux/kernel.h printk()Լ log level KERN_ALERT ؼ ⼭ ʿϸ ̷ printk()Լ Ұ ̴.
3.1.1 printk()Լ Ұ ¶ ٸ printk() ڿ ȯ ƴϴ.(츮 ̷ hello-1 printk() ) ̰ Ŀο (logging)ϴ ̸, ϰų ȴ. printk() 켱 Ǹ, /which is the <1> and KERN_ALERT you see /̰ <1> Ȥ KERN_ALERT · ȴ. 8 켱 , Ŀ ϴ ũθ ־ ϱ ȣ ʿ䰡 . ̵ linux/kernel.h ãƺ ִ. 켱 ʰ Ѵٸ ⺻ 켱 DEFAULT_MESSAGE_LOGLEVEL ̴.
켱 ũθ б⸦ Ѵ. 켱 ǹϴ ٵ ش. <4> ȣ KERN_WARNNING ũθ Ѵ.
int console_loglevel 켱 , ش ̳ο ̴. syslogd klogd ̶ ð ַܼ ¿ο /var/log/messages ߰ȴ. printk() α Ͽ ʰ ַܼ ݵ µǵ ϱ KERN_ALERT 켱 Ѵ. ۼ Ȳ ´ 켱 ϱ ̴.
3.2 Ŀ ¶Ŀ ۵ϱ ؼ GCC Ư ɼǰ Բ Ǿ Ѵ. ǵ Ư ɺ Բ ʿ䵵 ִ. ϴ Ŀ ϴ Ŀ ٸ ؾ ϱ 빮̴. GCC –Dɼ ϰų #define μ ɺ ִ. 忡 Ŀ ϱ ʿ ͵ ٷ.
hello-1.c Makefile 캸
Example 2-2. Makefile for a basic kernel module
TARGET := hello-1 WARN := -W -Wall -Wstrict-prototypes -Wmissing-prototypes INCLUDE := -isystem /lib/modules/`uname -r`/build/include CFLAGS := -O2 -DMODULE -D__KERNEL__ ${WARN} ${INCLUDE} CC := gcc-3.0 ${TARGET}.o: ${TARGET}.c .PHONY: clean clean: rm -rf {TARGET}.o hello-1.c ϰ insmod ./hello-1.o Ŀο ÷. ߵǴ°? Ŀο /proc/modules Ʈȴ. Ŀ Ϻΰ ƴ ˾ غ. Ѵ. Ŀڵ ۼڰ ƴ. ſ ̷ΰ rmmod hello-1 Ŀηκ . ý αϿ ƴ /var/log/messages .
ڸ ٸ ִ. ռ init_module()Լ 0 ƴ ٽ ϰ ε . Ͼ°?
3.3 Hello World (part 2) ¶ init cleanup Լ ̸ ٲ ִ. ķ ̸ ݵ init_moduel(), cleanup_module() ʿ . ̴ module_init() module_exit()ũο ̷. ũδ linux/init.h ִ. Ǿ ũθ ȣϱ init cleanupԼ ־ Ѵ , ʴٸ ̴. ̷ ũ ִ.
Example 2-3. hello-2.c
/* hello-2.c - Demonstrating the module_init() and module_exit() macros. This is the preferred over using init_module() and cleanup_module(). hello-2.c – module_init()¿Í module_exit()¸ÅÅ©·ÎÀÇ ¿¹. À̰ÍÀº init_module()°ú cleanup_moduel() º¸´Ù ³´´Ù. */ #include <linux/module.h> // Needed by all modules #include <linux/kernel.h> // Needed for KERN_ALERT#include <linux/init.h> // Needed for the macrosstatic int hello_2_init(void) { printk(KERN_ALERT "Hello, world 2\n"); return 0; } static void hello_2_exit(void) { printk(KERN_ALERT "Goodbye, world 2\n"); } module_init(hello_2_init); module_exit(hello_2_exit); 츮 ΰ Ŀ üߴ. 꼺 ̱ 츮 Makefile Ȱؾ Ѵ. ִ Makefile ̴. Ἲ, Ը鿡 ȭ ̴. ٸ GNU MakefileŴ Ȥ, makefile info б ٶ.
Example 2-4. Makefile for both our modules
WARN := -W -Wall -Wstrict-prototypes -Wmissing-prototypes INCLUDE := -isystem /lib/modules/`uname -r`/build/include CFLAGS := -O2 -DMODULE -D__KERNEL__ ${WARN} ${INCLUDE} CC := gcc-3.0 OBJS := ${patsubst %.c, %.o, ${wildcard *.c}} all: ${OBJS} .PHONY: clean clean: rm -rf *.o ڵ , 丮 hello-3.c ϳ ִٸ ڵ ϱ Makefile ΰ?
3.4 Hello World (part 3): The __init and __exit Macros ¶ Ŀ 2.2 Ȥ . init cleanup Լ ǿ ȭ ָ϶. ̹ initԼ Ǹ, __initũδ initԼ (-_-) ȯȴ. Ұϴ. initԼ ȣǴ Ѵٸ ̰ Ÿϴٰ ̴.
initԼ ü ̶ ٴ init ۿϴ __init ũ __initdata ͵ ִ.
__exitũδ Ŀη Ʈ( ϴ ƴ Ŀ Ϻη ϴ ) , Լ ȣ ϸ __exitó ġ ʴ´. cleanupԼ ۵ϴ Ѵٸ ص ̴. ̹ cleanupԼ ʿ . ݸ鿡 ʿϴ.
ũε linux/init.h ְ Ŀ ϴµ ȴ. Freeing unused kernel memory: 236k freed , Ŀ ϴ ̴.
Example 2-5. hello-3.c
/* hello-3.c - Illustrating the __init, __initdata and __exit macros. */ #include <linux/module.h> /* Needed by all modules */ #include <linux/kernel.h> /* Needed for KERN_ALERT */ #include <linux/init.h> /* Needed for the macros */ static int hello3_data __initdata = 3; static int __init hello_3_init(void) { printk(KERN_ALERT "Hello, world %d\n", hello3_data); return 0; } static void __exit hello_3_exit(void) { printk(KERN_ALERT "Goodbye, world 3\n"); } module_init(hello_3_init); module_exit(hello_3_exit); 2.2 Ŀ ̹ __initfunction() Լ ̴.
__initfunction(int init_module(void)) { printk(KERN_ALERT "Hi there.\n"); return 0; } 3.5 Hello World (prt 4): ۱ǰ ¶Ŀ 2.4 Ѵٸ, ռ ̴.
# insmod hello-3.o Warning: loading hello-3.o will taint the kernel: no license See http://www.tux.org/lkml/#export-tainted for information about tainted modules Hello, world 3 Module hello-3 loaded, with warnings 2.4 Ŀο GPL ̼ ڵ带 ĺϱ Ŀ ȵż, ҽ ƴ ڵ鿡 ְ ִ. ̴ ڵ忡 õ MODULE_LICENSE() ũο ȴ. GPL̼ ν µǴ ִ. ̷ ̼ Ŀ linux/module.h ǵְ ȭ Ǿ ִ.
ϰ MOUDLE_DESCRIPTION() ϴ° , MOUDLE_AUTHOR() ڸ MODULE_SUPPORTED_DEVICE() Ÿ ġ ϴ ˷ش.
̷ ũε linux/module.h ǵְ Ŀ ü ʴ´. ̰͵ ȭ ϸ, objdump ̿ ִ. ڵ linux/drivers ڵ ȭ ϱ ũε ߴ° ã ٶ.
Example 2-6. hello-4.c
/* hello-4.c - Demonstrates module documentation. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #define DRIVER_AUTHOR "Peiter Jay Salzman <p@dirac.org>" #define DRIVER_DESC "A sample driver" int init_hello_3(void); void cleanup_hello_3(void); static int init_hello_4(void){ printk(KERN_ALERT "Hello, world 4\n"); return 0; } static void cleanup_hello_4(void) { printk(KERN_ALERT "Goodbye, world 4\n"); } module_init(init_hello_4); module_exit(cleanup_hello_4); /* You can use strings, like this: */ MODULE_LICENSE("GPL"); // Get rid of taint message by declaring code as GPL.. /* Or with defines, like this: */ MODULE_AUTHOR(DRIVER_AUTHOR); // Who wrote this module? MODULE_DESCRIPTION(DRIVER_DESC); // What does this module do? /* This module uses /dev/testdevice. The MODULE_SUPPORTED_DEVICE macro might be used in the future to help automatic configuration of modules, but is currently unused other than for documentation purposes. */ MODULE_SUPPORTED_DEVICE("testdevice"); 3.6 Ŀǵ ѱ ¶ Ŀǵ ڸ ִ. ϴ ó argc/argv .
ڸ ѱ ؼ Ŀǵ Ŀ Ȱȭ Ű MODULE_PARAM()ũθ Ѵ. ð insmod ־ ڷ ä ̴. ũε Ȯ ο ġؾ Ѵ. ش.( ^^)
MODULE_PARAM() ũδ 2 ڸ ´ ̸ Ÿ ϴ Ÿ 1byteΡb, short int h, integer i, long l, string s ̴. ڿ(strings – string strings ) char * ؾ ϸ, insmod ڿ(strings) ҴѴ. ʱȭϴ Ѵ. ̰ Ŀ ڵ̴. ݵ α ؾ Ѵ. .
int myint = 3; char *mystr; MODULE_PARM (myint, "i"); MODULE_PARM (mystr, "s"); int myshortArray[4]; MODULE_PARM (myintArray, "2-4i"); Example 2-7. hello-5.c
/* hello-5.c - Demonstrates command line argument passing to a module. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Peiter Jay Salzman"); static short int myshort = 1; static int myint = 420; static long int mylong = 9999; static char *mystring = "blah"; MODULE_PARM (myshort, "h"); MODULE_PARM (myint, "i"); MODULE_PARM (mylong, "l"); MODULE_PARM (mystring, "s"); static int __init hello_5_init(void) { printk(KERN_ALERT "Hello, world 5\n=============\n"); printk(KERN_ALERT "myshort is a short integer: %hd\n", myshort); printk(KERN_ALERT "myint is an integer: %d\n", myint); printk(KERN_ALERT "mylong is a long integer: %ld\n", mylong); printk(KERN_ALERT "mystring is a string: %s\n", mystring); return 0; } static void __exit hello_5_exit(void) { printk(KERN_ALERT "Goodbye, world 5\n"); } module_init(hello_5_init); module_exit(hello_5_exit); Supercalifragilisticexpialidocious(ڰ ȣ ϳ... -_-)
3.7 ¶δ Ŀ ҽϷ ִ. ̷ ʿϴ.
Example 2-8. start.c
/* start.c - Illustration of multi filed modules */ #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> /* Specifically, a module */ int init_module(void) { printk("Hello, world - this is the kernel speaking\n"); return 0; } Example 2-9. stop.c
/* stop.c - Illustration of multi filed modules */ #if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) #include <linux/modversions.h> /* Will be explained later */ #define MODVERSIONS #endif #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> /* Specifically, a module */ #define __NO_VERSION__ /* It's not THE file of the kernel module */ #include <linux/version.h> /* Not included by module.h because of __NO_VERSION__ */ void cleanup_module() { printk("<1>Short is the life of a kernel module\n"); } Makefile
Example 2-10. Makefile for a multi-filed module
CC=gcc MODCFLAGS := -O -Wall –DMODULE -D__KERNEL__ hello.o: hello2_start.o hello2_stop.o ld -m elf_i386 -r -o hello2.o hello2_start.o hello2_stop.o start.o: hello2_start.c ${CC} ${MODCFLAGS} -c hello2_start.c stop.o: hello2_stop.c ${CC} ${MODCFLAGS} -c hello2_stop.c 4.1.1 ϰ ° ¶Ϲ α main()Լ Ѵ, ɾ ϰ ɾ Ϸ ȴ. Ŀ ణ ٸ ۵Ѵ. init_module()Լ, Ȥ module_init() Լ Ѵ. ̰ Լ(entry function); ʿ Լ Ŀο ̸ Ǵ ˷ ش. ̷ ̷ ϴ Լ ϰ ϴ ڵ带 Ŀ ΰ Ϸ ƹ ʴ´.
cleanup_module(), Ȥ module_exit() ִ Լ ȣ ȴ. ̰ Լ(exit function) ȴ. Լ Լ ϴ Ų. Լ ϵ ִ Ѵ..
Լ Լ ´. ϳ ̻ Լ Լ ϴ ֱ, `Լ(entry function)' `Լ(exit function)' ܾ ϰڴ. init_module() cleanup_module()̶ ϴ, ڵ ǹϴ ̶ ϰڴ.
4.1.2 Լ ¶αӵ ڽŵ Լ ϴ ƴϴ. ǥ printf(). αӵ ǥ C̺귯 ϴ ̷ ̺귯 Լ Ѵ. ̷ Լ Ǵ ŷ (linking stage) α ʴ´. ̷ ڵ尡 ϴٴ ְ ġ ɾ(instruction) ġ ش.
⼭ Ŀ ٴ . , 츮 printk() Լ ߴ װ ǥ ̺귯 Լ ƴ ̴. insmod ؼ ɺ Ʈ ̱ ̴. ɺ Ǵ Ŀ ü ִ. ܺ Լ Ŀο Ǵ ͵鿡 Ǿ. ɺ(- ʰ ִ Լ) 밡 ñϴٸ /proc/ksyms .
̺귯 Լ ý . ̺귯 Լ 鼭, Ϻϰ Ǹ, αӰ ۾ ϴµ Ǹ ش. ý Ŀο Ǹ Ŀ ȴ. ̺귯 Լ printf() Ϲ Լó δ. װ ϴ Ʈ ϰ Ʈ ο췹 ý write() ̿ϴ ̴, װ ǥ .
Printf() ִ ? װ ˾ƺ . α غ.
#include <stdio.h> int main(void) { printf("hello"); return 0; } ŵ Ŀ ý üϱ ̴.(츮 Ұ̴) ũĿ 鵵 ̷ ͵ Ѵ. ýۿ ִ õ ̿ؼ 峭 ġ(Tee hee, that tickles! ǿ) ȭ ̴.
4.1.3 vs Ŀ ¶Ŀ ī, ϵ ̺, ҽ Ѵ. α ҽ Ѵ. , updatedb ġ ̽ ϱ Ѵ. vim ǰ updatedb ϵ 긦 ÿ ϴ ̴. Ŀ ̷ ͵ ʿ䰡 , ڰ ϴ ڿ ϵ ־ ȴ. ̷ CPU ٸ ۵Ѵ. ýۿ ϰ ϴ Ͽ ٸ ش. 30386 ŰĴ 4 带 ִ. н ˷ 0 (ֻ – ring ܾ... -_-) Ҹ , Ѵ.
̺귯 Լ ý ݿ Ǹ . 忡 ̺귯 Լ Ѵ. ̺귯 Լ ϳ Ȥ ̻ ý ȣѴ. ̷ ý ̺귯 Լó ൿѴ. 忡 Ŀ Ϻ̱ ൿѴ. ý ۾ Ϸϸ (return)ϰ Ѵ.
4.1.4 ̸ ¶ Cα ۼ , ڵ带 ̸ ̴. Ը ū α Ϻθ ۼϰ , ϴ ̸ ٸ ̸ ִ. ŭ ǹ ִ ̸ Ѵٸ ̸ ̴. Ը Ʈ ̸ ̷ ݵ ־ ϸ, ɺ ũ ȹ ʿϴ.
Ŀ ڵ带 ۼ װ ƹ ̶ Ŀ ü ũDZ ̰ и ȴ. ̰ ذϴ ϰ, ǵ ξ ɺ ϴ ̴. Ŀ ξ ҹڴ. ϱ ġ ʴ´, symbol table ϰ װ Ŀο ϽŰ . Ŀ ̰ ϰ ̴.
/proc/ksyms Ŀ ˰ ִ ɺ ְ, ɺ Ŀ ڵ ̽ DZ , / ִ.
4.1.5 ڵ ¶ ſ . `Understanding The Linux Kernel κ д. 츮 ƴ ۼϴµ ؾ ǿ ʿ ִ.
Ʈ Ʈ ǹϴ غ ʾҴٸ Ͱ ġ ϰ ʴٴ Ǹ ̴. · ͵ ʴ. μ , Ŀ Ϻθ Ҵ, (ǻ ڳ ˹ ͵) μ ϴ ڵ, , , ϵ μ ѱ. $0$ μ ʿ ŭ Ȯȴ. ٸ μ ġ ʱ 0xbffff978 ϴ μ ٸ ̴. μ Ư μ Ҵ ϴ 0xbffff978 ̸ ε Ϸ ̴. Ŀ ٷ ұϰ, 츮 Hello, World κ ٸ μ .
Ŀ ڽŸ ִ. Ŀο ǰų ŵ ִ ڵ , ڽŸ Ŀ ڵ ٴ Ŀ ڵ Ѵ. , ü ݵǴ ̴. (as opposed to a semi-autonomous object ǿ) Ƿ 츮 Ʈ Ʈ Ŀ Ʈ Ʈ ȴ. off-by-one Ѵٸ, Ŀ ڵ带 Ʈ ̴. ̰ ɰϹǷ Ǹ ← Ѵ. ( off-by-one error : n=0 n=1 ؼ Ͼ ǹ)
ǵ ƽ Ŀ ϴ ۷ ýۿ ȴٴ ϰ ʹ. ڱ ڽŸ ڵ ũ Ŀ̶ ͵ ִ. GNU Hurd QNX Neutrino ũ Ŀ .
4.1.6 ġ ̹ ¶ ġ ̸̹, TVī峪 ø Ʈ ϵ Ѵ. н ϳ ϵ ϵ ǻ ϴ ϴ named device (/dev/ Ʒ ġѴ.) . ̽ ̹ α , ǻ Ѵ. es1370 ī ̹ Ensoniq IS1370 ī忡 ϱ /dev/sound device Ѵ. mp3blaster ̽ α ī尡 ġƴ ü /dev/sound ġ ִ.
4.1.6.1 ġ ȣ ġ ȣ ¶̽ 캸. ̸Ӹ IDEϵ ̹ ù Ƽ Ÿ ̽ ̴.
# ls -l /dev/hda[1-3] brw-rw---- 1 root disk 3, 1 Jul 5 2000 /dev/hda1 brw-rw---- 1 root disk 3, 2 Jul 5 2000 /dev/hda2 brw-rw---- 1 root disk 3, 3 Jul 5 2000 /dev/hda3 е ȣ ֽ ù ° ȣ ġ ȣ θ ° ȣ ȣ θ. ȣ ̹ ϵ ϴ ˷ش. ̹ ȣ Ҵ , ȣ ̽ ̹ Ʈ ȴ. ȣ 3 , ̹ Ʈ DZ ̴.
ȣ ̹ ڽ Ʈϴ ϵ ϱ ȴ. ư , ġ ̹ ٸ ȣ µ, ̴ ̹ (ϵ ũ Ƽǵ) ٸ ϵ νϱ ̴.
̽ ij ̽ ̽ Ÿ . ̽ ۸ ־, ϴ ΰ ִٴ ̴. ִ ͺ ִ Ϳ б/⸦ ϴ ٴ ġ ־ ߿ϴ. ٸ ̽ ִٴ ̴( ũ ġ ٸ). ݸ鿡 ij ̽ Ʈ ʴ ũ⸸ Ѵ. κ ġ ij ̴̽. ֳϸ ġ κ ̷ ʿ ʰ ũ ۵ ʱ ̴. ls –l ù ° ڸ ν ̽ ̽ ij ̽ ִ. ù ڰ b̸ ϵ̽, c̸ ij ̴̽. ij ̽ (ø Ʈ).
crw-rw---- 1 root dial 4, 64 Feb 18 23:34 /dev/ttyS0 crw-r----- 1 root dial 4, 65 Nov 17 10:26 /dev/ttyS1 crw-rw---- 1 root dial 4, 66 Jul 5 2000 /dev/ttyS2 crw-rw---- 1 root dial 4, 67 Jul 5 2000 /dev/ttyS3 ȣ Ҵƴ° ˱⸦ Ѵٸ, /usr/src/linux/Documentation/devices.txt ϱ ٶ. ý ġ , ̷ ̽ ϵ mknod ȴ. ֹȣ/ιȣ 12, 2 coffee ο ij ̽ ϰ Ѵٸ, ܼ mknod /dev/coffee c 12 2 Ű ȴ. ̽ /dev/ ʿ . ܼ ̴. ̽ /dev/ ־ ŵ ϴ ̴. Ʈ ̽ Ѵٸ, Ŀ ۾ 丮 ̽ ־ ϴ. ̽ ̹ ۼ Ϸ ùٸ ġ ȴ.
ߴ ͵ Ǵ ϱ⸦ Ѵ. װ͵ 쿡 ѵȴ. ̽ Ŀ ġ ȣ ̹ ̿ ó ΰ ϱ Ѵ. ̾߱ Ŀ ġ ȣ ʿ䰡 , ȴٴ ̴. ġ ȣ ̹ ü ̴. ϵ ߿ ġΰ ϱ ؼ ġ ȣ ȴ.
ϵ , տ ִ PCIī ̻ ǹϴ ̴. ̽ .
% ls -l /dev/fd0 /dev/fd0u1680 brwxrwxrwx 1 root floppy 2, 0 Jul 5 2000 /dev/fd0 brw-rw---- 1 root floppy 2, 44 Jul 5 2000 /dev/fd0u1680̽ ̽ Ͱ ̹ óȴٴ ̴. ̽ ÷ ̺긦 Ÿٴ ̴. ϳ ÷ ̹ . ΰ? ϳ 1.44MB ÷ ̺긦 Ÿ. ٸ ϳ ϴ superformatted ̺꿡 شǴ 1.68MB ġ. ǥ ÷ ϴ ̴. ̰ ϵ ٸ ġ ȣ . 츮 ǿ ϵ ϴ ܾ ̶ ˱ ٶ. 5.1.1 file_operations ü ¶file_operations ü linux/fs.h ǵ ġ鿡 پ ۵ ϴ ̹ ǵ Լ ִ. ü ʵ 䱸Ǵ ۵ ϱ ̹ ǵ Լ ּҿ ȴ.
, ġ ̹ ġκ о Լ ʿ䰡 ִ. file_operations ü ϴ Լ ּҸ ִ ̴. Ŀ 2.4.2 Ǵ .
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); }; ۵ ̹ ʴ´. ī带 ٷ ̹ 丮 ü о ʿ . file_operationsü ش ʵ NULL õȴ.
gcc Ȯ ü Ҵ ϰ ش. ֱ ̹鿡 ̷ ͵ ִ, Ƹ ( ) ִ. ̷ ο Ҵ ش.
struct file_operations fops = { read: device_read, write: device_write, open: device_open, release: device_release; } C99 ü Ҵĵ ϸ GNUȮ Կ ȣǰ ִ. ϰ ִ gcc 2.95 C99 ο κ ϰ ִ. ̹ ϱ ϴ ؾ ̴. ؽ ִ.
struct file_operations fops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release }; ǹ̴ Ȯϴ Ҵ ü gcc NULL ʱȭȴٴ . file_operations ü ʹ Ϲ fops ̸ ´.
5.1.2 file ü ¶ ġ fileü Ŀ ο . װ linux/fs.h ִ. ü Ŀ ο ϸ α ʴ´ٴ . ü ʴ. FILEü glibc ǵ ְ Ŀο ʴ´. ̸ ߸ صDZ Ѵ. װ ǹ ũ inodeü õǴ ǹϴ ƴϴ.
fileü ʹ Ϲfilp ̸ ´. struct file file ȥ ( -_-)
fileü Ǹ 캸. κ dentryüó ̽ ̹ ʴ´. κ ص . ̽ ̹ fileü ä ʰ ü ̿ϱ⸸ ̴.
5.1.3 Registering A Device ¶ռ ó ġ dev[1] ִ ̽ ٵȴ. ġ ȣ ̹ ̽ ϴ° ˷ش. ġȣ ̹ ϳ ̻ ġ ۵ų , ġ Ǵ° ϱ ̹ ο ȴ.
ýۿ ̹ ߰Ѵٴ Ŀο װ ϽŰ ǹѴ. ʱȭǴ ġ ȣ οѴٴ Ͱ ǹ̴. linux/fs.h ǵ register_chrdev()Լ ν ̷ ۾ Ѵ.
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
unsigned int major û ġ ȣ̰, const char *name /proc/devices ġ ̸̰ struct file_operations *fops ̹ file_operations table ʹ. ġ ̹ Ͽ ̴. ġ ȣ register_chrdev() ѱ ʴ´ٴ Ϳ . Ŀ ġ ȣ Ű澲 ʱ ̴. 츮 ̹ ȣ Ѵ.
̹ ȣ ä ʰ ġ ȣ Ĵ . Documentation/devices.txt ʴ ϴ ̴. ȣ ߿ Ƿ ƴϴ. ذå Ŀο ġ ȣ Ҵ ûϴ ̴.
register_chrdev() ġ ȣ 0 ѱ, Ҵ ġ ȣ ش. ġ ȣ ̸ ̽ ٴ ִ. ذ å ִ. 켱, ̹ ڽſ Ҵ ġ ȣ ϰ ۾ ġ ̴. , /proc/devices ϵ ġ ̴. 츮 ̽ Ȥ о 鿩 ̽ ũƮ ȴ. ° ġ ̹ mknod ̿ ̽ cleanup_module() ȣ rm ϴ ̴.
5.1.4 ġ ¶root ʹٰ ؼ Ŀ rmmodϰ . μ ġ , ġ õ read/write ùٸ Լ ϴ ġ ٸ Ͽ ϰ ̴. ٸ װ ƹ ڵ嵵 ε ̰ ̴. 츮 ٸ ġ ٸ Ŀ ö ̰ װ Ŀ ٸ Լ ߰ ġ ٲ ̴. ſ ̴.
Ϲ 츮 ΰ 㰡 ϰ ʴٸ óϴ Լ ڵ() ϵ Ѵ. void Ÿ̱ cleanup_module() ̰ Ұ ϴ. μ ̿ϰ ִ ϴ īͰ ִ. /proc/modules ° ʵ ٷ ̴̰. ȣ 0 ƴ϶, rmmod Ѵ. linux/module.c ִ sys_delete_module() üũǰ Ƿ cleanup_module() īƮ ʿ䰡 . ī , linux/modules.h ǵ ũΰ ִ. װ ī Űų ҽŰ Ѵ.
5.1.5 chardev.c ¶ ڵchardev ̸ ̹ . ġ ȭ鿡 (Ȥ ٸ α ̿ ִ) ִ. ̹ Ƚ Ͽ Ѵ. echo "hi" > /dev/hello Ͽ ϴ ʴ´. ̷ õ ڿ ʴ ٴ ˷ ش. 츮 о ۿ ϴ 츮 ϴ ٰ ; 츮 װ ʴ´. о Ҵٴ Ѵ.
Example 4-1. chardev.c
/* chardev.c: ¾ó¸¶³ª ¸¹ÀÌ µð¹ÙÀ̽º ÆÄÀÏ¿¡ Á¢±ÙÇß´ÂÁö ¾Ë·ÁÁÖ´Â ¸ðµâ */ #if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) #include <linux/modversions.h> #define MODVERSIONS #endif #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> /* for put_user */ /* Prototypes - this would normally go in a .h file */ int init_module(void); void cleanup_module(void); static int device_open(struct inode *, struct file *); static int device_release(struct inode *, struct file *); static ssize_t device_read(struct file *, char *, size_t, loff_t *); static ssize_t device_write(struct file *, const char *, size_t, loff_t *); #define SUCCESS 0 #define DEVICE_NAME "chardev" /* /proc/devices¿¡ ³ªÅ¸³ª´Â ÀåÄ¡ À̸§ */ #define BUF_LEN 80 /* ÀåÄ¡·ÎºÎÅÍ ¸Þ½ÃÁöÀÇ ÃÖ´ë ±æÀÌ */ /* Á¤Àû º¯¼ö·Î Àü¿ªº¯¼ö ¼±¾ð */ static int Major; /* ÁÖÀåÄ¡ ¹øÈ£ */ static int Device_Open = 0; /* ÀåÄ¡°¡ ¿·È´Â°¡? Áߺ¹»ç¿ë ¹æÁö */ /* access to the device */ static char msg[BUF_LEN]; /* ¿äûÀÌ ÀÖÀ» ¶§ ÀåÄ¡°¡ º¸³»´Â ¸Þ½ÃÁö */ static char *msg_Ptr; static struct file_operations fops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release }; /* Functions */ int init_module(void) { Major = register_chrdev(0, DEVICE_NAME, &fops); if (Major < 0) { printk ("Registering the character device failed with %d\n", Major); return Major; } printk("<1>I was assigned major number %d. To talk to\n", Major); printk("<1>the driver, create a dev file with\n"); printk("'mknod /dev/hello c %d 0'.\n", Major); printk("<1>Try various minor numbers. Try to cat and echo to\n"); printk("the device file.\n"); printk("<1>Remove the device file and module when done.\n"); return 0; } void cleanup_module(void) { /* Unregister the device */ int ret = unregister_chrdev(Major, DEVICE_NAME); if (ret < 0) printk("Error in unregister_chrdev: %d\n", ret); } /* Methods */ /* "cat /dev/mycharfile" ó·³ ÇÁ·Î¼¼½º°¡ µð¹ÙÀ̽º ÆÄÀÏÀ» ¿·Á°í ÇÒ ¶§ È£ÃâµÊ */ static int device_open(struct inode *inode, struct file *file) { static int counter = 0; if (Device_Open) return -EBUSY; Device_Open++; sprintf(msg,"I already told you %d times Hello world!\n", counter++); msg_Ptr = msg; MOD_INC_USE_COUNT; return SUCCESS; } /* µð¹ÙÀ̽º ÆÄÀÏÀÌ ´ÝÈú ¶§ È£ÃâµÊ. */ static int device_release(struct inode *inode, struct file *file) { Device_Open --; /* We're now ready for our next caller */ /* »ç¿ë Ä«¿îÆ® °¨¼Ò ȤÀº Çѹø ¿¾îº» ÆÄÀÏÀÌ ¾Æ´Ï¶ó¸é ¸ðµâÀ» Á¦°ÅÇÒ ¼ö ¾øÀ½ */ MOD_DEC_USE_COUNT; return 0; } /* ÀÌ¹Ì ¿¸° ÀåÄ¡ ÆÄÀÏ¿¡¼ ¹«¾ùÀΰ¡ ÀÐÀ¸·Á ÇÒ ¶§ È£Ãâ */ static ssize_t device_read(struct file *filp, char *buffer, /* The buffer to fill with data */ size_t length, /* The length of the buffer */ loff_t *offset) /* Our offset in the file */ { /* ¹öÆÛ¿¡ ½ÇÁ¦ ¾²¿©Áø ¹ÙÀÌÆ® ¼ö */ int bytes_read = 0; /* ¸Þ½ÃÁöÀÇ ³¡¿¡ ¸Þ½ÃÁöÀÇ ³¡ÀÓÀ» ¾Ë¸®±â À§ÇØ 0¸®ÅÏ */ if (*msg_Ptr == 0) return 0; /* ¹öÆÛ¿¡ ½ÇÁ¦ µ¥ÀÌÅ͸¦ ÀÔ·Â */ while (length && *msg_Ptr) { /* ¹öÆÛ´Â Ä¿³Î ¼¼±×¸ÕÆ®°¡ ¾Æ´Ï°í »ç¿ëÀÚ ¼¼±×¸ÕÆ®´Ù * ÇÒ´çÀº ÀÏ¾î ³ªÁö ¾Ê´Â´Ù. Ä¿³Î µ¥ÀÌÅÍ¿µ¿ª¿¡¼ »ç¿ëÀÚ ¿µ¿ªÀ¸·Î * µ¥ÀÌÅ͸¦ º¹»çÇÏ´Â put_user() »ç¿ë */ put_user(*(msg_Ptr++), buffer++); length--; bytes_read++; } /* ÀÐÇôÁø µ¥ÀÌÅÍÀÇ ¹ÙÀÌÆ® ¼ö¸¦ ¹öÆÛ¿¡ ±â·Ï */ return bytes_read; } /* Called when a process writes to dev file: echo "hi" > /dev/hello */ static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t *off) { printk ("<1>Sorry, this operation isn't supported.\n"); return -EINVAL; } 5.1.6 Ŀ ۼ ¶Ŀΰ μ ֿ ̽ ý Ϲ ġ Ѵ. ý ߰Ǵ ý Ѵ. ̰ ʿϴ. – Ŀ Ϲ μ Ű ʴ´. κ , ġ ״ ִ. On the other handݸ鿡 , Ŀ ̽ ٲ.
Ŀ (n.$<$¦$>$.m) ߹ (n.$<$Ȧ$>$.m) . ٽ ؾ Ѵٰų ִ ̵ ϰ ִ. , ̷ ߹ ̽ ŷ ( ۿ װ͵ ٷ ʴ ̱ ϴ. װ ̰ ٲ ٲ.). ݸ鿡 , ο ̽ ִ.
ٸ Ŀο ٸ ִ. Ŀ Ϸ Ѵٸ, ڵ ʿ ˰ ̴.. LINUX_VERSION_CODE KERNEL_VERSION ϴ . Ŀ a.b.c, ũ
̴. Ŀ2.0.35 ũΰ ٴ ˾Ƶ. Ŀ ϴ ۼϰ Ѵٸ, ؾ Ѵ.
Example 4-2. some title
#if LINUX_KERNEL_VERSION >= KERNEL_VERSION(2,2,0) #define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) #endif ̷ ũΰ ֱ , ũ 翩θ Ʈϱ Ŀ Ʈ ϴ ͺ #ifndef KERNEL_ VERSION ϴ ̴.
6.1 /proc File System ¶ Ŀΰ Ŀ μ ߰ ִ. /proc ý װ̴. ̰ μ ϵ ̴(̸ .). Ʈ ִ /proc/modules, 뷮 ִ /proc/meminfo Ŀ ͵鿡 ȴ.
/proc ý ϴ ̽ ̹ ̿ϴ ſ ϴ. Լ ڵ鷯 /procϿ ʿ ü Ѵ.( 츮 Ѱ , /proc Ϸκ ΰ õ װ ȣȴ.). Ŀο init_module() ü Ŀο ϰ cleanup_module Ѵ.
츮 츮 ̿ inode ȣ ϱ⸦ ʰ Ŀ ϰ ؼ ý ٿ ϱ ϱ 츮 proc_register_dynamic()[1]Լ Ѵ. Ϲ ϱ⺸ٴ ũ Ѵ.(װ /proc̴.), ε (index-node, inode ) ġ ũ inodeȣ Ѵ. inodeü Ͽ ִ, ۹̼, ũ ã ִ° .
ų 츮 ȣ ʾұ , ο MOD_INC_USE_COUNT MOD_DEC_USE_COUNT . ¿ ŵ . 忡 츮 , װ /proc ٷµ ־ ̴. װ ʰ ֱ Ѵ.
Example 5-1. procfs.c
/* procfs.c - create a "file" in /proc */ #include <linux/kernel.h> /* Ä¿³Î ÀÛ¾÷ */ #include <linux/module.h> /* ¸ðµâ ÀÛ¾÷ */ /* Deal with CONFIG_MODVERSIONS */ #if CONFIG_MODVERSIONS==1 #define MODVERSIONS #include <linux/modversions.h> #endif /* proc fs¸¦ ´Ù·é´Ù */ #include <linux/proc_fs.h> /* 2.2.3 ¹öÀü ÀÌÀü¿¡´Â ´ÙÀ½ÀÇ ¸ÅÅ©·Î°¡ ¾ø´Ù. ÇÊ¿äÇÏ´Ù¸é ¿©±â¼ Á¤ÀÇ ÇÑ´Ù.*/ #ifndef KERNEL_VERSION#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) #endif /* proc fs¿¡ µ¥ÀÌÅÍ Àü´Þ. Argument ========= 1. µ¥ÀÌÅͰ¡ »ðÀԵǴ ¹öÆÛ, ¸¸ÀÏ »ç¿ëÇϱ⠿øÇÑ´Ù¸é 2. ¹®ÀÚ¿¿¡ ´ëÇÑ Æ÷ÀÎÅÍ, ¸¸ÀÏ Ä¿³ÎÀÌ ¹öÆÛ¸¦ ÇÒ´çÇϱ⠿øÇÏÁö ¾Ê´Â °æ¿ì »ç¿ë 3. ÆÄÀÏ¿¡¼ ÇöÀç À§Ä¡ 4. ù ÀÎÀÚ ¹öÆÛÀÇ Å©±â 5. 0(¹Ì·¡ÀÇ »ç¿ëÀ» À§ÇØ?). Usage and Return Value ====================== ¸¸ÀÏ ÀڽŸ¸ÀÇ ¹öÆÛ¸¦ ¿øÇÑ´Ù¸é ³»°¡ ÇÑ °Íó·³ µÎ¹ø ° ÀÎÀÚ¿¡ ³Ö°í ¹öÆÛ¿¡¼ »ç¿ëÇÑ ¹ÙÀÌÆ® ¼ö¸¦ ¸®ÅÏÇÏ¶ó ¸®ÅÏ °ªÀÌ NULLÀΰÍÀº ´õ ÀÌ»óÀÇ Á¤º¸°¡ ¾øÀ½À» ÀǹÌÇÑ´Ù(end of file). ¶ÇÇÑ À½¼öÀÎ °æ¿ì ¿¡·¯ »óȲÀÌ´Ù. For More Information ==================== The way I discovered what to do with this function ³»°¡ ÀÌ ÇÔ¼ö¿¡¼ ¹«¾ùÀΰ¡¸¦ ¹ß°ßÇÑ ¹æ¹ýÀº ¹®¼¸¦ Àд °ÍÀÌ ¾Æ´Ï°í, »ç¿ëµÇ´Â Äڵ带 Àд °ÍÀ̾ú´Ù. proc_dir_entry±¸Á¶Ã¼ÀÇ get_infoÇʵ带 ¾îµð´Ù ¾²´Â°¡¸¦ »ìÆìºÃ´Ù. ¶ÇÇÑ /fs/proc/array.c ÆÄÀÏÀÌ »ç¿ëµÇ´Â °ÍÀ» »ìÆì º» °ÍÀÌ´Ù. If something is unknown about the kernel, this is ¹«¾ð°¡ Ä¿³Î¿¡¼ Àß ¸ð¸£´Â ºÎºÐÀÌ ÀÖ´Ù¸é ÀÌ·± ¹æ½ÄÀÌ ÀϹÝÀûÀ¸·Î ÇàÇØÁø´Ù. ¸®´ª½º¿¡¼ ¿ì¸®´Â Ä¿³Î Äڵ带 ¾òÀ» ¼ö ÀÖ´Ù´Â °ÍÀº ´ë´ÜÇÑ ÀåÁ¡ÀÌ´Ù. Ȱ¿ëÇ϶ó. */ int procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int zero) { int len; /* ½ÇÁ¦ »ç¿ëµÇ´Â ¹ÙÀÌÆ® ¼ö */ /* ¿ì¸®°¡ ÀÌ ÇÔ¼ö¸¦ ³ª°¥ ¶§µµ ÀÌ ½ºÅׯ½ º¯¼öÀ̱⠶§¹®¿¡ ¸Þ¸ð¸®¿¡ ³²°Ü Áø´Ù. */ static char my_buffer[80]; static int count = 1; /* ¿ì¸®(Á÷¿ª)´Â Çѹø¿¡ ¸ðµç Á¤º¸¸¦ Áֱ⠶§¹®¿¡ À¯Àú(?Á÷¿ª)°¡ Ãß°¡ÀûÀÎ Á¤º¸¸¦ ¿ä±¸ÇÒ ¶§ ¿ì¸®ÀÇ ´ë´äÀº NO´Ù. * Ç¥ÁØ ¶óÀ̺귯¸® ÇÔ¼ö´Â Ä¿³ÎÀÌ ´õ ÀÌ»ó ÁÙ Á¤º¸°¡ ¾ø´Ù°í ¸»ÇÒ ¶§±îÁö, ȤÀº Ç¥ÁØ ¶óÀ̺귯¸® ÇÔ¼öÀÇ ¹öÆÛ°¡ ´Ù Âû ¶§±îÁö * read ½Ã½ºÅÛ ÄÝÀ» Çϱ⠶§ ¹®¿¡ Áß¿äÇÏ´Ù. */ if (offset > 0) return 0; /* Fill the buffer and get its length */ len = sprintf(my_buffer, "For the %d%s time, go away!\n", count, (count % 100 > 10 && count % 100 < 14) ? "th" : (count % 10 == 1) ? "st" : (count % 10 == 2) ? "nd" : (count % 10 == 3) ? "rd" : "th" ); count++; /* ÇÔ¼ö¿¡ ¹öÆÛÀÇ À§Ä¡¸¦ ¾Ë¸°´Ù. */ *buffer_location = my_buffer; /* Return the length */ return len; } struct proc_dir_entry Our_Proc_File = {0, /* Inode ¹øÈ£ –¿©±â¼´Â ¹«½ÃÇÑ´Ù. À̰ÍÀº proc_register[_dynamic]¿¡ ÀÇÇØ ä¿öÁø´Ù. */ 4, /* ÆÄÀÏÀÇ À̸§ ±æÀÌ */ "test", /* ÆÄÀÏÀ̸§ */ S_IFREG | S_IRUGO, /* File mode – ÀÏ¹Ý ÆÄÀÏ, ¼ÒÀ¯ÀÚ, ±×·ì, ¸ðµç »ç¶÷µéÀÌ ÀÖÀ» ¼ö ÀÖ´Ù.*/ 1, /* ÆÄÀÏÀÌ Âü°íÇÏ´Â µð·ºÅ丮ÀÇ ¸µÅ© °³¼ö*/ 0, 0, /* uid, gid ·çÆ®¿¡°Ô ±ÇÇÑÀ» ÁØ´Ù.*/ 80, /* ls¿¡ ÀÇÇØ º¸°íµÇ´Â ÆÄÀÏÀÇ Å©±â. */ NULL, /* link, removeµî inode»ó¿¡ ÇàÇØÁö´Â ÇÔ¼ö- ¿ì¸®´Â ¾Æ¹«°Íµµ Áö¿øÇÏÁö ¾Ê´Â´Ù. */ procfile_read, /* Àбâ ÇÔ¼ö, ¹«¾ùÀΰ¡¸¦ ÀÐÀ¸·Á ÇÒ ¶§ È£ÃâµÇ´Â ÇÔ¼ö */ NULL /* ÆÛ¹Ì¼Ç, ¼ÒÀ¯±Ç µîÀ» ´Ù·ç´Â inode¸¦ ä¿ì´Â ÇÔ¼öÀÇ Æ÷ÀÎÅÍ */ }; /* Initialize the module - register the proc file */ int init_module() { /* proc_register[_dynamic]ÀÌ ¼º°øÇÏ¸é ¼º°ø, ¾Æ´Ï¸é ½ÇÆÐ */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0) /* ±¸Á¶Ã¼¿¡¼ ±× °ªÀÌ 0À̸é 2.2¹öÀü¿¡¼´Â proc_register()ÇÔ¼ö°¡ inode¹øÈ£¸¦ µ¿ÀûÀ¸·Î ÇÒ´çÇÑ´Ù. * proc_register_dynamic()ÇÔ¼ö°¡ ÇÊ¿ä¾ø°Ô µÈ´Ù. */ return proc_register(&proc_root, &Our_Proc_File); #else return proc_register_dynamic(&proc_root, &Our_Proc_File); #endif /* proc_root´Â procÆÄÀÏ ½Ã½ºÅÛÀÇ ·çÆ®(/proc) * ¿ì¸®°¡ ÆÄÀÏÀ» À§Ä¡½ÃŰ±â ¿øÇÏ´Â À§Ä¡´Ù. */ } /* Cleanup - unregister our file from /proc */ void cleanup_module() { proc_unregister(&proc_root, Our_Proc_File.low_ino); } 7.1 Է /proc ¶ݱ 츮 Ŀ κ ˾ Ҵ. 츮 ġ ̹ mknod ϰų /proc ִ. ̰ Ŀ Ͽ 츮 ΰ ֵ ش. ݴ 찡 Ұϴٴ ȴ. 켱 Ŀ Էϴ /proc Ͽ Ѵ.
/proc ý Ŀ μ ¸ ֵ ̱ , Է¿ Ư . proc_dir_entry ü Լ Է Լ Ͱ . /procϿ ⸦ ϴ , ǥ ý Ŀ ʿ䰡 ִ.
ý ǥ Ŀ ִ. ý inode file operation ٷ Լ ־ ϱ Լ ִ inode_operations ü , װ file_operationsü ϴ ϰ ִ. , /proc װͿ ϱ inode_operations ü Ѵ. 츮 module_input(), module_output()Լ ϴ file_operation ü ϴ inode_operations ü, ̰ 츮 ϴ Ŀ̴.
б ⸦ ǥ Ŀο ݴ ִٴ . б Լ , ݴ Լ Է ȴ. ϱ ̱ ̴. --- μ Ŀο д´ٸ Ŀ װ ־ Ѵ. μ Ŀο 𰡸 ٸ Ŀ װ Է 鿩 Ѵ.
ִ ٸ module_permissions() Լ. μ /proc Ͽ 𰡸 Ϸ , Լ ȣ ȴ. Լ ΰ ΰ ϰ ȴ. ̰ uid (operation) ü ٰ , ( ̰ Ǵ μ ִ ü Ϳ ã ִ ͵) ̰ μ ϴ ΰ, ۾ ð, Է Ҵ° , ϴ Ϳ ٰ ִ.
ŰĿ( ٸ μ Ͽ ) put_user() ũο get_user()ũθ ϴ Ʈ ̱ ̴. ü ũ ġ , Ʈ ġ ִ. Ʈ ִ ʿ䰡 ִ. Ŀ ϳ Ʈ ְ, μ鸶 ϳ Ʈ Ѵ.
μ ڽ Ʈ ϴ. Ϲ α μμ Ʈ Ű ʾƵ ȴ. Ŀ ۼ , Ϲ ýۿ ڵ ٷ Ŀ Ʈ ϱ⸦ Ѵ. Ӹ μ Ŀ ̿ Ǿ , Ŀ Լ μ Ʈ ִ ް ȴ. put_user() ũο get_user() ũΰ ش.
Example 6-1. procfs.c
/* procfs.c - create a "file" in /proc, which allows both input and output. */ #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> /* Specifically, a module */ /* Necessary because we use proc fs */ #include <linux/proc_fs.h> /* In 2.2.3 /usr/include/linux/version.h includes a * macro for this, but 2.0.35 doesn't - so I add it * here if necessary. */ #ifndef KERNEL_VERSION#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) #include <asm/uaccess.h> /* for get_user and put_user */ #endif /* The module's file functions ********************** */ /* Here we keep the last message received, to prove * that we can process our input */ #define MESSAGE_LENGTH 80 static char Message[MESSAGE_LENGTH]; /* Since we use the file operations struct, we can't * use the special proc output provisions - we have to * use a standard read function, which is this function */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static ssize_t module_output( struct file *file, /* The file read */ char *buf, /* The buffer to put data to (in the * user segment) */ size_t len, /* The length of the buffer */ loff_t *offset) /* Offset in the file - ignore */ #else static int module_output( struct inode *inode, /* The inode read */ struct file *file, /* The file read */ char *buf, /* The buffer to put data to (in the * user segment) */ int len) /* The length of the buffer */ #endif { static int finished = 0; int i; char message[MESSAGE_LENGTH+30]; /* We return 0 to indicate end of file, that we have * no more information. Otherwise, processes will * continue to read from us in an endless loop. */ if (finished) { finished = 0; return 0; } /* We use put_user to copy the string from the kernel's * memory segment to the memory segment of the process * that called us. get_user, BTW, is * used for the reverse. */ sprintf(message, "Last input:%s", Message); for(i=0; i<len && message[i]; i++) put_user(message[i], buf+i); /* Notice, we assume here that the size of the message * is below len, or it will be received cut. In a real * life situation, if the size of the message is less * than len then we'd return len and on the second call * start filling the buffer with the len+1'th byte of * the message. */ finished = 1; return i; /* Return the number of bytes "read" */ } /* This function receives input from the user when the * user writes to the /proc file. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static ssize_t module_input( struct file *file, /* The file itself */ const char *buf, /* The buffer with input */ size_t length, /* The buffer's length */ loff_t *offset) /* offset to file - ignore */ #else static int module_input( struct inode *inode, /* The file's inode */ struct file *file, /* The file itself */ const char *buf, /* The buffer with the input */ int length) /* The buffer's length */ #endif { int i; /* Put the input into Message, where module_output * will later be able to use it */ for(i=0; i<MESSAGE_LENGTH-1 && i<length; i++) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) get_user(Message[i], buf+i); /* In version 2.2 the semantics of get_user changed, * it not longer returns a character, but expects a * variable to fill up as its first argument and a * user segment pointer to fill it from as the its * second. * * The reason for this change is that the version 2.2 * get_user can also read an short or an int. The way * it knows the type of the variable it should read * is by using sizeof, and for that it needs the * variable itself. */ #else Message[i] = get_user(buf+i);#endif Message[i] = '\0'; /* we want a standard, zero * terminated string */ /* We need to return the number of input characters * used */ return i; } /* This function decides whether to allow an operation * (return zero) or not allow it (return a non-zero * which indicates why it is not allowed). * * The operation can be one of the following values: * 0 - Execute (run the "file" - meaningless in our case) * 2 - Write (input to the kernel module) * 4 - Read (output from the kernel module) * * This is the real function that checks file * permissions. The permissions returned by ls -l are * for referece only, and can be overridden here. */ static int module_permission(struct inode *inode, int op) { /* We allow everybody to read from our module, but * only root (uid 0) may write to it */ if (op == 4 || (op == 2 && current->euid == 0)) return 0; /* If it's anything else, access is denied */ return -EACCES; } /* The file is opened - we don't really care about * that, but it does mean we need to increment the * module's reference count. */ int module_open(struct inode *inode, struct file *file) { MOD_INC_USE_COUNT; return 0; } /* The file is closed - again, interesting only because * of the reference count. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) int module_close(struct inode *inode, struct file *file) #else void module_close(struct inode *inode, struct file *file) #endif { MOD_DEC_USE_COUNT; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) return 0; /* success */ #endif } /* Structures to register as the /proc file, with * pointers to all the relevant functions. */ /* File operations for our proc file. This is where we * place pointers to all the functions called when * somebody tries to do something to our file. NULL * means we don't want to deal with something. */ static struct file_operations File_Ops_4_Our_Proc_File = { NULL, /* lseek */ module_output, /* "read" from the file */ module_input, /* "write" to the file */ NULL, /* readdir */ NULL, /* select */ NULL, /* ioctl */ NULL, /* mmap */ module_open, /* Somebody opened the file */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) NULL, /* flush, added here in version 2.2 */ #endif module_close, /* Somebody closed the file */ /* etc. etc. etc. (they are all given in * /usr/include/linux/fs.h). Since we don't put * anything here, the system will keep the default * data, which in Unix is zeros (NULLs when taken as * pointers). */ }; /* Inode operations for our proc file. We need it so * we'll have some place to specify the file operations * structure we want to use, and the function we use for * permissions. It's also possible to specify functions * to be called for anything else which could be done to * an inode (although we don't bother, we just put * NULL). */ static struct inode_operations Inode_Ops_4_Our_Proc_File = { &File_Ops_4_Our_Proc_File, NULL, /* create */ NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ module_permission /* check for permissions */ };/* Directory entry */ static struct proc_dir_entry Our_Proc_File = { 0, /* Inode number - ignore, it will be filled by * proc_register[_dynamic] */ 7, /* Length of the file name */ "rw_test", /* The file name */ S_IFREG | S_IRUGO | S_IWUSR, /* File mode - this is a regular file which * can be read by its owner, its group, and everybody * else. Also, its owner can write to it. * * Actually, this field is just for reference, it's * module_permission that does the actual check. It * could use this field, but in our implementation it * doesn't, for simplicity. */ 1, /* Number of links (directories where the * file is referenced) */ 0, 0, /* The uid and gid for the file - * we give it to root */ 80, /* The size of the file reported by ls. */ &Inode_Ops_4_Our_Proc_File, /* A pointer to the inode structure for * the file, if we need it. In our case we * do, because we need a write function. */ NULL /* The read function for the file. Irrelevant, * because we put it in the inode structure above */ }; /* Module initialization and cleanup ******************* */ /* Initialize the module - register the proc file */ int init_module() { /* Success if proc_register[_dynamic] is a success, * failure otherwise */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* In version 2.2, proc_register assign a dynamic * inode number automatically if it is zero in the * structure , so there's no more need for * proc_register_dynamic */ return proc_register(&proc_root, &Our_Proc_File); #else return proc_register_dynamic(&proc_root, &Our_Proc_File); #endif } /* Cleanup - unregister our file from /proc */ void cleanup_module() { proc_unregister(&proc_root, Our_Proc_File.low_ino); } 8.1 Talking to Device Files (writes and IOCTLs) ¶̽ ġ Ÿ ֵȴ. κ ġ ȴ. Ŀ ȿ ̽ ̹ μκ ̽ ƿDz Ư Ŀ ʿϴ. Ͽ ⸦ ϴ ó, ƿDz ̽ ϴ ȴ. ̰ device_write()Լ ȴ.
̰ ƴϴ. ø Ʈ ִٰ غ. ( ̶ ص CPU ø Ʈ ȴ. ʹ ġ ). ؾ ̽ ̿ ( ɾ Ȥ ȭ ) ϴ ̰, κ (ɿ , Ȥ ȭ ) о ̴. ӵ ΰ? ´.
н Input Output Control ioctl()̶ Ư Լ ȣϴ ȴ. ̹̽ ڽŸ ioctl ְ, װ б⸦ ioctl Ŀǵ(μκ Ŀη )̰ų ⸦ ioctl Ŀǵ(μ ), Ȥ , Ȥ ƴϴ. ioctlԼ ڸ . شǴ ̽ ũ, ioctlȣ, Ÿδ ɽ ִ [2]
ioctl ȣ ġ ֹȣ, ioctl Ÿ(б/), Ŀǵ, Ÿ . ioctl ȣ Ͽ ִ _IO, _IOR, _IOW or _IOWR(̵ ŸԿ ) ũο . ioctl ϴ α( ioctl )̳, Ŀ ( ioctl ϱ ) ο ԵǾ Ѵ. , chardev.h ̰ װ ϴ α ioctl.c̴.
Ŀ ioctl ϱ Ѵٸ, ioctl Ҵϴ . 쿬 ٸ ioctl Ŀǵ带 ް ǰų, ٸ ioctlĿǵ带 Ƶ װ ߸ƴٴ ˰ ȴ. ؼ Ŀμҽ Documentation/ioctl-number.txt ϱ ٶ.
Example 7-1. chardev.c
/* chardev.c - Create an input/output character device */ #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> /* Specifically, a module */ /* Deal with CONFIG_MODVERSIONS */ #if CONFIG_MODVERSIONS==1 #define MODVERSIONS #include <linux/modversions.h> #endif /* For character devices */ /* The character device definitions are here */ #include <linux/fs.h> /* A wrapper which does next to nothing at * at present, but may help for compatibility * with future versions of Linux */ #include <linux/wrapper.h> /* Our own ioctl numbers */ #include "chardev.h" /* In 2.2.3 /usr/include/linux/version.h includes a * macro for this, but 2.0.35 doesn't - so I add it * here if necessary. */ #ifndef KERNEL_VERSION #define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) #include <asm/uaccess.h> /* for get_user and put_user */ #endif #define SUCCESS 0 /* Device Declarations ******************************** */ /* The name for our device, as it will appear in * /proc/devices */ #define DEVICE_NAME "char_dev" /* The maximum length of the message for the device */ #define BUF_LEN 80 /* Is the device open right now? Used to prevent * concurent access into the same device */ static int Device_Open = 0; /* The message the device will give when asked */ static char Message[BUF_LEN]; /* How far did the process reading the message get? * Useful if the message is larger than the size of the * buffer we get to fill in device_read. */ static char *Message_Ptr; /* This function is called whenever a process attempts * to open the device file */ static int device_open(struct inode *inode, struct file *file) { #ifdef DEBUG printk ("device_open(%p)\n", file); #endif /* We don't want to talk to two processes at the * same time */ if (Device_Open) return -EBUSY; /* If this was a process, we would have had to be * more careful here, because one process might have * checked Device_Open right before the other one * tried to increment it. However, we're in the * kernel, so we're protected against context switches. * * This is NOT the right attitude to take, because we * might be running on an SMP box, but we'll deal with * SMP in a later chapter. */ Device_Open++; /* Initialize the message */ Message_Ptr = Message; MOD_INC_USE_COUNT; return SUCCESS; } /* This function is called when a process closes the * device file. It doesn't have a return value because * it cannot fail. Regardless of what else happens, you * should always be able to close a device (in 2.0, a 2.2 * device file could be impossible to close). */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static int device_release(struct inode *inode, struct file *file) #else static void device_release(struct inode *inode, struct file *file) #endif { #ifdef DEBUG printk ("device_release(%p,%p)\n", inode, file); #endif /* We're now ready for our next caller */ Device_Open --; MOD_DEC_USE_COUNT; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) return 0; #endif } /* This function is called whenever a process which * has already opened the device file attempts to * read from it. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static ssize_t device_read( struct file *file, char *buffer, /* The buffer to fill with the data */ size_t length, /* The length of the buffer */ loff_t *offset) /* offset to the file */ #else static int device_read( struct inode *inode, struct file *file, char *buffer, /* The buffer to fill with the data */ int length) /* The length of the buffer * (mustn't write beyond that!) */ #endif { /* Number of bytes actually written to the buffer */ int bytes_read = 0; #ifdef DEBUG printk("device_read(%p,%p,%d)\n", file, buffer, length); #endif /* If we're at the end of the message, return 0 * (which signifies end of file) */ if (*Message_Ptr == 0) return 0; /* Actually put the data into the buffer */ while (length && *Message_Ptr) { /* Because the buffer is in the user data segment, * not the kernel data segment, assignment wouldn't * work. Instead, we have to use put_user which * copies data from the kernel data segment to the * user data segment. */ put_user(*(Message_Ptr++), buffer++); length --; bytes_read ++; } #ifdef DEBUG printk ("Read %d bytes, %d left\n", bytes_read, length); #endif /* Read functions are supposed to return the number * of bytes actually inserted into the buffer */ return bytes_read; } /* This function is called when somebody tries to * write into our device file. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) static ssize_t device_write(struct file *file, const char *buffer, size_t length, loff_t *offset) #else static int device_write(struct inode *inode, struct file *file, const char *buffer, int length) #endif { int i; #ifdef DEBUG printk ("device_write(%p,%s,%d)", file, buffer, length); #endif for(i=0; i<length && i<BUF_LEN; i++) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) get_user(Message[i], buffer+i); #else Message[i] = get_user(buffer+i); #endif Message_Ptr = Message; /* Again, return the number of input characters used */ return i; } /* This function is called whenever a process tries to * do an ioctl on our device file. We get two extra * parameters (additional to the inode and file * structures, which all device functions get): the number * of the ioctl called and the parameter given to the * ioctl function. * * If the ioctl is write or read/write (meaning output * is returned to the calling process), the ioctl call * returns the output of this function. */ int device_ioctl( struct inode *inode, struct file *file, unsigned int ioctl_num,/* The number of the ioctl */ unsigned long ioctl_param) /* The parameter to it */ { int i; char *temp; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) char ch; #endif /* Switch according to the ioctl called */ switch (ioctl_num) { case IOCTL_SET_MSG: /* Receive a pointer to a message (in user space) * and set that to be the device's message. */ /* Get the parameter given to ioctl by the process */ temp = (char *) ioctl_param; /* Find the length of the message */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) get_user(ch, temp); for (i=0; ch && i<BUF_LEN; i++, temp++) get_user(ch, temp); #else for (i=0; get_user(temp) && i<BUF_LEN; i++, temp++) ; #endif /* Don't reinvent the wheel - call device_write */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) device_write(file, (char *) ioctl_param, i, 0); #else device_write(inode, file, (char *) ioctl_param, i); #endif break; case IOCTL_GET_MSG: /* Give the current message to the calling * process - the parameter we got is a pointer, * fill it. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) i = device_read(file, (char *) ioctl_param, 99, 0); #else i = device_read(inode, file, (char *) ioctl_param, 99); #endif /* Warning - we assume here the buffer length is * 100. If it's less than that we might overflow * the buffer, causing the process to core dump. * * The reason we only allow up to 99 characters is * that the NULL which terminates the string also * needs room. */ /* Put a zero at the end of the buffer, so it * will be properly terminated */ put_user('\0', (char *) ioctl_param+i); break; case IOCTL_GET_NTH_BYTE: /* This ioctl is both input (ioctl_param) and * output (the return value of this function) */ return Message[ioctl_param]; break; } return SUCCESS; } /* Module Declarations *************************** */ /* This structure will hold the functions to be called * when a process does something to the device we * created. Since a pointer to this structure is kept in * the devices table, it can't be local to * init_module. NULL is for unimplemented functions. */ struct file_operations Fops = { NULL, /* seek */ device_read, device_write, NULL, /* readdir */ NULL, /* select */ device_ioctl, /* ioctl */ NULL, /* mmap */ device_open, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) NULL, /* flush */ #endif device_release /* a.k.a. close */ }; /* Initialize the module - Register the character device */ int init_module() { int ret_val; /* Register the character device (atleast try) */ ret_val = module_register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops); /* Negative values signify an error */ if (ret_val < 0) { printk ("%s failed with %d\n", "Sorry, registering the character device ", ret_val); return ret_val; } printk ("%s The major device number is %d.\n", "Registeration is a success", MAJOR_NUM); printk ("If you want to talk to the device driver,\n"); printk ("you'll have to create a device file. \n"); printk ("We suggest you use:\n"); printk ("mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM); printk ("The device file name is important, because\n"); printk ("the ioctl program assumes that's the\n"); printk ("file you'll use.\n"); return 0; } /* Cleanup - unregister the appropriate file from /proc */ void cleanup_module() { int ret; /* Unregister the device */ ret = module_unregister_chrdev(MAJOR_NUM, DEVICE_NAME); /* If there's an error, report it */ if (ret < 0) printk("Error in module_unregister_chrdev: %d\n", ret); } Example 7-2. chardev.h
/* chardev.h - the header file with the ioctl definitions. * * The declarations here have to be in a header file, because * they need to be known both to the kernel module * (in chardev.c) and the process calling ioctl (ioctl.c) */ #ifndef CHARDEV_H #define CHARDEV_H #include <linux/ioctl.h> /* The major device number. We can't rely on dynamic * registration any more, because ioctls need to know * it. */ #define MAJOR_NUM 100 /* Set the message of the device driver */ #define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *) /* _IOR means that we're creating an ioctl command * number for passing information from a user process * to the kernel module. * * The first arguments, MAJOR_NUM, is the major device * number we're using. * * The second argument is the number of the command * (there could be several with different meanings). * * The third argument is the type we want to get from * the process to the kernel. */ /* Get the message of the device driver */ #define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *) /* This IOCTL is used for output, to get the message * of the device driver. However, we still need the * buffer to place the message in to be input, * as it is allocated by the process. */ /* Get the n'th byte of the message */ #define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int) /* The IOCTL is used for both input and output. It * receives from the user a number, n, and returns * Message[n]. */ /* The name of the device file */ #define DEVICE_FILE_NAME "char_dev" #endif Example 7-3. ioctl.c
/* ioctl.c - the process to use ioctl's to control the kernel module * * Until now we could have used cat for input and output. But now * we need to do ioctl's, which require writing our own process. */ /* device specifics, such as ioctl numbers and the * major device file. */ #include "chardev.h" #include <fcntl.h> /* open */ #include <unistd.h> /* exit */ #include <sys/ioctl.h> /* ioctl */ /* Functions for the ioctl calls */ ioctl_set_msg(int file_desc, char *message) { int ret_val; ret_val = ioctl(file_desc, IOCTL_SET_MSG, message); if (ret_val < 0) { printf ("ioctl_set_msg failed:%d\n", ret_val); exit(-1); } } ioctl_get_msg(int file_desc) { int ret_val; char message[100]; /* Warning - this is dangerous because we don't tell * the kernel how far it's allowed to write, so it * might overflow the buffer. In a real production * program, we would have used two ioctls - one to tell * the kernel the buffer length and another to give * it the buffer to fill */ ret_val = ioctl(file_desc, IOCTL_GET_MSG, message); if (ret_val < 0) { printf ("ioctl_get_msg failed:%d\n", ret_val); exit(-1); } printf("get_msg message:%s\n", message); } ioctl_get_nth_byte(int file_desc) { int i; char c; printf("get_nth_byte message:"); i = 0; while (c != 0) { c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++); if (c < 0) { printf( "ioctl_get_nth_byte failed at the %d'th byte:\n", i); exit(-1); } putchar(c); } putchar('\n'); } /* Main - Call the ioctl functions */ main() { int file_desc, ret_val; char *msg = "Message passed by ioctl\n"; file_desc = open(DEVICE_FILE_NAME, 0); if (file_desc < 0) { printf ("Can't open device file: %s\n", DEVICE_FILE_NAME); exit(-1); } ioctl_get_nth_byte(file_desc); ioctl_get_msg(file_desc); ioctl_set_msg(file_desc, msg); close(file_desc); } 9.1 ý ¶ݱ 츮 /procϰ ̽ 鷯 ϱ ǵ Ŀ Ŀ ̴. ġ ̹ ۼϴ Ͱ Ŀ α ϴ Ϳ ݱ ͵ . Ư ý ϴ Ͱ Ư ϴ°? ͵ ̴.
κ Ŀ α ؾ κ̴. Ʒ open() ý . ̰ ϵ ų, α ϰų, ǻ ˴ٿ Ű ϰ ǹѴ. ġ Ѵ. ϵ ջ ʾҴ. ϵ ʱ ؼ insmod ϰų rmmod ϱ sync Ű.
/proc̳ ̽ . . μ ϴ ϳ μ Ŀη Ŀ ý ̴. ų ο μ ϰų ߰ 䱸ϴ , μ Ŀο 䱸 , Ŀ ȴ. ϴ Ŀ ̱⸦ Ѵٸ ٷ Ⱑ ۾ؾ κ̴. ý α Ǵ ˱ Ѵٸ, strace <arguments> Ű.
Ϲ μ Ŀο . μ Ŀ Ѵٰų Ŀ Լ ȣ . ϵ CPU ϸ ̰ ȣ θ .
ý Ϲ Ģ ܴ. μ ä, Ŀ ȿ ̸ ִ ġ ϴ νƮ() ȣϴ ٷ ̴̰. CPU ̰ ͷƮ 0x80 ǹѴ. ϴ ġ ̵ϸ ϵ 尡 ƴϰ ü Ŀη νѴ. μ ϴ ִ.
Ŀ ġ μ ý ݷ ϴ. ġ ν ý ȣ ȮѴ. װ Ŀο μ 䱸ߴ ˷ش. , ý ̺(sys_chall_tabel) Ŀ Լ ȣϱ ּҸ ˾ƺ Ѵ. Լ ȣϸ, Լ ý üũ ϰ, μ ƿ´. ڵ带 ⸦ Ѵٸ, /$<$architecture$>$/kernel/entry.S ENTRY(system_call) ٶ. /
Ư ý ۵ ٲٰ ʹٸ, ؾ Լ 츮 ϴ ̴.( εǴ 츮 ڵ带 ְ Լ ȣѴ.) sys_call_table 츮 Լ ͷ üϸ ȴ. ߿ 츮 ϰ ý Ҿ · ⸦ ٶ ʴ ٸ, cleanup_module() · sys_call_table ʿϴ.
ҽڵ Ŀ . Ư ڿ ̡ ڰ prink() Ѵ. ̰ open ý ڵ带 츮 Լ our_sys_open() ٲ Ѵ. Լ μ uid ˻غ 츮 ϰ ϴ uid ġ , printk() ȣ ̸ Ѵ. ڸ open()Լ ȣѴ.
init_module() Լ sys_call_table ´ ġ üϰ Լ Ѵ. cleanup_module() Լ · . Ŀ ÿ ý ɼ 빮 ϴ. A B Ŀ ִٰ . A open()Լ A_open B B_open̶ . A Ŀο , ý A_open ٲ, sys_open ǵ ̴. B Ǹ, ý B_open ٲٰ, װ Ѿ ý A_open ̴.
B . --- װ ܼ ý A_open ǵ ̴. A ŵǰ B ŵǸ, ý ٿ ̴. A ϴ ý Ű B ´. B , B ý A_open̶ ̴. ̰ ̻ ʴ´. , ý 츮 ۼ Լ , ȭ ° Ȯν ذ δ.(B ƹ͵ ν) ̰ Ѵ. A , ý B_open ٲ ̻ A_open ̴. ̴ ŵDZ sys_open() ñ ̴. B_open ̻ ʴ A_open ȣϷ ϰ B ¿ ý ٿ ̴.
̸ ذ ִ. ù ý sys_open() ϴ ̴. ϰԵ sys_open ̻ Ŀ ý ̺ /proc/ksyms Ϻΰ ƴϾ 츮 . ٸ ۷ ī ̶ root rmmod ϴ ̴. ̰ ǰ ̳ δ ϴ. --- ⼭ ̰ ʴ .
Example 8-1. procfs.c
/* syscall.c * * System call "stealing" sample. */ /* Copyright (C) 2001 by Peter Jay Salzman */ /* The necessary header files */ /* Standard in kernel modules */ #include <linux/kernel.h> /* We're doing kernel work */ #include <linux/module.h> /* Specifically, a module */ /* Deal with CONFIG_MODVERSIONS */ #if CONFIG_MODVERSIONS==1 #define MODVERSIONS #include <linux/modversions.h> #endif #include <sys/syscall.h> /* The list of system calls */ /* For the current (process) structure, we need * this to know who the current user is. */ #include <linux/sched.h> /* In 2.2.3 /usr/include/linux/version.h includes a * macro for this, but 2.0.35 doesn't - so I add it * here if necessary. */ #ifndef KERNEL_VERSION #define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) #include <asm/uaccess.h> #endif /* The system call table (a table of functions). We * just define this as external, and the kernel will * fill it up for us when we are insmod'ed */ extern void *sys_call_table[]; /* UID we want to spy on - will be filled from the * command line */ int uid; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) MODULE_PARM(uid, "i"); #endif /* A pointer to the original system call. The reason * we keep this, rather than call the original function * (sys_open), is because somebody else might have * replaced the system call before us. Note that this * is not 100% safe, because if another module * replaced sys_open before us, then when we're inserted * we'll call the function in that module - and it * might be removed before we are. * * Another reason for this is that we can't get sys_open. * It's a static variable, so it is not exported. */ asmlinkage int (*original_call)(const char *, int, int); /* For some reason, in 2.2.3 current->uid gave me * zero, not the real user ID. I tried to find what went * wrong, but I couldn't do it in a short time, and * I'm lazy - so I'll just use the system call to get the * uid, the way a process would. * * For some reason, after I recompiled the kernel this * problem went away. */ asmlinkage int (*getuid_call)(); /* The function we'll replace sys_open (the function * called when you call the open system call) with. To * find the exact prototype, with the number and type * of arguments, we find the original function first * (it's at fs/open.c). * * In theory, this means that we're tied to the * current version of the kernel. In practice, the * system calls almost never change (it would wreck havoc * and require programs to be recompiled, since the system * calls are the interface between the kernel and the * processes). */ asmlinkage int our_sys_open(const char *filename, int flags, int mode) { int i = 0; char ch; /* Check if this is the user we're spying on */ if (uid == getuid_call()) { /* getuid_call is the getuid system call, * which gives the uid of the user who * ran the process which called the system * call we got */ /* Report the file, if relevant */ printk("Opened file by %d: ", uid); do { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) get_user(ch, filename+i); #else ch = get_user(filename+i); #endif i++; printk("%c", ch); } while (ch != 0); printk("\n"); } /* Call the original sys_open - otherwise, we lose * the ability to open files */ return original_call(filename, flags, mode); } /* Initialize the module - replace the system call */ int init_module() { /* Warning - too late for it now, but maybe for * next time... */ printk("I'm dangerous. I hope you did a "); printk("sync before you insmod'ed me.\n"); printk("My counterpart, cleanup_module(), is even"); printk("more dangerous. If\n"); printk("you value your file system, it will "); printk("be \"sync; rmmod\" \n"); printk("when you remove this module.\n"); /* Keep a pointer to the original function in * original_call, and then replace the system call * in the system call table with our_sys_open */ original_call = sys_call_table[__NR_open]; sys_call_table[__NR_open] = our_sys_open; /* To get the address of the function for system * call foo, go to sys_call_table[__NR_foo]. */ printk("Spying on UID:%d\n", uid); /* Get the system call for getuid */ getuid_call = sys_call_table[__NR_getuid]; return 0; } /* Cleanup - unregister the appropriate file from /proc */ void cleanup_module() { /* Return the system call back to normal */ if (sys_call_table[__NR_open] != our_sys_open) { printk("Somebody else also played with the "); printk("open system call\n"); printk("The system may be left in "); printk("an unstable state.\n"); } sys_call_table[__NR_open] = original_call; } 10.1.1 printk() üϱ ¶ ش ϸ ϰڴ°? ̰ ٸ Ѵٸ , ٺ, ! ϸ ȴ. Ŀ ̰ μ ٸ ؾ Ѵ. μ 䱸 μ ؾ Ѵ. μ Ŀο ų ִ. (̰ ϳ CPU μ Ǵ ̴.)
Ŀ ̷ . /proc/sleep ϳ μ ؼ ִ. ̹ ִٸ, Ŀ module_interruptible_sleep_on()Լ ȣѴ. Լ ۾ (task – ۾ μ ȣǴ ý ݿ Ŀ Ѵ.) TASK_INTERRUPTIBLE Ѵ. ̰ ۾ Ͽ ִ ۾ ť Ե ǹѴ. Լ ٷ ȣ ٸ μ ȯ(context switch) ϰ ϸ, μ CPU Ѵ.
μ Ͽ ۾ ġ, ݰ, module_close()Լ ȣѴ. Լ ť ִ μ . ( μ Ư Ŀ ) μ ٸ ۾ Ѵ. ̶ ٷ μ , ٸ μ CPU ѱ Ѵ. ᱹ μ ϳ ٷ CPU 㰡 ް ȴ. ̷ ۾ module_interruptible_sleep_on()Լ ȣ ȴ. μ ϰ, ش Ϸ ٸ μ 뺸 Ѵ. ٸ μ CPU ϸ, ش Ȯϰ ٽ · ư.
̷ӰԵ, module_close()Լ Ͽ ϱ ٸ μ Ϳ Ѵ. Ctrl+c (SIGINT) ñ׳ε μ ִ.[3] ̷ 츮 –EINTR ﰢ ´. Ϸʷ μ ڰ װ ִٴ ߿ϴ.
ϳ ִ. · ٲ ġ ʴ μ ִ. ϴ ﰢ ⸦ ϰų ۾ ٰ ϱ Ѵ. μ O_NONBLOCK ÷ Ѵ. ϱ -EAGAIN ̶ ڵ带 ν Ŀ Ѵ. 忡 ִ cat_noblock α O_NONBLOCK ÷ ü, ̴.
Example 9-1. sleep.c
11.1 printK()üϱ ¶ Section 1.2.1.2 X Ŀ α Բ ߴ. Ŀ ϴ ´ ̴. 쿡 ϱ tty ֱ⸦ ̴.
½ũ tty ü ϱ , ½ũ ϴ current ũθ ؼ ذ ִ. ڿ ִ Լ ã ttyü 鿩ٺ, װ ̿ tty ڿ Ѵ.
Example 10-1. print_string.c
12.1 ۾ ٸϱ ¶츮 Ρ Ѵ. ̴ ̴ Ư ð Ѵ. μ Ѵٸ, crontabϿ Էϴ ذ ִ. ̷ Ŀ ؾ Ѵٸ, ִ. ù° ʿ , ý ݿ μ crontab Ͽ ִ ̴. ̰ ϰԵ ȿ̴. ̰ μ crontab Ű, ڵ带 д´. ̹ ϴ Ŀ ؼ ̴.
̷ ϴ , Ÿ̸ ͷƮ ȣǴ Լ ִ. ̷ ϴ ο ۾(task) , Լ ִ tq_struct ü ϴ ̴. queue_task() Լ tq_timer(̰ Ÿ ͷƮ ۾ Ʈ ִ.) Ҹ ۾ Ʈ ֱ Ѵ. Լ DZ ٶ , ̰ ȣ ð ͷƮ , ٽ tq_timer ִ´.
ϳ ִ. rmmod , ۷ īƮ ˵ȴ. 0 , module_cleanup() ȣȴ. Լ ŵȴ. timers task list ̷ ̻ ʴ Լ Ͱ ִ Ȯ ʴ´. ( ̰ ǻ ̴. ΰ ƾ ̴.) Ŀ Ÿ̸ ͷƮ Ұ, ۾Ʈ ִ Լ ȣߴ. Լ ̻ װ ʴ´. κ , Լ ϴ ̻ , ̴. ġ ο ڵ尡 ڸҴٸ, ´ . 츮 ۾Ʈ(task list) ۾ ϴ .
cleanup_module()Լ ڵ带 (̰ void Ѵ.), ذå ƹ͵ ϰ ϴ ̴. sleep_on() Ȥ module_sleep_on() ȣ rmmodμ Ѵ. , Ÿ̸ ͷƮ ȣ Լ ν ڽ ̻ ߰ ˸. Ÿ ͷƮ rmmodμ , 츮 Լ ̻ ť ʰ, ϴµ ϰ ȴ.
Example 11-1. sched.c
13.1.1 ͷƮ ڵ鷯 ¶ֽ ǻ ϰ, μ û Ϳ , Ư ٷų ioctl() ϰų, ý ν 츮 ݱ Ŀο ۾ ߴ. Ŀ μ û ϴ ƴϴ. ߿ ٸ Ŀ ۾ ϵ ǻ ϴ ̴.
CPU ϵ ǻ ϴ ִ. ù CPU ϵ , CPU ϵ ʿϴٰ ϴ ̴. ͷƮ Ҹ ° CPU ƴ ϵ Ǹ ٷ ̱ ƴ. ϵ ġ ſ RAM ְ, ʴ ٸ װ .
н ϵ ͷƮ IRQ θ. short long Ÿ IRQ Ѵ. short IRQ ſ ª ð ɸ ̰, ð ٸ ͷƮ ó ʰ ϵȴ. long IRQ ð ɸ, ð ٸ ͷƮ ִ. ( ġκ .) ɼ Ѵٸ, ͷƮ ڵ鷯 long Ÿ ϴ .
CPU ͷƮ , װ ϴ ߰( ߿ ͷƮ ó ϰ ʴٸ, , ߿ ͷƮ ó ó װ óѴ.), ÿ Ư Ķ ͷƮ ڵ鷯 ȣѴ. ̰ ý ¿ ִ ϱ , ͵ ͷƮ ڵ鷯 ο 㰡 ǹѴ. ذå ﰢ óǾ ϴ ͷƮ ڵ鷯 Ϲ ϵ ΰ аų ϵ ϰ ο óϴ ٷ Ű ̴.(츮 ̰ bottom half θ. Ŀ bottom half ȣ ̶ . -- , Ŀ 㰡 ִ.
IRQ , ͷƮ ڵ鷯 ã request_irq()Լ ȣν ȴ. Լ IRQȣ, Լ ̸, ÷ , /proc/interupts ̸, ͷƮ ڵ鷯 ڸ ´. , ÷״ ٸ ͷƮ ڵ鷯 IRQ ϰڴٴ ˸ SA_SHIRQ(, ϵ ̽ ȣ IRQ ϱ ), óž ˸ SA_INTERRUPT Ѵ. Ϸ ϰų, IRQ ٸ ڵ鷯 Լ Ѵ.
ͷƮ ڵ鷯 ηκ, 츮 ϵ ְ, bottom half ϱ , queue_task_irq()Լ mark_bh(BH_IMMEDIATE) Ѵ. 츮 ǥ queue_task()Լ 2.0 ϴ ͷƮ ٸ queue_task()Լ ߿ ֱ ̴. ʱ 32 迭 bottom half , ϳ(BH_IMMEDIATE) bottom half Ʈ Ҵ ̹ bottom half ũ Ʈ Ѵ.
13.1.2 ŰĿ Ű ¶ κ 迭 ȴ. ٸ ÷ ۵״ٸ, ۵ ̴. ٸ õ .
ڵ带 ۼϴµ ־. δ ǻͿ ۵ ϸ鼭 , ٸ δ Ŀ ̹ ϰ ִ Ϲ ̽ ̹, ۼϷ ϴ Ͱ ġ ʴ . ãƳ ذå Ű ͷƮ ΰ ۼϴ ̰, 켱 Ϲ Ű ͷƮ Ű ̴. static symbol ̹ Ŀ ο ֱ (Ư drivers/char/keyboard.c), װ . ڵ带 ϱ 120 ٸ ̳ .(sleep 120); ý ߿ϰ ٸ reboot.
ڵ ڽ IRQ1 ε Ű, IRQ1 Ű Ʒ Ű IRQ̴. ڵ尡 Ű ͷƮ , Ű ¸ о(inb(0x64) ϱ ̴.), ڵ带 ĵѴ. ڵ Ű忡 ϵǴ ̴. Ŀ ̰ ̶ Ǵڸ, got_char()Լ Ű, Լ Ű ڵ带 ְ(ĵ ڵ ù 7Ʈ), װ ȴ( 8° Ʈ 0̴), ȴ ( 1) ˷ش.
Example 12-1. intrpt.c
14.1 Ī ó(μ) ¶ϵ ϳ 忡 CPU ϳ ġϴ ̴. ٸ CPU ٸ ۾ ϰ ϴ (Ī ó) , ϰ (Ī ó)νᵵ ϴ. ȿ Ī ó ؼ ǻͰ ϴ ۾ Ư ʿϸ, Ϲ ü迡 ̴. ݸ鿡, Ī ó ϱ .
, ʾ ִ. Ī μ ȯ濡, CPU ϰ , ϳ CPU Ǵ ڵ ٸ CPU ϴ ų ִ. ִٰ Ȯ . Ǹ ʴ ̿ ٸ CPU ִ. ϰԵ, α ̷ .
μ α 쿡, μ Ϲ ϳ CPU DZ Ϲ ̷ ʴ´. ݸ鿡 Ŀ ٸ CPU Ǵ μ ȣ ִ.
ü Ŀ ϳ Ŵ spinlock() ̱ , 2.0.x ̰ ʾҴ. , ϳ CPU Ŀ , ý ݿ ٸ CPU Ŀ ûϸ, װ ó CPU ĥ , ٷ Ѵ. ̰ SMP ϰ ȿ ߷ȴ.
2.2.x CPU ÿ Ŀ ִ. ̰ ۼϴ ؾ ̴.
15.1 ¶ Ŀ ۼϱ , ſ Ǹ ־ ͵ ִ. Ѵٸ, ˷ֱ ٶ.
ǥ ̺귯 ϴ
ǥ ̺귯 . Ŀ ִ Լ /proc/ksyms ̴ Լ ̴.
ͷƮ Ұϰ ϴ
̰ ʿ䵵 ְ, . ٽ ͷƮ ϰ س ʴ ٸ, ýۿ ̰, ߸ ̴.
Ӹ ĵ ȿ ִ
̷ ͱ ؾ ʿ , Ȥó ؼ · Ѵ.
16.1.1 2.0 2.2 ȭ ¶ü Ŀ ȭ ȭϰ ִ ڴ. ȯ(Ȥ Emmanuel Papirakis changes )ϴ ߰ߴ. αӵ(߿ Ư å Ǻ ȯ 鿡 ͼ ) ؼ װ͵ ߴ.
2.2 ȯϱ⸦ ٶ ̵ ߰ ҽ
Richard Gooch Ʈ ã ִ.
asm/uaccess.h
put_user get_user ʿϸ, װ (#include)Ѿ Ѵ.
get_user
2.2 , get_user Ϳ Ŀ ִ Ѵ Ƽ شǴ ä. 츮 о̴ 2Ʈ Ȥ 4Ʈ ̸ , get_user ѹ 2Ʈ Ǵ 4Ʈ ֱ ̴.
file_operations
ü open Լ close Լ ̿ flush Լ ִ.
close in file_operations
2.2 , close Լ integer ȯϱ , so it's allowed to fail.
file_operations read, write
Լ鿡 Ǿ. װ͵ integer ssize_t ȯϸ, Ķ ϵ ٸ. inode ̻ ĶͰ ƴϸ, ݴ Ͽ ĶͰ Ǿ.
proc_register_dynamic
Լ ̻ ʴ´. , proc_register ȣϸ, ü inode ʵ忡 zero ִ´.
Signals
½ũ ñ׳ε ̻ 32Ʈ ƴϰ _NSIG_WORDS 迭̴.
queue_task_irq
½ũ ͷƮ ڵ鷯 ο ϰԲ ϴ 쿡 queue_task_irq queue_task ٶ.
Module Parameters
̻ Ķ μ ʾƵ ȴ. 2.2 MODULE_PARM Ἥ װ͵ Ÿ ֵ Ѵ. ̰ Ŀٶ ̴. ֳϸ, Ͽ digit(: 0 Ǵ 1) ϴ ڿ Ķ (, ȥ ʰ) ְ Ǿ ̴.
Symmetrical Multi-Processing
Ŀ ̻ Ŵ spinlock ȿ , ̴ Ŀθ SMP ˰ ־ ǹѴ.
|
Of all forms of caution, caution in love is the most fatal. |









̴. Ŀ2.0.35 ũΰ ٴ ˾Ƶ. Ŀ ϴ ۼϰ Ѵٸ, ؾ Ѵ.
![[http]](/imgs/http.png)