⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 linux设备驱动程序笔记.txt

📁 Linux设备驱动程序笔记
💻 TXT
📖 第 1 页 / 共 5 页
字号:
void set_bit(nr, void *addr); /*设置第 nr 位在 addr 指向的数据项中。*/
void clear_bit(nr, void *addr); /*清除指定位在 addr 处的无符号长型数据.*/
void change_bit(nr, void *addr);/*翻转nr位.*/



test_bit(nr, void *addr); /*这个函数是唯一一个不需要是原子的位操作; 它简单地返回这个位的当前值.*/

/*以下原子操作如同前面列出的, 除了它们还返回这个位以前的值.*/

int test_and_set_bit(nr, void *addr); 
int test_and_clear_bit(nr, void *addr); 
int test_and_change_bit(nr, void *addr); 
 

 以下是一个使用范例: 

/* try to set lock */
while (test_and_set_bit(nr, addr) != 0)
    wait_for_a_while(); 
/* do your work */ 
/* release lock, and check. */
if (test_and_clear_bit(nr, addr) == 0)
    something_went_wrong(); /* already released: error */
 

 

(4)seqlock

2.6内核包含了一对新机制打算来提供快速地,无锁地存取一个共享资源。seqlock要保护的资源小,简单,并且常常被存取,并且很少写存取但是必须要快。seqlock 通常不能用在保护包含指针的数据结构。seqlock 定义在 <linux/seqlock.h> 。 

/*两种初始化方法*/
seqlock_t lock1 = SEQLOCK_UNLOCKED;
seqlock_t lock2;
seqlock_init(&lock2);
 

这个类型的锁常常用在保护某种简单计算,读存取通过在进入临界区入口获取一个(无符号的)整数序列来工作。在退出时,那个序列值与当前值比较;如果不匹配,读存取必须重试。读者代码形式:  

unsigned int seq;
do {
    seq = read_seqbegin(&the_lock);
    /* Do what you need to do */
} while read_seqretry(&the_lock, seq);
 

 如果你的seqlock可能从一个中断处理里存取,你应当使用 IRQ 安全的版本来代替:

unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);
int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned long flags);
 

写者必须获取一个排他锁来进入由一个 seqlock 保护的临界区,写锁由一个自旋锁实现, 调用:

void write_seqlock(seqlock_t *lock); 
void write_sequnlock(seqlock_t *lock);
 

因为自旋锁用来控制写存取, 所有通常的变体都可用: 

void write_seqlock_irqsave(seqlock_t *lock, unsigned long flags);
void write_seqlock_irq(seqlock_t *lock);
void write_seqlock_bh(seqlock_t *lock);
void write_sequnlock_irqrestore(seqlock_t *lock, unsigned long flags);
void write_sequnlock_irq(seqlock_t *lock);
void write_sequnlock_bh(seqlock_t *lock);
 

 还有一个 write_tryseqlock 在它能够获得锁时返回非零。

(5)读取-复制-更新

读取-拷贝-更新(RCU) 是一个高级的互斥方法, 在合适的情况下能够有高效率. 它在驱动中的使用很少。 


--------------------------------------------------------------------------------
4.7 开发板实验
SBC2440V4开发板上作completion的实验,因为别的实验都要在并发状态下才可以实验,所以本章只做了completion的实验。将《Linux设备驱动程序(第3版)》提供的源码做了修改,将原来的2.4内核的模块接口改成了2.6的接口,并编写了测试程序。实验源码如下:

模块程序链接:complete模块
模块测试程序链接:测试程序

 

