7-1、设备ioctl控制

7-2、内核等待队列

7-3、阻塞型字符设备驱动

7-4、poll设备操作

7-5、自动创建设备文件

 

7-2、内核等待队列

在linux中,可以使用等待队列来实现进程的阻塞睡眠,可以把队列看做是保存容器中,阻塞进程形成一个队列。唤醒是从队列中取出来。

1、创建一个队列:

wait_queue_head_t my_queue;

2、初始化等待队列

init_waitqueue_head(&my_queue);

3、定义并初始化等待队列

DECLARE_WAIT_QUEUE_HEAD(my_queue);

4、有条件的睡眠:

wait_event(queue,condition)

当condition(一个布尔表达式)为真时,立即返回。否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠。并挂在queue参数所指令的等待队列上。

wait_event_interruptible(queue, condition)

当condition(一个布尔表达式)为真时,立即放回。否则让进程进入TASK_INTERRUPTIBLE模式。并挂在queue参数所指令的等待队列上。

int wait_event(wait_queue_t queue, condition)

当condition(一个布尔表达式)为真时,立即放回。否则让进程进入TASK_KILLABLE模式。并挂在queue参数所指令的等待队列上。

5、无条件睡眠

interruptible_sleep_on(wait_queue_head_t *q)

让进程无条件进入可中断的睡眠,并放入q队列中。

 

6、从等待队列中唤醒进程

wake_up(wait_queue_t *q)

从等待队列中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE的所有进程。

wake_up_interruptible(wait_queue_t *q)

从等待嘟列中唤醒状态为TASK_INTERRUPTIBLE的进程

 

7-3、阻塞型字符设备驱动(重点)

简介:即通过上面的内核等待队列接口实现用户态调用read时阻塞。用于当内核态没有数据的时候实现进程的休眠功能。用户态可以通过read()函数的O_NONBLOCK参数指定读操作是否发生阻塞。

 memdev.c

#ifndef __KERNEL__
#define __KERNEL__
#endif

#ifndef MODULE
#define MODULE
#endif

#include <linux/config.h>
#include <linux/module.h>

#include <linux/kernel.h>   
#include <linux/init.h>     
#include <linux/slab.h>   
#include <linux/fs.h>       
#include <linux/errno.h>    
#include <linux/types.h>    
#include <linux/proc_fs.h>

#include <asm/system.h>  
#include <asm/uaccess.h>   /*使用copy_to_user,copy_from_user必须要包含该文件*/

#include "memdev.h"
MODULE_LICENSE("GPL");

/*设备打开*/
int memdev_open(struct inode *inode, struct file *filp)
{
    Mem_Dev *dev;
    
    /*获取次设备号*/
    int num = MINOR(inode->i_rdev);

    dev = (Mem_Dev *)filp->private_data;
    if (!dev) 
    {
        if (num >= MEMDEV_NR_DEVS) 
            return -ENODEV;
        dev = &mem_devices[num];
        filp->private_data = dev; 
    }
    
    /*增加模块引用计数*/
    MOD_INC_USE_COUNT; 
    
    return 0;          
}

/*设备关闭*/
int memdev_release(struct inode *inode, struct file *filp)
{
    MOD_DEC_USE_COUNT;
    return 0;
}
/*设备读操作*/
ssize_t memdev_read(struct file *filp, char *buf, size_t count,loff_t *f_pos)
{
    Mem_Dev *dev = filp->private_data; 
    int pos = *f_pos;
    ssize_t ret = 0;
        
  //为什么这里要用while??防止不期望的的中断发生唤醒
while (dev->rp == dev->wp) /* 没有数据可读,考虑为什么不用if,而用while */ { if (filp->f_flags & O_NONBLOCK) return -EAGAIN;    interruptible_sleep_on(&(dev->inq));
     //获取使用有条件睡眠:wait_event_interruptible(dev->inq,have_data); }
/* 判断读写数据多少 */ count = min(count, dev->wp - dev->rp); /*读数据到用户空间*/ if (copy_to_user(buf, &(dev->data[pos]), count)) { ret = -EFAULT; goto out; } *f_pos += count; dev->rp += count; ret = count; out: return ret; } /*文件写操作*/ ssize_t memdev_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) { Mem_Dev *dev = filp->private_data; int pos = *f_pos; ssize_t ret = -ENOMEM; /*判断写位置是否有效*/ if (dev->wp + count > dev->size) count = dev->size - dev->wp; /*从用户空间写入数据*/ if (copy_from_user(&(dev->data[pos]), buf, count)) { ret = -EFAULT; goto out; } /* 唤醒读进程 */ wake_up(&(dev->inq)); *f_pos += count; dev->wp += count; ret = count; out: return ret; } /*文件定位*/ loff_t memdev_llseek(struct file *filp, loff_t off, int whence) { Mem_Dev *dev = filp->private_data; loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = off; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + off; break; case 2: /* SEEK_END */ newpos = dev->size -1 + off; break; default: /* can't happen */ return -EINVAL; } if (newpos<0) return -EINVAL; filp->f_pos = newpos; return newpos; } /* * The following wrappers are meant to make things work with 2.0 kernels */ struct file_operations memdev_fops = { llseek: memdev_llseek, read: memdev_read, write: memdev_write, open: memdev_open, release: memdev_release, }; /*加载函数*/ int memdev_init_module(void) { int result, i; /*设置模块owner*/ SET_MODULE_OWNER(&memdev_fops); /*注册字符设备*/ result = register_chrdev(memdev_major, "memdev", &memdev_fops); if (result < 0) { printk(KERN_WARNING "mem: can't get major %d\n",memdev_major); return result; } if (memdev_major == 0) memdev_major = result; /*为设备描述结构分配内存*/ mem_devices = kmalloc(MEMDEV_NR_DEVS * sizeof(Mem_Dev), GFP_KERNEL); if (!mem_devices) { result = -ENOMEM; goto fail; } memset(mem_devices, 0, MEMDEV_NR_DEVS * sizeof(Mem_Dev)); /*为设备分配内存*/ for (i=0; i < MEMDEV_NR_DEVS; i++) { mem_devices[i].size = MEMDEV_SIZE; mem_devices[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL); memset(mem_devices[i].data, 0, MEMDEV_SIZE); /*初始化等待队列*/ init_waitqueue_head(&(mem_devices[i].inq)); } return 0; fail: memdev_cleanup_module(); return result; } /*卸载函数*/ void memdev_cleanup_module(void) { int i; /*注销字符设备*/ unregister_chrdev(memdev_major, "memdev"); /*释放内存*/ if (mem_devices) { for (i=0; i<MEMDEV_NR_DEVS; i++) kfree(mem_devices[i].data); kfree(mem_devices); } } module_init(memdev_init_module); module_exit(memdev_cleanup_module);

 

