linux驱动学习(3)--同步、信号量和自旋锁

发布时间:2024-08-14  

在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源) ,可能会引发“竞态” ,因此我们必须对共享资源进行并发控制。Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用) 。


自旋锁与信号量“类似而不类” ,类似说的是它们功能上的相似性, “不类”指代它们在本质和实现机理上完全不一样,不属于一类。


自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已经释放了锁, “自旋”就是“在原地打转” 。而信号量则引起调用者睡眠,它把进程从运行队列上拖出去,除非获得锁。这就是它们的“不类” 。


但是,无论是信号量,还是自旋锁,在任何时刻,最多只能有一个保持者,即在任何时刻最多只能有一个执行单元获得锁。这就是它们的“类似” 。


鉴于自旋锁与信号量的上述特点,一般而言,自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用;信号量适合于保持时间较长的情况,会只能在进程上下文使用。如果被保护的共享资源只在进程上下文访问,则可以以信号量来保护该共享资源,如果对共享资源的访问时间非常短,自旋锁也是好的选择。但是,如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断) ,就必须使用自旋锁。


先贴代码


#include

#include

#include

#include

#include

#include

#include


/*

 * xiaoyang yi @HIT 2011-9-22

 * char device driver with sync(semaphore and spinlock)

 */


MODULE_LICENSE('GPL');


static int MAJOR_NUM=0;

static struct semaphore sem;

static int global_var = 0;

static int global_var_count = 0;

static spinlock_t spin;


static ssize_t globalvar_read(struct file*,char*,size_t, loff_t*);

static ssize_t globalvar_write(struct file*,const char*,size_t, loff_t*);

static int globalvar_open(struct inode*node, struct file* fp);

static int globalvar_release(struct inode*node, struct file* fp);


/*init the file_operation structure*/

static struct file_operations globalvar_fpos={

.read = globalvar_read,

.write = globalvar_write,

.open = globalvar_open,

.release = globalvar_release,

};


static int __init globalvar_init(void)

{

int ret;


/*register device drivre*/

ret = register_chrdev(MAJOR_NUM,'globalvar',&globalvar_fpos);

if(ret < 0){

printk('globalvar reg failed!n');

}else{

printk('globalvar reg okn');

spin_lock_init(&spin);

}

if(MAJOR_NUM == 0){

MAJOR_NUM = ret;

}


sema_init(&sem,1);

return ret;

}


static void __exit globalvar_exit()

{

unregister_chrdev(MAJOR_NUM,'globalvar');

}


static ssize_t globalvar_read(struct file* fp, char* buf, size_t len, loff_t* off)

{

printk('[debug]:globalvar read get in!n');

/*get semaphore*/

if(down_interruptible(&sem)){

return -ERESTARTSYS;

}

/*copy from kernel to user space*/

if(copy_to_user(buf,&global_var,sizeof(int)) != 0){

/*release semaphore*/

up(&sem);

return -EFAULT;

}

/*release semaphore*/

up(&sem);

printk('[debug]:globalvar read ok!n');

return sizeof(int);

}


static ssize_t globalvar_write(struct file* fs,const char* buf, size_t len, loff_t* off)

{

/*get semaphore*/

if(down_interruptible(&sem)){

return -ERESTARTSYS;

}

printk('down_interruptible ok!n');

if(copy_from_user(&global_var,buf,sizeof(int) != 0)){

/*release semaphore*/

up(&sem);

return -EFAULT;

}


/*release semaphore*/

up(&sem);

return sizeof(int);

}


/*

 * open device with checking busy.

 * if busy,count++;else return 0

 */

static int globalvar_open(struct inode*node, struct file* fp)

{

/*get spinlock*/

spin_lock(&spin);

/*reach criticle section*/

if(global_var_count){

spin_unlock(&spin);

printk('[debug]:globalvar open fialed!n');

return -EBUSY;

}

/*release spinlock*/

global_var_count++;

spin_unlock(&spin);

printk('[debug]:globalvar open ok!n');

return 0;

}