[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod complete.ko
[Tekkaman2440@SBC2440V4]#echo 8 > /proc/sys/kernel/printk
[Tekkaman2440@SBC2440V4]#cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
252 complete
253 usb_endpoint
254 rtc

Block devices:
  1 ramdisk
256 rfd
  7 loop
 31 mtdblock
 93 nftl
 96 inftl
179 mmc
[Tekkaman2440@SBC2440V4]#mknod -m 666 /dev/complete c 252 0
[Tekkaman2440@SBC2440V4]#cd /tmp/
[Tekkaman2440@SBC2440V4]#./completion_testr&
[Tekkaman2440@SBC2440V4]#process 814 (completion_test) going to sleep
[Tekkaman2440@SBC2440V4]#./completion_testr&
[Tekkaman2440@SBC2440V4]#process 815 (completion_test) going to sleep
[Tekkaman2440@SBC2440V4]#ps
  PID Uid VSZ Stat Command
    1 root 1744 S init
    2 root SW< [kthreadd]
    3 root SWN [ksoftirqd/0]
    4 root SW< [watchdog/0]
    5 root SW< [events/0]
    6 root SW< [khelper]
   59 root SW< [kblockd/0]
   60 root SW< [ksuspend_usbd]
   63 root SW< [khubd]
   65 root SW< [kseriod]
   77 root SW [pdflush]
   78 root SW [pdflush]
   79 root SW< [kswapd0]
   80 root SW< [aio/0]
  707 root SW< [mtdblockd]
  708 root SW< [nftld]
  709 root SW< [inftld]
  710 root SW< [rfdd]
  742 root SW< [kpsmoused]
  751 root SW< [kmmcd]
  769 root SW< [rpciod/0]
  778 root 1752 S -sh
  779 root 1744 S init
  781 root 1744 S init
  782 root 1744 S init
  783 root 1744 S init
  814 root 1336 D ./completion_testr
  815 root 1336 D ./completion_testr
  816 root 1744 R ps
[Tekkaman2440@SBC2440V4]#./completion_testw
process 817 (completion_test) awakening the readers...
awoken 814 (completion_test)
write code=0
[Tekkaman2440@SBC2440V4]#read code=0
[Tekkaman2440@SBC2440V4]#ps
  PID Uid VSZ Stat Command
    1 root 1744 S init
    2 root SW< [kthreadd]
    3 root SWN [ksoftirqd/0]
    4 root SW< [watchdog/0]
    5 root SW< [events/0]
    6 root SW< [khelper]
   59 root SW< [kblockd/0]
   60 root SW< [ksuspend_usbd]
   63 root SW< [khubd]
   65 root SW< [kseriod]
   77 root SW [pdflush]
   78 root SW [pdflush]
   79 root SW< [kswapd0]
   80 root SW< [aio/0]
  707 root SW< [mtdblockd]
  708 root SW< [nftld]
  709 root SW< [inftld]
  710 root SW< [rfdd]
  742 root SW< [kpsmoused]
  751 root SW< [kmmcd]
  769 root SW< [rpciod/0]
  778 root 1752 S -sh
  779 root 1744 S init
  781 root 1744 S init
  782 root 1744 S init
  783 root 1744 S init
  815 root 1336 D ./completion_testr
  818 root 1744 R ps
[1] - Done ./completion_testr
[Tekkaman2440@SBC2440V4]#./completion_testw
process 819 (completion_test) awakening the readers...
awoken 815 (completion_test)
write code=0
[Tekkaman2440@SBC2440V4]#read code=0
[Tekkaman2440@SBC2440V4]#ps
  PID Uid VSZ Stat Command
    1 root 1744 S init
    2 root SW< [kthreadd]
    3 root SWN [ksoftirqd/0]
    4 root SW< [watchdog/0]
    5 root SW< [events/0]
    6 root SW< [khelper]
   59 root SW< [kblockd/0]
   60 root SW< [ksuspend_usbd]
   63 root SW< [khubd]
   65 root SW< [kseriod]
   77 root SW [pdflush]
   78 root SW [pdflush]
   79 root SW< [kswapd0]
   80 root SW< [aio/0]
  707 root SW< [mtdblockd]
  708 root SW< [nftld]
  709 root SW< [inftld]
  710 root SW< [rfdd]
  742 root SW< [kpsmoused]
  751 root SW< [kmmcd]
  769 root SW< [rpciod/0]
  778 root 1752 S -sh
  779 root 1744 S init
  781 root 1744 S init
  782 root 1744 S init
  783 root 1744 S init
  820 root 1744 R ps
[2] + Done ./completion_testr
[Tekkaman2440@SBC2440V4]#ps
  PID Uid VSZ Stat Command
    1 root 1744 S init
    2 root SW< [kthreadd]
    3 root SWN [ksoftirqd/0]
    4 root SW< [watchdog/0]
    5 root SW< [events/0]
    6 root SW< [khelper]
   59 root SW< [kblockd/0]
   60 root SW< [ksuspend_usbd]
   63 root SW< [khubd]
   65 root SW< [kseriod]
   77 root SW [pdflush]
   78 root SW [pdflush]
   79 root SW< [kswapd0]
   80 root SW< [aio/0]
  707 root SW< [mtdblockd]
  708 root SW< [nftld]
  709 root SW< [inftld]
  710 root SW< [rfdd]
  742 root SW< [kpsmoused]
  751 root SW< [kmmcd]
  769 root SW< [rpciod/0]
  778 root 1752 S -sh
  779 root 1744 S init
  781 root 1744 S init
  782 root 1744 S init
  783 root 1744 S init
  821 root 1744 R ps
[Tekkaman2440@SBC2440V4]#./completion_testw
process 822 (completion_test) awakening the readers...
write code=0
[Tekkaman2440@SBC2440V4]#./completion_testr
process 823 (completion_test) going to sleep
awoken 823 (completion_test)
read code=0         
 
实验表明:如果先读数据,读的程序会被阻塞(因为驱动在wait_for_completion,等待写的完成)。如果先写,读程序会比较顺利的执行下去(虽然也会休眠,但马上会被唤醒!)。其原因可以从completion的源码中找答案。completion其实就是自旋锁的再包装,具体细节参见completion的源码。 
 

Linux设备驱动程序(4-补)-Linux中的循环缓冲区

--------------------------------------------------------------------------------

参考资料:《Linux内核中的循环缓冲区》作者:西邮 王聪    严重感谢文章作者! 但是(可能是源码版本问题)有些结论并不正确: “而kfifo_init只会接受一个已分配好空间的fifo->buffer,不能和kfifo_free搭配,用kfifo_init分配的kfifo只能用kfree释放。” 阅读源码可以得出这样的结论:kfifo_init和kfifo_alloc分配的kfifo都能用kfree释放。已经用实验证实。

原文链接地址: http://www.kerneltravel.net/jiaoliu/kern-kfifo.html


--------------------------------------------------------------------------------

在学习到第十章 中断处理 时,其中的中断驱动的I/O需要使用缓冲区,觉得与其自己实现一个缓冲区,不如利用内核已经写好的fifo。内核里有一个通用的循环缓冲区的实现在 <linux/kfifo.h>。
缓冲区结构
使用的数据结构如下:

struct kfifo {
    unsigned char *buffer;    /* 使用的缓冲区头指针 */
    unsigned int size;    /* 缓冲区总大小 */
    unsigned int in;    /* 已写入缓冲区的数据总量,当前缓冲区写指针的偏移量:(in % size) */
    unsigned int out;    /* 已读出缓冲区的数据总量,当前缓冲区读指针的偏移量:(out % size) */
    spinlock_t *lock;    /* 为避免竞态的自旋锁 */
};/*当in==out时,缓冲区为空;当(in-out)==size时,缓冲区已满*/
 

kfifo提供的循环缓冲的部分函数分为2类:

(1)以双下划线开头,没有使用自旋锁函数;

(2)没有双下划线开头,需要额外加锁的情况下使用的函数。

其实第二类只是在第一类的基础上进行加锁后,实际的代码如下: 

    unsigned long flags;
    spin_lock_irqsave(fifo->lock, flags);
    /*第一类函数*/
    spin_unlock_irqrestore(fifo->lock, flags);
 

以下按使用的顺序介绍每个函数的使用,部分函数源码在kernel/kfifo.c中定义,这些接口是经过精心构造的,可以小心地避免一些边界情况,原理其实很简单,建议去看源码弄清楚实现的原理,可以学到一些编程技巧。

(0)声明循环缓冲数据结构指针 

struct kfifo *tekkamanfifo;
 

(1)初始化循环缓冲结构体 

struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
                gfp_t gfp_mask, spinlock_t *lock);
