210 likes | 513 Views
Kernel Modules. Kernel Module. Pieces of code that can be loaded and unloaded into the kernel upon demand. Compiled as an independent program With appropriate flags to indicate that it is kernel code. Can be linked into the kernel by installation. Static/Dynamic loading
E N D
Kernel Module • Pieces of code that can be loaded and unloaded into the kernel upon demand. • Compiled as an independent program • With appropriate flags to indicate that it is kernel code. • Can be linked into the kernel by installation. • Static/Dynamic loading • “/proc/modules” or “lsmod” • Enhance the functionality of the kernel. • Example: • Device Drivers
Functions in Kernel Module • int init_module(void) • Invoked when the module is inserted to kernel • Return 0 if success. • void cleanup_module(void) • Invoked when the module is removed from kernel • printk() • /usr/src/linux-2.6.5-1.358/include/linux/kernel.h
#include <linux/kernel.h> #include <linux/module.h> int init_module(void) { printk(KERN_ALERT "Hello world.\n“); return 0; } void cleanup_module(void) { printk(KERN_ALERT "Goodbye world.\n"); } MODULE_LICENSE("GPL"); //GNU Public License v2 or later
Makefile ifneq ($(KERNELRELEASE),) obj-m += hello.o else KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules endif
insmod hello.ko • lsmod or “/proc/modules” • tail -f /var/log/messages • rmmod hello
Modules vs. Programs • Programs • main() • performs a single task from beginning to end • Run in user mode • Can call the libc functions • Modules • init_module & cleanup_module • registers itself in order to serve future requests • Run in kernel mode • Part of kernel program • Only can call the ones exported by the kernel. • include/linux • include/asm • Export all non-static symbols
Device Drivers • Character devices • one that can be accessed as a stream of bytes • Keyboard driver • Block devices • One that can be accessed as multiples of a block, usually 1KB per block • Disk driver
Device Drivers • To communicate with hardware • Each piece of hardware is represented by a file located in /dev • An application do some operations on that file • The kernel finds corresponding device driver for that file. • Each operation on that file will trigger a function in the device driver for that hardware • The file in /dev is not regular file • To find the device driver for that file • ls -l /dev/b* • Major number • Match with driver • Minor number • used by the driver to distinguish between the various hardware it controls • ls -l /dev/fd0u*
Device Drivers • Defines functions on the file operation • Read, write, open, close, etc • Register the device to kernel • creates a file in “/dev” using “mknod”
Device Drivers • Function mapping struct file_operations fops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release }; • vi /usr/src/linux-2.6.5-1.358/include/linux/fs.h • int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); • int unregister_chrdev(unsigned int major, const char *name);
#include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> #define DEVICE_NAME "chardev" #define BUF_LEN 80 static int Major; static char msg[BUF_LEN]; static char *msg_Ptr;
static int device_open(struct inode *inode, struct file *file) { static int counter = 0; sprintf(msg, "This device is opened %d times!\n", counter++); msg_Ptr = msg; try_module_get(THIS_MODULE); return 0; } static int device_release(struct inode *inode, struct file *file) { module_put(THIS_MODULE); return 0; }
We cannot remove the device driver while device file is opened by a process. • There's a counter which keeps track of how many processes are using your module • the 3rd column in lsmod • try_module_get(THIS_MODULE); • Increment the use count • module_put(THIS_MODULE); • Decrement the use count
static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t * offset) { int bytes_read = 0; if (*msg_Ptr == 0) return 0; while (length && *msg_Ptr) { put_user(*(msg_Ptr++), buffer++); length--; bytes_read++; } return bytes_read; }
put_user(char src, char* dst); • src is in kernel code segment • dst is in user code segment • get_user(char dst, char* src); • src is in user code segment • dst is in kernel code segment • A pointer into userspace should never be simply dereferenced • #include <asm/uaccess.h> • buffer is in user data segment • length: length of the buffer
static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off) { printk("<1>This operation isn't supported.\n"); return -EINVAL; }
static struct file_operations fops = { .read = device_read, .write = device_write, .open = device_open, .release = device_release };
int init_module(void) { Major = register_chrdev(0, DEVICE_NAME, &fops); if (Major < 0) { printk("Failed to register char device.\n"); return Major; } printk(KERN_ALERT "chardev is assigned to major number %d.\n", Major); return 0; } void cleanup_module(void) { int ret = unregister_chrdev(Major, DEVICE_NAME); if (ret < 0) printk("Error in unregister_chrdev: %d\n", ret); }
mknod • Create a file in /dev • And match the file with a device driver • mknod /dev/hello c Major 0 • This Major number is from the output of /var/log/messages
References • Linux Device Drivers, 2nd Edition • http://www.xml.com/ldd/chapter/book/index.html • The Linux Kernel Module Programming Guide • http://tldp.org/LDP/lkmpg/2.6/html/