Understanding 'Blocking' in Linux Kernel's Device Driver Sleep/Wake Support, Slides of Computer Applications

The concept of 'blocking' in linux kernel device drivers, where tasks can be put to sleep and awakened when data is available or a device is ready. It covers the use of 'sleep' and 'wakeup' functions, 'run' queues and 'wait' queues, and the advantages of avoiding 'busy waiting'.

Typology: Slides

2012/2013

Uploaded on 04/17/2013

pamelaaaa
pamelaaaa 🇮🇳

4.5

(12)

103 documents

1 / 26

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
A ‘ringbuffer’ application
Introduction to process ‘blocking’
and the Linux kernel’s support for
‘sleeping’ and ‘waking’
Docsity.com
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a

Partial preview of the text

Download Understanding 'Blocking' in Linux Kernel's Device Driver Sleep/Wake Support and more Slides Computer Applications in PDF only on Docsity!

A ‘ringbuffer’ application

Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

Devices might be ‘idle’

  • With our previous device-driver examples (i.e., dram, cmosram), the data to be read was already there, just waiting to be input
  • But with certain other character devices, such as a keyboard, a program may want to input its data before any new data has actually been entered by a user
  • In such cases we prefer to wait until data arrives rather than to abandon reading

We could do ‘busy waiting’…

  • It is possible for a device-driver to ‘poll’ a status-bit continuously until data is ready, (or until a device is no longer “too busy”):
  • Such a technique is called ‘busy waiting’
  • But it could waste a lot of valuable CPU time before any benefit was realized!

do { status = inb( 0x64 ); } while ( ( status & READY ) == 0 );

Avoid ‘busy waiting’

  • In a multitasking system we would want to avoid having any processes use the ‘busy waiting’ strategy whenever possible, as it ‘stalls’ any progress by other tasks – it’s a system-performance ‘bottleneck’!
  • So modern operating systems support an alternative strategy, which allows those tasks that could proceed to do so

‘blocking’ while busy

  • Similarly, if a task is trying to ‘write’ to a device-file, but that device is ‘busy’ with previously written data, then the OS can put this task to sleep, preventing it from wasting any CPU time during its delay so that other tasks can do useful work – but arranging for this ‘sleeping’ task to be ‘woken up’ as soon as the device is no longer ‘busy’ and can accept fresh data

What does ‘sleep’ mean?

  • The Linux kernel puts a task to sleep by simply modifying the value of its ‘state’ variable: - TASK_RUNNING - TASK_STOPPED - TASK_UNINTERRUPTIBLE - TASK_INTERRUPTIBLE …
  • Only tasks with ‘state == TASK_RUNNING’ are granted time on the CPU by the ‘scheduler’

‘run’ queues and ‘wait’ queues

  • In order for Linux to efficiently manage the scheduling of its various ‘tasks’, separate queues are maintained for ‘running’ tasks and for tasks that temporarily are ‘blocked’ while waiting for a particular event to occur (such as the arrival of new data from the keyboard, or the exhaustion of prior data sent to the printer)

Some tasks are ‘ready-to-run’

Those tasks that are ready-to-run comprise a sub-list of all the tasks, and they are arranged on a queue known as the ‘run-queue’ Those tasks that are blocked while awaiting a specific event to occur are put on alternative sub-lists, called ‘wait queues’, associated with the particular event(s) that will allow a blocked task to be unblocked

Kernel’s support-routines

  • The Linux kernel makes it easy for drivers to perform the ‘sleep’ and ‘wakeup’ actions while avoiding potential ‘race conditions’ that are inherent in a ‘preemptive’ kernel
  • Your driver can use the support-routines by including the header: <linux/sched.h>

Use of Linux wait-queues

  • #include <linux/sched.h>
  • wait_queue_head_t my_queue;
  • init_waitqueue_head( &my_queue );
  • sleep_on ( &my_queue );
  • wake_up ( &my_queue );
  • But can’t unload driver if task stays asleep!

A convenient ‘macro’

  • DECLARE_WAIT_QUEUE_HEAD( wq );
  • This statement can be placed outside your

module’s functions (i.e., a ‘global’ object)

  • It combines declaration with initialization:

wait_queue_head_t wq; init_waitqueue_head( &wq );

Our ‘stash’ device

  • Device works like a public ‘clipboard’
  • It uses kernel memory to store its data
  • It allows ‘communication’ between tasks
  • What one task writes, another can read!

‘ringbuffer’ depicted

DA TA

HEAD

TAIL

DA TA

DA TA

D A T A

DA TA

FIFO rules: The next data to be added goes in at the current ‘tail’ position, and the next data to be removed comes from the ‘head’ position The ringbuffer is ‘empty’ when ‘head’ equals ‘tail’, and it is ‘full’ if ‘tail’ + 1 equals ‘head’ (modulo RINGSIZE)

Ringbuffer (continued)

  • One array-position is always left unused
  • Condition ‘head == tail’ means “empty”
  • Condition tail == head-1 means “full”
  • Both ‘head’ and ‘tail’ will “wraparound”
  • Calculation: next = ( next+1 )%RINGSIZE;