memdev.h

#ifndef _MEMDEV_H_
#define _MEMDEV_H_

#include <linux/ioctl.h>

#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0   
#endif

#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 3    
#endif

#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif


typedef struct Mem_Dev {
   char *data;
   struct Mem_Dev *next;   /* next listitem */
   unsigned long size;  
   unsigned long wp, rp;  /* 读写数据的位置 */
   struct semaphore sem;    /* 定义信号量 */
   wait_queue_head_t inq;  /* 不要使用指针方式,否则要用kmalloc先为其分配空间 */
} Mem_Dev;

/* 定义幻数 */
#define MEMDEV_IOC_MAGIC  'k'


/* 定义命令 */
#define MEMDEV_IOCPRINT   _IO(MEMDEV_IOC_MAGIC, 1)
#define MEMDEV_IOCGETDATA _IOR(MEMDEV_IOC_MAGIC, 2, int)
#define MEMDEV_IOCSETDATA _IOW(MEMDEV_IOC_MAGIC, 3, int)


#define MEMDEV_IOC_MAXNR 3

#endif /* _MEMDEV_H_ */

 

Makefile

# The path of kernel source code
INCLUDEDIR = /home/study/part2/lesson3/linux-2.4.18/include/

# Compiler
CC = arm-linux-gcc

# Options
CFLAGS = -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -Wno-trigraphs -Os \
-mapcs -fno-strict-aliasing -fno-common -fno-common -pipe \
-mapcs-32 -march=armv4 -mtune=arm9tdmi -mshort-load-bytes \
-msoft-float  -I$(INCLUDEDIR)

# Target
OBJS = memdev.o

all: $(OBJS)

$(OBJS): memdev.c memdev.h
    $(CC) $(CFLAGS) -c $<
    $(CC) app-read.c -static -o app-read
    $(CC) app-write.c -static -o app-write        
    
install:
    insmod $(OBJS)

uninstall:
    rmmod memdev

.PHONY: clean
clean:
    rm -f *.o
    rm -f app-*

 

Read.c write.c

#include <stdio.h>

int main()
{
    FILE *fp = NULL;
    char Buf[128];
    
    /*初始化Buf*/
    strcpy(Buf,"memdev is char dev!");
    printf("BUF: %s\n",Buf);
    
    /*打开设备文件*/
    fp = fopen("/dev/memdev","r+");
    if (fp == NULL)
    {
        printf("Open memdev Error!\n");
        return -1;
    }
    
    /*清除Buf*/
    strcpy(Buf,"Buf is NULL!");
    printf("Read BUF1: %s\n",Buf);
    
    /*读出数据*/
    fread(Buf, sizeof(Buf), 1, fp);
    
    /*检测结果*/
    printf("Read BUF2: %s\n",Buf);
    
    fclose(fp);
    
    return 0;    

}

write.c
#include <stdio.h>

int main()
{
    FILE *fp = NULL;
    char Buf[128];
    
    
    /*打开设备文件*/
    fp = fopen("/dev/memdev","r+");
    if (fp == NULL)
    {
        printf("Open Dev memdev Error!\n");
        return -1;
    }
    
    /*写入设备*/
    strcpy(Buf,"memdev is char dev!");
    printf("Write BUF: %s\n",Buf);
    fwrite(Buf, sizeof(Buf), 1, fp);
    
    sleep(5);
    fclose(fp);

    return 0;
}