All about programming in GNU/LINUX

Posts tagged “Linux device Driver

Using Semaphores and Mutex in Linux Device drivers to tackle concurency

Semaphores provide a satisfactory solution for issues related to concurrency . Access to critical section is controlled by enforcing threads to hold a lock before entering the critical section , Without a semaphore being unlocked no thread is allowed access to execute in the critical section .

Semaphore is a single integer value bound to two functions P and V . P is called down or some variation of that name and V is called up . Function P is used to lock the critical region and V is used to unlock . Function P decreases the count of semaphores , for example from 1 to 0 . When the value of the semaphore still remains 0 or less no other threads are allowed to execute in the critical region . There are made to wait or even may be put to sleep till the region is unlocked .

Functions of type V releases the semaphores that’s being help , these category of functions increases the value count of the semaphore and may even wake up the processes waiting for the access into the critical region .

Semaphores are generally and mostly used for the purpose of mutual exclusion , that is to make sure that only one thread of execution or a process executes in the critical section .

 Linux Implementation of Semaphores

<asm/semaphore.h> defines several functionalities for semaphore and mutex implementation ( Mutexes are just a special case of semaphores ) .

To initialize a declared semaphore ,

void sema_init(struct semaphore *sema, int val) can be used ,

 this initializes the declared mutex to value val ( The second argument to the function ) ;

But , as i had mentioned earlier semaphores are generally used for implementation of mutex ,

the following two functions declare and initialize the mutex

DECLARE_MUTEX(mutex_name ) ;

DECLARE_MUTEX_LOCKED(mutex_name) ;

 These functions declares mutex named as ‘mutex_name’ , the function DECLARE_MUTEX declares the mutex and initializes it to 1 ( 1 is the unlocked state , a thread or a process can possibly lock the critical region ) , wherein the later function declares the mutex and initializes it to 0 ( 0 is the locked state ) , mutex has to be explicitly unlocked before it can be used for locking .

 

 For initializing the mutex at runtime the following functions can be used ,

void init_MUTEX(struct semaphore *sema ) ;

void init_MUTEX_LOCKED(struct semaphore *sema) ;

 Holding and releasing Mutex ( The version of P [down] and V[up] functions )

 Linux kernel provides three versions of down function

  1. void down(struct semaphore *sema) ;

  2. int down_interruptible(struct semaphore *sema) ;

  3. int down_trylock(struct semaphore *sema ) ;

As briefed earlier down decrements the value of the semaphore , successful exection of any version of down function leads to locking of the critical region of the code , and the thread of execution or the process is said to have acquired the lock . Other user space process trying to access the critical region of code will be made to wait .

In case of the first version of down ( void down_interruptible() ) , the user space process waiting for the mutex to be released cannot to interrupted , it’ll be running in a dreaded state . This is not desirable in many cases , so the second version of down allows the waiting user space process to be interrupted .

The third version can used to test and lock , if the lock is already being held the function returns with a negative value , If no other thread is holding the lock , the lock will be acquired . Using down_trylock() the process wont wait for the acquired lock to be released , it returns immediately .

void up(struct semaphore * sema) is used to unlock the acquired lock and even wake up the processes waiting on the semaphore .

When executing in the critical region on occurance of error its very important to unlock the region before returning

 

 

 

 

 

Advertisements

A simple Linux Driver : Allocating the Major Minor number

The first step in writing any Linux Device driver is to allocate a Major , Minor pair for the driver , this is a simple Linux Device Driver which allocates the Major , Minor pair and prints the same into DMESG


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>

static dev_t first;

static int __init my_init(void) /*constructor*/
{
    int ret;

    printk(KERN_INFO "Drivre is being registered");
    if((ret = alloc_chrdev_region(&first,0,3,"Rao")) < 0) /*dynamic allocatin of Major , minor numbers*/
    {

        return ret;
    }
    printk(KERN_INFO "<MAJOR , MINOR ><%d %d>\n" , MAJOR(first),MINOR(first)); /*printing the Major , Minor pair into the dmesg*/
    return 0;
}

static void __exit my_exit(void) /*destructor function , executed when the driver is removed*/
{
    unregister_chrdev_region(first,3); 
    printk(KERN_INFO "dRIVER REMOVED");
}

    module_init(my_init);
    modue_exit(my_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("KARTHIC RAO");
    MODULE_DESCRIPTION("A SIMPLE DRIVER OF MINE");

here is the MAKEFILE for the driver

# If called directly from the command line, invoke the kernel build system.
ifeq ($(KERNELRELEASE),)

	KERNEL_SOURCE := /usr/src/linux-headers-$(shell uname -r)
	PWD := $(shell pwd)
default:
	$(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=$(PWD) modules

clean:
	$(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=$(PWD) clean

# Otherwise KERNELRELEASE is defined; we've been invoked from the
# kernel build system and can use its language.
else

	obj-m := simple_driver.o

endif