/*调用kfifo_init必须保证size是2的整数次幂,而且buffer只接受一个已分配好空间的指针。也就是说之前要使用kmalloc分配好空间,将返回的指针传递到buffer*/
struct kfifo *kfifo_alloc(unsigned int size, gfp_t gfp_mask,
                 spinlock_t *lock);
/*调用kfifo_alloc不必保证size是2的幂,它内部会把size向上调整到2的整数次幂。空间分配的内部实现使用kmalloc。函数内部调用kfifo_init/
 

buffer:之前要使用kmalloc分配好的空间指针;

size:循环缓冲空间大小;

gfp_mask:和kmalloc使用的分配标志(flags)一样。(参阅Linux设备驱动程序(8)-分配内存)

lock:是事先声明并初始化好的自旋锁结构体指针;

返回值 为初始化好的循环缓冲数据结构指针 。

(2) 向缓冲区里写入数据 

unsigned int kfifo_put(struct kfifo *fifo,unsigned char *buffer, unsigned int len);
unsigned int __kfifo_put(struct kfifo *fifo,unsigned char *buffer, unsigned int len);
 

fifo:要写入数据的缓冲区结构体指针;

buffer:要写入的数据指针,指向内核空间。如需要用户空间数据,之前要用copy_from_user复制数据到内核空间;

len:要写入的数据大小;

返回值 为写入缓冲区的数据字节数。

(3)从缓冲区里读出数据 

unsigned int kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len);
unsigned int __kfifo_get(struct kfifo *fifo, unsigned char *buffer, unsigned int len);
 

参数

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -