350 likes | 591 Views
Il kernel di Linux. Riferimenti: http://www.ltn.lv/~guntis/unix/kernel_modules.ppt ( Dzintars Lepešs, University of Latvia) http://freeelectrons.com. Struttura dei sorgenti. arch Codice dipendente dall'architettura documentation Documentazione del kernel;
E N D
Il kernel di Linux Riferimenti: http://www.ltn.lv/~guntis/unix/kernel_modules.ppt(Dzintars Lepešs, University of Latvia) http://freeelectrons.com
Struttura dei sorgenti • arch Codice dipendente dall'architettura • documentation Documentazione del kernel; • drivers Tutti i device drivers • fs Filesystems • include File header del kernel • kernel Scheduler, … • lib Librerie • … • Makefile Makefile principale
Compilazione Configurazione kernel: • make xconfig • oppure make menuconfig • oppure make oldconfig Generano il file .config con le impostazioni scelte. Compilazione: make [opzioni]
Compilazione • make bzImage • crea il kernel compilato • make modules • compila i moduli • make modules_install • installa i moduli • make … -j 4 • compilazione parallela • make mrproper • cancellazione file intermedi
Implementazione • Vengono memorizzati come file oggetto in formato ELF; • Viene memorizzato l’indirizzo di tutti i simboli esportati (/proc/syms <2.6 /proc/kallsyms - 2.6); • Viene momorizzato l’uso e le dipendenze dei moduli usati (/proc/modules), caricati con insmod o modprobe. • Per ogni modulo vengono mantenuti: • Una struttura dati con le informazioni su di esso; • Una stringa identificativa; • L’implementazione.
Programs for linking and unlinking • insmod • Reads from the name of the module to be linked • Locates the file containing the module's object code • Computes the size of the memory area needed to store the module code, its name, andthe module object • Invokes the create_module( ) system call • Invokes the query_module( ) system call • Using the kernel symbol table, the module symbol tables, and the address returned bythe create_module( ) system call, relocates the object code included in the module'sfile. • Allocates a memory area in the User Mode address space and loadswith a copy ofthe module object • Invokes the init_module( ) system call, passing to it the address of the User Modememory area • Releases the User Mode memory area and terminates
Programs for linking and unlinking • lsmod reads /proc/modules • rmmod • From reads the name of the module to be unlinked. • Invokes the query_module( ) • Invokes the delete_module( ) system call, with the QM_REFS subcommand several times, to retrieve dependency information on the linked modules. • modprobe takes care of possible complications due to module dependencies, uses depmod program and /etc/modules.conf file
Device drivers • There are two major ways for a kernel module to talk to processes: • To use the proc file system (/proc directory) • Through device files (/dev directory) • Device driver sits between some hardware and the kernel I/O subsystem. Its purpose is to give the kernel a consistent interface to the type of hardware it "drives".
Driver a caratteri Accesso tramite flusso sequenziale di caratteri singoli Individuabili per il loro tipo di file c (ls -l): crwrw1 root uucp 4, 64 Feb 23 2004 /dev/ttyS0 crww1 jdoe tty 136, 1 Sep 13 06:51 /dev/pts/1 crw1 root root 13, 32 Feb 23 2004 /dev/input/mouse0 crwrwrw1 root root 1, 3 Feb 23 2004 /dev/null Esempio: tastiera, mouse, porta parallela, IrDA, porta Bluetooth, console, terminale...
Driver a blocchi Accesso casuale a blocchi di dati di dimensioni fisse. Individuabili per il loro tipo di file b (ls l): brwrw1 root disk 3, 1 Feb 23 2004 /dev/hda1 Esempi: hard disk e floppy disk, ram disk, dispositivi loop...
Numeri di device Ogni device ha due numeri associati: • major number • Associato ad ogni driver in maniera unica • minor number • Associato ad ogni device in maniera unica
Creazione dei file di device I file di device non vengono creati al momento di caricare il driver, devono essere creati esplicitamente: mknod /dev/<device> [c|b] <major> <minor> Esempi: • mknod /dev/ttyS0 c 4 64 • mknod /dev/hda1 b 3 1
Modulo minimale /* hello.c */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> static int hello_init(void) { printk(KERN_ALERT “inizio\n"); return 0; } static void hello_exit(void) { printk(KERN_ALERT “fine\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL");
Funzioni di libreria • Non si possono usare le funzioni della libreria standard C come printf(), strcat(), ... • Linux fornisce alcune funzioni, come ad esempio printk(), la cui interfaccia e molto simile a quella di printf(). • Si possono usare solo gli header del kernel!
Compilazione di un kernel module • A kernel module is not an independent executable, but an object file which will belinked into the kernel in runtime and they should be compiled with • -c flag • _KERNEL_ symbol • MODULE symbol • LINUX symbol • CONFIG_MODVERSIONS symbol
Example of simple char device /* The necessary header files */ /* Standard in kernel modules */ #include <linux/kernel.h> /* We’re doing kernel work */ #include <linux/module.h> /* Specifically, a module */ #if CONFIG_MODVERSIONS==1 #define MODVERSIONS #include <linux/modversions.h> #endif #include <linux/fs.h> #include <linux/wrapper.h> #ifndef KERNEL_VERSION #define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c)) #endif #include <asm/uaccess.h> #define SUCCESS 0 /* Device Declarations */ /* The name for our device, as it will appear /* in /proc/devices */ #define DEVICE_NAME "char_dev" #define BUF_LEN 80
/* 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]; 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) { static int counter = 0; #ifdef DEBUG printk ("device_open(%p,%p)\n", inode, file); #endif printk("Device: %d.%d\n“,inode->i_rdev >> 8, inode->i_rdev & 0xFF); if (Device_Open) return -EBUSY; Device_Open++; sprintf(Message, counter++, Message_Ptr = Message; MOD_INC_USE_COUNT; return SUCCESS; }
if (Device_Open) return -EBUSY; Device_Open++; sprintf(Message, counter++, Message_Ptr = Message; MOD_INC_USE_COUNT; return SUCCESS; } static int device_release(struct inode *inode, struct file *file) { Device_Open --; MOD_DEC_USE_COUNT; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) return 0; #endif }
static ssize_t device_read(struct file *file, char *buffer, /* The buffer to fill with data */ size_t length, /* The length of the buffer */ loff_t *offset) /* Our offset in the file */ { /* Number of bytes actually written to the buffer */ int bytes_read = 0; /* If we’re at the end of the message, return 0 if (*Message_Ptr == 0) return 0; /* Actually put the data into the buffer */ while (length && *Message_Ptr) { put_user(*(Message_Ptr++), buffer++); length --; bytes_read ++; } #ifdef DEBUG printk ("Read %d bytes, %d left\n", bytes_read, length); #endif return bytes_read; }
static ssize_t device_write(struct file *file, const char *buffer, /* The buffer */ size_t length, /* The length of the buffer */ loff_t *offset) /* Our offset in the file */ { return -EINVAL; }
/* Module Declarations */ struct file_operations Fops = { NULL, /* seek */ device_read, device_write, NULL, /* readdir */ NULL, /* select */ NULL, /* 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() { /* Register the character device */ Major = module_register_chrdev(0, DEVICE_NAME, &Fops); /* Negative values signify an error */ if (Major < 0) { printk ("%s device failed with %d\n", "Sorry, registering the character", Major); return Major; } return 0; } /* Cleanup - unregister the appropriate file from /proc */ void cleanup_module() { int ret; /* Unregister the device */ ret = module_unregister_chrdev(Major, DEVICE_NAME); if (ret < 0) printk("Error in unregister_chrdev: %d\n", ret); }
Current Driver Models • Windows has several different driver models • Windows Driver Model (WDM) is the generic model • Specific driver models for popular devices classes • Storage, Networking, Printing, Imaging, etc... • Some built on top of WDM. Others run as user-mode services. • WDM Features • Asynchronous, packet-based I/O • I/O Cancellation • Layering of drivers • Dynamic loading and unloading of drivers • Plug and Play & Power management • Low-level high-performance interfaces
Limitations with Current Models • Generic driver model (WDM) is too complex • Focuses on very advanced drivers which punishes simple ones • Many drivers must be written in kernel mode • Even though much functionality could be user mode • Developers spend too much time driving our software • Cannot concentrate on driving their hardware • Driver quality suffers as a result • Do not allow extension and future growth
Kernel-Mode or User-Mode Driver? You must use kernel mode when you: • Need direct hardware access, for example, • Require DMA • Must handle interrupts • Need access to device registers • Have strict timing requirements • UMDF will have increased latency • Need kernel-only resources • Or kernel components need access to your driver
Introduction to WDM To allow driver developers to write device drivers that are source-code compatible across all Microsoft Windows operating systems, the Windows Driver Model (WDM) was introduced. Kernel-mode drivers that follow WDM rules are called WDM drivers. All WDM drivers must: Include wdm.h, not ntddk.h. (Note that wdm.h is a subset of ntddk.h.) • Be designed as a bus driver, a function driver, or a filter driver. • Create device objects. • Support Plug and Play. • Support power management. • Support Windows Management Instrumentation (WMI)
Esempio NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath { UNREFERENCED_PARAMETER (RegistryPath); DebugPrint (("Entered Driver Entry\n")); DriverObject->MajorFunction[IRP_MJ_CREATE] = GpdDispatch; DriverObject->MajorFunction[IRP_MJ_CLOSE] = GpdDispatch; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = GpdDispatch; DriverObject->DriverUnload = GpdUnload; DriverObject->MajorFunction[IRP_MJ_PNP] = GpdDispatchPnp; DriverObject->MajorFunction[IRP_MJ_POWER] = GpdDispatchPower; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = GpdDispatchSystemControl; DriverObject->DriverExtension->AddDevice = GpdAddDevice; return STATUS_SUCCESS; }
Esempio NTSTATUS GpdAddDevice(/* Plug & Play */ IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) { NTSTATUS status = STATUS_SUCCESS; PDEVICE_OBJECT deviceObject = NULL; PLOCAL_DEVICE_INFO deviceInfo; UNICODE_STRING ntDeviceName; UNICODE_STRING win32DeviceName; PAGED_CODE(); DebugPrint(("Entered AddDevice: %p\n", PhysicalDeviceObject)); RtlInitUnicodeString(&ntDeviceName, GPD_DEVICE_NAME); status = IoCreateDevice(DriverObject, sizeof (LOCAL_DEVICE_INFO), &ntDeviceName, GPD_TYPE, FILE_DEVICE_SECURE_OPEN, // do security checks on relative open FALSE, &deviceObject);
Esempio if (!NT_SUCCESS (status)) { DebugPrint(("IoCreateDevice failed: %x\n", status)); return status; } RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); status = IoCreateSymbolicLink( &win32DeviceName, &ntDeviceName ); if (!NT_SUCCESS(status)) // If we we couldn't create the link then { // abort installation. IoDeleteDevice(deviceObject); return status; } deviceInfo = (PLOCAL_DEVICE_INFO) deviceObject->DeviceExtension; deviceInfo->NextLowerDriver = IoAttachDeviceToDeviceStack ( deviceObject, PhysicalDeviceObject); if(NULL == deviceInfo->NextLowerDriver) { IoDeleteSymbolicLink(&win32DeviceName); IoDeleteDevice(deviceObject); return STATUS_NO_SUCH_DEVICE; }
Esempio IoInitializeRemoveLock (&deviceInfo->RemoveLock , PORTIO_TAG, 1, // MaxLockedMinutes 5); // HighWatermark, this parameter is // used only on checked build. deviceObject->Flags |= DO_POWER_PAGABLE; deviceInfo->DeviceObject = deviceObject; INITIALIZE_PNP_STATE(deviceInfo); deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; deviceInfo->PortMemoryType = 1; DebugPrint(("AddDevice: %p to %p->%p \n", deviceObject, deviceInfo->NextLowerDriver, PhysicalDeviceObject)); return STATUS_SUCCESS; }