static int globalvar_release(struct inode*node, struct file* fp)

{

spin_lock(&spin);

global_var_count--;

spin_unlock(&spin);

return 0;

}


/*module setting*/

module_init(globalvar_init);

module_exit(globalvar_exit);


/*this is end of file*/

makefile如下:


CC=gcc


PWD:=$(shell pwd)


KERNELDIR=/usr/src/kernels/2.6.40.4-5.fc15.i686.PAE


INSTALLDIR=./


INC=$(KERNELDIR)/include


MOD_NAME=globalvar


obj-m:=globalvar.o


    modules:

     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules -I $(INC)

     @echo 'compiled ok!'

    run:

     sudo insmod *.ko 

    install:

     @echo 'install ok!'

    unstall:

     sudo rmmod $(MOD_NAME) 

     sudo rmmod /dev/$(MOD_NAME)

     @echo 'unregister module ok!'

    debug:

     cat /var/log/messages | tail

    result:

     cat /proc/devices 

    help:

     @echo 'install with 'mknod /dev/devname c dev_num 0' cmd'

    clean:

     rm -rf *.o *.ko *.mod.c *.markers *.order *.symvers




程序很简单,就不多解释了。


在测试时出现过Oops NULL Pointer内核错误。


Oops信息如下:


UG: sleeping function called from invalid context at arch/x86/mm/fault.c:1103

in_atomic(): 0, irqs_disabled(): 1, pid: 11416, name: a.out

Pid: 11416, comm: a.out Not tainted 2.6.40.4-5.fc15.i686.PAE #1

Call Trace:

 [] ? printk+0x2d/0x2f

 [] __might_sleep+0xdd/0xe4

 [] do_page_fault+0x173/0x32d

 [] ? unlock_page+0x21/0x24

 [] ? notify_page_fault+0x40/0x40

 [] error_code+0x67/0x6c

 [] ? __list_add+0x3e/0x81

 [] __down_common+0x2a/0xb8

 [] __down_interruptible+0x17/0x19

 [] down_interruptible+0x2a/0x3c

 [] globalvar_read+0x15/0x56 [globalvar]

 [] vfs_read+0x8d/0xd5

 [] ? globalvar_write+0x54/0x54 [globalvar]

 [] sys_read+0x42/0x63

 [] sysenter_do_call+0x12/0x28

BUG: unable to handle kernel NULL pointer dereference at   (null)

IP: [] __list_add+0x3e/0x81

*pdpt = 000000002fed0001 *pde = 0000000000000000 

Oops: 0000 [#1] SMP 


是空指针错误,由其堆栈及调用信息可大概猜出是semaphore或者spin的错误。后来排查发现semaphore没有init,如果信号量用户互斥(mutex:mutual exclusion),将信号量的值初始化的1,这样只允许一个进程或者线程执行。这种情况下,信号量也成为互斥锁。从实践现象看来;没有初始化信号量直接down和up会造成访问错误,具体原因还没查到。炯!


应用程序:


#include

#include

#include

#include

#include

 

/*

 * xiaoyang yi @HIT 2011.9.24

 * this is a test for char device 'globalvar'

 */

  

int main()

{

    int fd,num;

     

    /*opemn device*/

    fd = open('/dev/globalvar',O_RDWR,S_IRUSR|S_IWUSR);

    if(fd != -1){

        /*read globalvar the first time*/

        read(fd,&num,sizeof(int));

        printf('read first time,globalvar=%dn',num);

 

        /*writing test*/

        printf('print number to write: ');

        scanf('%d',&num);

        write(fd,&num,sizeof(int));

 

        /*read globalvar*/

        read(fd,&num,sizeof(int));

        printf('read again globalvar=%dn',num);

 

        close(fd);

    }else{

        printf('device open failed!n');

    }

    return 0;

}


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>