Top Banner
A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’
26

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

Dec 20, 2015

Download

Documents

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

A ‘ringbuffer’ application

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

‘sleeping’ and ‘waking’

Page 2: 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

Page 3: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

Devices might be ‘busy’

• Sometimes an application wants to ‘write’ some data to a character device, such as a printer, but the device temporarily is not able to accept more data, being still busy with processing previously written data

• Again, in such situations we prefer to just wait until the device becomes ready for us to send it more data rather than to give up

Page 4: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

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 );

Page 5: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

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

Page 6: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

‘blocking’ while idle

• If a task is trying to read from a device-file when no data is present, but new data is expected to arrive, the operating system can ‘block’ that task from consuming any valuable CPU time while it is waiting, by ‘putting the task to sleep’ – yet arranging for that task to be ‘awakened’ as soon as some fresh data has actually arrived

Page 7: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

‘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

Page 8: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

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’

Page 9: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

What does ‘wakeup’ mean?

• A sleeping task is one whose ‘task.state’ is equal to ‘TASK_INTERRUPTIBLE’ or to ‘TASK_UNINTERRUPTIBLE’

• A sleeping task is ‘woken up’ by changing its ‘task,state’ to be ‘TASK_RUNNING’

• When the Linux scheduler sees that a task is in the ‘TASK_RUNNING’ state, it grants that task some CPU time for execution

Page 10: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

‘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)

Page 11: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

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 occurare put on alternative sub-lists, called ‘wait queues’, associated withthe particular event(s) that will allow a blocked task to be unblocked

Page 12: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

Kernel waitqueues

waitqueue

waitqueue

waitqueue

waitqueue

Page 13: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

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>

Page 14: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

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!

Page 15: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

‘interruptible’ is preferred

#include <linux/sched.h>

wait_queue_head_t wq;

init_waitqueue_head( &wq );

wait_event_interruptible( wq, <condition> );

wake_up_interruptible( &wq );

An ‘interruptible’ sleep can be awoken by a signal,

in case you might want to ‘unload’ your driver!

Page 16: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

A convenient ‘macro’

• DECLARE_WAIT_QUEUE_HEAD( wq );

• This statement can be placed outside yourmodule’s functions (i.e., a ‘global’ object)

• It combines declaration with initialization:wait_queue_head_t wq;init_waitqueue_head( &wq );

Page 17: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

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!

Page 18: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

Ringbuffer

• A first-in first-out data-structure (FIFO)

• Uses a storage array of finite length

• Uses two array-indices: ‘head’ and ‘tail’

• Data is added at the current ‘tail’ position

• Data is removed from the ‘head’ position

Page 19: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

‘ringbuffer’ depicted

DATA

HEAD

TAIL

DATA

DATA

DATA

DATA

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)

Page 20: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

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;

Page 21: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

read-algorithm for ‘stash’

• if ( ringbuffer_is_empty )

{

// sleep, until another task supplies some data

// or else exit if a signal is received by this task

}

• Remove a byte from the ringbuffer; • Copy the byte to user-space;• Awaken any sleeping writers;• return 1;

Page 22: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

write-algorithm for ‘stash’

• if ( ringbuffer_is_full )

{

// sleep, until some data is removed by another task

// or else exit if a signal is received by this task

}

• Copy a byte from user-space;• Insert this byte into ringbuffer;• Awaken any sleeping readers;• return 1;

Page 23: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

Demonstration of ‘stash’

• Quick demo: we can use I/O redirection

• For demonstrating ‘write’ to /dev/stash:

$ echo “Hello” > /dev/stash

• For demonstrating ‘read’ from /dev/stash:

$ cat /dev/stash

Page 24: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

The ‘device’ file-node

• We cannot use the ‘stash.c’ device-driver until a device-node has been created that allows both ‘read’ and ‘write’ access (the SysAdmin must usually do this setup):

#root mknod /dev/stash c 40 0#root chmod a+rw /dev/stash

• But you can do it, by using a module that resembles our ‘tempcdev.c’ demo (if you just modify its module-data appropriately)

Page 25: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

In-class exercise #1

• Download a fresh copy of our ‘tempcdev.c’ module and edit it, so that it will create the ‘/dev/stash’ device-file when you install it

• Then you can try using our ‘stash.c’ demo to send data from one task to another task by using the ‘echo’ and ‘cat’ commands

Page 26: A ‘ringbuffer’ application Introduction to process ‘blocking’ and the Linux kernel’s support for ‘sleeping’ and ‘waking’

In-class exercise #2

• Add a ‘get_info()’ function to this driver to create a pseudo-file (named ‘/proc/stash’) that will show the current contents of the ringbuffer (if any) and the current values for the ‘head’ and ‘tail’ buffer-indices

• Don’t forget: use ‘create_proc_info_entry()’ in your ‘init_module()’ function, and use ‘remove_proc_entry()’ during ‘cleanup’