360 likes | 551 Views
Character Device Driver ( Pre req : Multi tasking, threading). Dr A Sahu Dept of Comp Sc & Engg . IIT Guwahati. Outline. Character Device Driver Characteristics and functionality Basic IO functions Multi tasking (pre requisite to do this) Examples Drivers (in detail)
E N D
Character Device Driver ( Pre req: Multi tasking, threading) Dr A Sahu Dept of Comp Sc & Engg. IIT Guwahati
Outline • Character Device Driver • Characteristics and functionality • Basic IO functions • Multi tasking (pre requisite to do this) • Examples Drivers (in detail) • ADC, Printer, Tape drive
Linux Char Vs block $cd /dev/block/ $ ls –l rwxrwxrwx 1 root root 7 2010-08-12 21:32 1:0 -> ../ram0 lrwxrwxrwx 1 root root 7 2010-08-12 21:32 1:1 -> ../ram1 … lrwxrwxrwx 1 root root 8 2010-08-12 21:32 1:15 -> ../ram15 lrwxrwxrwx 1 root root 6 2010-08-13 03:02 11:0 -> ../sr0 lrwxrwxrwx 1 root root 8 2010-08-13 03:02 7:0 -> ../loop0 lrwxrwxrwx 1 root root 8 2010-08-13 03:02 7:1 -> ../loop1 …. rwxrwxrwx 1 root root 8 2010-08-13 03:02 7:7 -> ../loop7 lrwxrwxrwx 1 root root 6 2010-08-13 03:02 8:0 -> ../sda lrwxrwxrwx 1 root root 7 2010-08-13 03:02 8:1 -> ../sda1 …. lrwxrwxrwx 1 root root 7 2010-08-13 03:02 8:5 -> ../sda5 # Formats, mounts, and sets permissions on my 16MB ramdisk $/bin/mount /dev/ram0 /mnt/rd Mount –o loop CD.iso /mnt/cdrom
Linux Char Vs block $cd /dev/char/ $ ls –l lrwxrwxrwx 1 root root 8 2010-08-13 03:02 10:144 -> ../nvram lrwxrwxrwx 1 root root 15 2010-08-12 21:32 10:223 -> ../input/uinput lrwxrwxrwx 1 root root 9 2010-08-13 03:02 10:227 -> ../mcelog lrwxrwxrwx 1 root root 7 2010-08-13 03:02 10:228 -> ../hpet lrwxrwxrwx 1 root root 7 2010-08-12 21:33 10:229 -> ../fuse lrwxrwxrwx 1 root root 11 2010-08-13 03:02 10:231 -> ../snapshot lrwxrwxrwx 1 root root 6 2010-08-12 21:32 10:232 -> ../kvm lrwxrwxrwx 1 root root 13 2010-08-12 21:32 10:57 -> ../vboxnetctl lrwxrwxrwx 1 root root 10 2010-08-12 21:32 10:58 -> ../vboxdrv lrwxrwxrwx 1 root root 21 2010-08-13 03:02 10:59 -> ../network_throughput lrwxrwxrwx 1 root root 18 2010-08-13 03:02 10:60 -> ../network_latency lrwxrwxrwx 1 root root 18 2010-08-13 03:02 10:61 -> ../cpu_dma_latency lrwxrwxrwx 1 root root 17 2010-08-13 03:02 10:62 -> ../mapper/control lrwxrwxrwx 1 root root 14 2010-08-13 03:02 10:63 -> ../vga_arbiter
Linux Char Vs block $cd /dev/char/ $ ls –l lrwxrwxrwx 1 root root 6 2010-08-13 03:02 1:1 -> ../mem lrwxrwxrwx 1 root root 7 2010-08-13 03:02 1:11 -> ../kmsg lrwxrwxrwx 1 root root 9 2010-08-13 03:02 1:12 -> ../oldmem lrwxrwxrwx 1 root root 15 2010-10-06 22:42 116:10 -> ../snd/pcmC1D0c lrwxrwxrwx 1 root root 16 2010-10-06 22:42 116:11 -> ../snd/controlC1 lrwxrwxrwx 1 root root 12 2010-08-12 21:32 116:2 -> ../snd/timer lrwxrwxrwx 1 root root 10 2010-08-12 21:32 116:3 -> ../snd/seq lrwxrwxrwx 1 root root 15 2010-08-12 21:32 116:4 -> ../snd/pcmC0D1p lrwxrwxrwx 1 root root 15 2010-08-12 21:32 116:5 -> ../snd/pcmC0D0p lrwxrwxrwx 1 root root 15 2010-08-12 21:32 116:6 -> ../snd/pcmC0D0c lrwxrwxrwx 1 root root 13 2010-08-12 21:32 116:7 -> ../snd/hwC0D3 lrwxrwxrwx 1 root root 16 2010-08-12 21:32 116:8 -> ../snd/controlC0 lrwxrwxrwx 1 root root 15 2010-10-06 22:42 116:9 -> ../snd/pcmC1D0p lrwxrwxrwx 1 root root 7 2010-08-13 03:02 1:3 -> ../null lrwxrwxrwx 1 root root 15 2010-08-13 03:02 13:32 -> ../input/mouse0 lrwxrwxrwx 1 root root 15 2010-08-13 03:02 13:33 -> ../input/mouse1 lrwxrwxrwx 1 root root 13 2010-08-13 03:02 13:63 -> ../input/mice
Linux Char Vs block $cd /dev/char/ $ ls –l lrwxrwxrwx 1 root root 15 2010-08-13 03:02 13:64 -> ../input/event0 lrwxrwxrwx 1 root root 15 2010-08-13 03:02 13:65 -> ../input/event1 lrwxrwxrwx 1 root root 15 2010-08-13 03:02 13:66 -> ../input/event2 rwxrwxrwx 1 root root 13 2010-08-13 03:02 162:0 -> ../raw/rawctl lrwxrwxrwx 1 root root 7 2010-08-13 03:02 1:7 -> ../full lrwxrwxrwx 1 root root 9 2010-08-13 03:02 1:8 -> ../random lrwxrwxrwx 1 root root 18 2010-08-13 03:02 189:0 -> ../bus/usb/001/001 lrwxrwxrwx 1 root root 18 2010-08-13 03:02 189:128 -> ../bus/usb/002/001 lrwxrwxrwx 1 root root 18 2010-08-13 03:02 189:256 -> ../bus/usb/003/001 lrwxrwxrwx 1 root root 18 2010-08-13 03:02 189:384 -> ../bus/usb/004/001 lrwxrwxrwx 1 root root 18 2010-08-13 03:02 189:512 -> ../bus/usb/005/001 lrwxrwxrwx 1 root root 18 2010-08-13 03:02 189:513 -> ../bus/usb/005/002 lrwxrwxrwx 1 root root 18 2010-08-13 03:02 189:640 -> ../bus/usb/006/001 lrwxrwxrwx 1 root root 18 2010-10-06 22:42 189:649 -> ../bus/usb/006/010 lrwxrwxrwx 1 root root 18 2010-08-13 03:02 189:768 -> ../bus/usb/007/001 lrwxrwxrwx 1 root root 10 2010-08-13 03:02 1:9 -> ../urandom lrwxrwxrwx 1 root root 12 2010-08-13 03:02 202:0 -> ../cpu/0/msr lrwxrwxrwx 1 root root 12 2010-08-13 03:02 202:1 -> ../cpu/1/msr lrwxrwxrwx 1 root root 12 2010-08-13 03:02 202:2 -> ../cpu/2/msr lrwxrwxrwx 1 root root 14 2010-08-13 03:02 203:0 -> ../cpu/0/cpuid lrwxrwxrwx 1 root root 14 2010-08-13 03:02 203:1 -> ../cpu/1/cpuid lrwxrwxrwx 1 root root 14 2010-08-13 03:02 203:2 -> ../cpu/2/cpuid
Linux Char Vs block $cd /dev/char/ $ ls –l lrwxrwxrwx 1 root root 6 2010-08-13 03:02 21:0 -> ../sg0 lrwxrwxrwx 1 root root 6 2010-08-13 03:02 21:1 -> ../sg1 lrwxrwxrwx 1 root root 12 2010-08-13 03:02 226:0 -> ../dri/card0 lrwxrwxrwx 1 root root 17 2010-08-13 03:02 226:64 -> ../dri/controlD64 lrwxrwxrwx 1 root root 10 2010-08-13 03:02 250:0 -> ../hidraw0 lrwxrwxrwx 1 root root 10 2010-10-06 22:42 250:1 -> ../hidraw1 lrwxrwxrwx 1 root root 10 2010-08-13 03:02 251:0 -> ../usbmon0 … lrwxrwxrwx 1 root root 10 2010-08-13 03:02 251:7 -> ../usbmon7 lrwxrwxrwx 1 root root 14 2010-08-13 03:02 252:0 -> ../bsg/0:0:0:0 lrwxrwxrwx 1 root root 7 2010-08-13 03:02 254:0 -> ../rtc0 lrwxrwxrwx 1 root root 6 2010-08-13 03:02 29:0 -> ../fb0 lrwxrwxrwx 1 root root 7 2010-08-13 03:02 4:0 -> ../tty0 … lrwxrwxrwx 1 root root 8 2010-08-13 03:02 4:38 -> ../tty38 lrwxrwxrwx 1 root root 10 2010-08-13 03:02 5:1 -> ../console lrwxrwxrwx 1 root root 7 2010-08-13 03:02 5:2 -> ../ptmx lrwxrwxrwx 1 root root 6 2010-08-13 03:02 7:0 -> ../vcs .. lrwxrwxrwx 1 root root 7 2010-08-13 03:02 7:1 -> ../vcs6 lrwxrwxrwx 1 root root 7 2010-08-13 03:02 7:128 -> ../vcsa.. lrwxrwxrwx 1 root root 8 2010-08-12 21:32 7:134 -> ../vcsa6 lrwxrwxrwx 1 root root 7 2010-08-12 21:32 7:2 -> ../vcs2 lrwxrwxrwx 1 root root 7 2010-08-12 21:32 7:3 -> ../vcs3 lrwxrwxrwx 1 root root 7 2010-08-12 21:32 7:4 -> ../vcs4 lrwxrwxrwx 1 root root 7 2010-08-12 21:32 7:5 -> ../vcs5 lrwxrwxrwx 1 root root 7 2010-08-12 21:32 7:6 -> ../vcs6 lrwxrwxrwx 1 root root 11 2010-08-12 21:32 99:0 -> ../parport0
Basic char-driver components Device-driver LKM layout module’s ‘payload’ is a collection of callback-functions having prescribed prototypes function function function AND a ‘package’ of function-pointers fops . . . the usual pair of module-administration functions init registers the ‘fops’ exit unregisters the ‘fops’
Character Device Drivers • Character device drivers normally perform I/O in a byte stream. • Examples of devices using character drivers include tape drives and serial ports. • Character device drivers can also provide additional interfaces not present in block drivers, • I/O control (ioctl) commands • memory mapping • device polling.
Standard File-I/O functions intopen( char *pathname, int flags, … ); intread( intfd, void *buf, size_t count ); intwrite( intfd, void *buf, size_t count ); intlseek( intfd, loff_t offset, int whence ); intclose( intfd ); (and other less-often-used file-I/O functions) root# mknod /dev/cmos c 70 0 Read /dev/cmos as a FILE
Multi-tasking • Modern operating systems allow multiple users to share a computer’s resources • Users are allowed to run multiple tasks • The OS kernel must protect each task from interference by other tasks, while allowing every task to take its turn using some of the processor’s available time
Stacks and task-descriptors • To manage multitasking, the OS needs to use a data-structure which can keep track of every task’s progress and usage of the computer’s available resources (physical memory, open files, pending signals, etc.) • Such a data-structure is called a ‘process descriptor’ – every active task needs one • Every task needs its own ‘private’ stack
What’s on a program’s stack? Upon entering ‘main()’: • A program’s exit-address is on user stack • Command-line arguments on user stack • Environment variables are on user stack During execution of ‘main()’: • Function parameters and return-addresses • Storage locations for ‘automatic’ variables
Entering the kernel… A user process enters ‘kernel-mode’: • when it decides to execute a system-call • when it is ‘interrupted’ (e.g. by the timer) • when ‘exceptions’ occur (e.g. divide by 0)
Switching to a different stack • Entering kernel-mode involves not only a ‘privilege-level transition’ (from level 3 to level 0), but also a stack-area ‘switch’ • This is necessary for robustness: e.g., user-mode stack might be exhausted • This is desirable for security: e.g, privileged data might be accessible
What’s on the kernel stack? Upon entering kernel-mode: • task’s registers are saved on kernel stack (e.g., address of task’s user-mode stack) During execution of kernel functions: • Function parameters and return-addresses • Storage locations for ‘automatic’ variables
Supporting structures • So every task, in addition to having its own code and data, will also have a stack-area that is located in user-space, plus another stack-area that is located in kernel-space • Each task also has a process-descriptor which is accessible only in kernel-space
A task’s virtual-memory layout process descriptor and kernel-mode stack User space Kernel space Privilege-level 0 User-mode stack-area Privilege-level 3 Shared runtime-libraries Task’s code and data
The Linux process descriptor pagedir[] task_struct state mm_struct Each process descriptor contains many fields and some are pointers to other kernel structures which may themselves include fields that point to structures *stack flags *pgd *mm user_struct exit_code *user files_struct pid *files signal_struct *parent *signal
Something new in 2.6 • Linux uses part of a task’s kernel-stack • page-frame to store ‘thread information’ • The thread-info includes a pointer to the task’s process-descriptor data-structure Task’s kernel-stack structtask_struct 8-KB Task’s process-descriptor Task’s thread-info page-frame aligned
Tasks have ’states’ From kernel-header: <linux/sched.h> • #define TASK_RUNNING 0 • #define TASK_INTERRUPTIBLE 1 • #define TASK_UNINTERRUPTIBLE 2 • #define TASK_STOPPED 4 • #define TASK_TRACED 8 • #define TASK_NONINTERACTIVE 64 • #define TASK_DEAD 128
Fields in a process-descriptor struct task_struct { volatile long state; void *stack; unsigned long flags; struct mm_struct *mm; struct thread_struct *thread; pid_t pid; char comm[16]; /* plus many other fields */ };
Finding a task’s ‘thread-info’ • During a task’s execution in kernel-mode, it’s very quick to find that task’s thread-info object • Just use two assembly-language instructions: movl $0xFFFFF000, %eax andl %esp, %eax Ok, now %eax = the thread-info’s base-address There’s a macro that implements this computation
Finding task-related kernel-data • Use a macro ‘task_thread_info( task )’ to get a pointer to the ‘thread_info’ structure: struct thread_info *info = task_thread_info( task ); • Then one more step gets you back to the address of the task’s process-descriptor: struct task_struct *task = info->task;
The kernel’s ‘task-list’ • Kernel keeps a list of process descriptors • A ‘doubly-linked’ circular list is used • The ‘init_task’ serves as a fixed header • Other tasks inserted/deleted dynamically • Tasks have forward & backward pointers, implemented as fields in the ‘tasks’ field • To go forward: task = next_task( task ); • To go backward: task = prev_task( task );
Doubly-linked circular list next_task init_task (pid=0) newest task … prev_task
Maybe a big /proc file… • We can’t know ahead of time how many tasks are active in our system – this will depend on many varying factors, such as who else is logged in, which commands have been issued, whether we’re using text-mode console or graphical desktop • So it’s perfectly possible our pseudo-file might ‘overflow’ its kernel-supplied buffer!
How to avoid buffer-overflow • Our module’s ‘get_info()’ callback-function has four parameter-values passed to it by the kernel: • char *buf - address of a small kernel buffer • char **start - address of a pointer variable • off_t offset - current offset of file-pointer • int buflen - size of the kernel buffer • The initial conditions are: offset == 0 and *start == NULL • Kernel’s behavior will vary if we modify *start
Normal case • We expect the ‘/proc’ file to deliver a small amount of text-data (not more than would fit in the kernel-supplied buffer (e.g., 3KB) • So we make no change to ‘*start’ • Then kernel will deliver the data it finds in the buffer it had supplied to ‘get_info()’ • The kernel will not call ‘get_info()’ again (unless our file is closed and reopened)
Alternative case • Our ‘get_info()’ function modifies the value of the (initially NULL) ‘*start’ pointer – for example, maybe assigning it the address of some buffer we’ve allocated, or even assigning the address of the kernel-buffer: *start = buf; • In this case, the kernel will again call our module’s ‘get_info()’ function, provided we returned a nonzero function-value before!
The benefit • Knowing about this alternative option, we can design our ‘get_info()’ function so that it delivers a big amount of data in several small-size chunks, never overflowing the size-limitations on the kernel’s buffer • We just need to think carefully about the differing senarios under which ‘get_info()’ will be repeatedly called
First pass • The value of ‘offset’ will be zero • We set *start to a buffer-address where we place a positive number of data-bytes • Kernel delivers those bytes to the ‘reader’, taking them from the *start address, then advances the file-pointer by that amount • Kernel calls our ‘get_info()’ again, but with a non-zero ‘offset’ value this time!
Final time • When our ‘get_info()’ function has finally finished delivering all the desired data to the file’s ‘reader’, and still we receive yet another ‘get_info()’ call, then we simply return a function-value equal to zero, telling the kernel that the data has been exhausted -- and so not to call again!
Our implementation structtask_struct *task; // ‘global’ variables’ values remembered intmy_get_info( char *buf, char **start, off_t offset, intbuflen ) { intlen = 0; if ( offset == 0 ) // our first time through this function { task = &init_task; // start of circular linked-list } else if ( task == &init_task ) return 0; // our final pass // put some data into the kernel-supplied buffer len += sprintf( buf+len, “pid=%d \n”, task->pid ); *start = buf; // tell kernel where to find data, and to call again task = next_task( task ); // advance to next node of circular list return len; // and tell kernel how far to advance }
‘Kernel threads’ • Some tasks don’t have a page-directory of their own – because they don’t need one • They only execute code, and access data, that resides in the kernel’s address space • They can just ‘borrow’ the page-directory that belongs to another task • These ‘kernel thread’ tasks will store the NULL-pointer value (i.e., zero) in the ‘mm’ field of their ‘task_struct’ descriptor (mm:mem map)