• Samsung_tiny4412(驱动笔记05)----Makefile,open,read,write,lseek,poll,ioctl,fasync


    /***********************************************************************************
     *                    
     *               Makefile,open,read,write,lseek,poll,ioctl,fasync
     *
     * 声明:
     *      1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
     *         不对齐,从而影响阅读.
     *
     *
     *                                          2015-3-9 阴 深圳 尚观 Var 曾剑锋
     **********************************************************************************/
    
                            \\\\\\\--*目录*--//////////////
                            |  一. Makefile大致写法:             
                            |  二. 获取进程task_struct的方法:    
                            |  三. open 大致写法:                
                            |  四. read 大致写法:                
                            |  五. write 大致写法:               
                            |  六. lseek 大致写法:               
                            |  七. poll 大致写法:                
                            |  八. ioctl 大致写法:               
                            |  九. close 大致写法:               
                            |  十. fasync 大致写法:              
                            |  十一. 等待队列API:                
                            |  十二. 驱动wait_queue poll fasync: 
                            |  十三. 应用wait_queue poll fasync: 
                            \\\\\\\\\///////////////////
    
    一. Makefile大致写法:
        ...
        # 定义一个CC变量,赋值为arm-linux-
        CC = arm-linux-         
        # .c文件依赖的.h文件存放的目录,这里是:当前目录下include文件夹
        INCLUDE_DIRS = include
    
        # 如果变量objs在此之前未声明定义,那么就声明定义,并赋值objs为hello.o
        # 如果变量objs在此之前已声明定义,那么把hello.o放在objs字符串后面
        objs += hello.o         
        
        # 1. hello : 下面命令要生成的目标,当然也有可能是伪指令;
        # 2. $(objs) : 依赖文件,只有依赖文件都存在的情况下才会执行下面的指令;
        # 3. $(CC)gcc $< -o $@ : 编译依赖文件产生目标文件;
        #    1. $(CC) : 取CC变量里的值;
        #    2. $<    : 依赖文件($(objs))
        #    3. $@    : 目标文件(hello)
        hello : $(objs) 
            $(CC)gcc $< -o $@
        
        # 1. 生成的目标文件简写,将当前文件下所有的.c编译成对应的.o文件;
        # 2. -I$(INCLUDE_DIRS) : .c编译时需要的头文件所在的目录
        %.o : %.c 
            $(CC)gcc -c $< -o $@ -I$(INCLUDE_DIRS)
        
        # 伪命令声明,这样即使存在clean文件,也会把clean当命令使用
        .PHONY:clean
        clean:
            rm *.o hello
        ...
    
    二. 获取进程task_struct的方法:
        ...
        int ret;
        struct thread_info *info;
        struct task_struct *task;
        
        /** 
         * 获取当前进程的进程描述符(PCB)算法,主要是因为thread_info里有task_struct指针,
         * 而thread_info放在了内核线程栈的起始地方,而内核线程栈是8k对齐,
         * 所以thread_finfo的首地址是: ((unsigned int)&ret & ~(0x2000 - 1))
         */
        info = (void *)((unsigned int)&ret & ~(0x2000 - 1));
        task = info->task;
        printk("pid = %d, [%s]
    ", task->pid, task->comm); /* task->comm 运行的程序名 */
        
        // current 当前进程的PCB指针,系统自带提供的全局变量
        printk("pid = %d, [%s]
    ", current->pid, current->comm);
        ...
    
    三. open 大致写法:
        1. int (*open) (struct inode *, struct file *);
        2. 代码模型:
            static int test_open(struct inode *inode, struct file *file) 
            { 
                /**
                 * 一般open函数里面都要把私有数据绑定到file->private_data上
                 */
                test_t *p; 
                p = container_of(file->f_op, test_t, fops); 
                file->private_data = p; 
             
                //声明当前设备不支持llseek方法调整读写位置 
                //nonseekable_open(inode, file);
                return 0; 
            } 
    
    四. read 大致写法:
        1. ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        2. 代码模型:
            static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
            {
                int ret;
                test_t *p = file->private_data;
                
                /**
                 * 检查可读数据是否足够
                 */
                if(count > BUF_SIZE - *pos)
                    count = BUF_SIZE - *pos;
                
                ret = copy_to_user(buf, p->buf + *pos, count);
                if(ret)
                    return -EFAULT;
                
                *pos += count;
            
                return count; //返回读到的数据个数
            }
    
    
    五. write 大致写法:
        1. ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        2. 代码模型:
            static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
            {
                test_t *p = file->private_data;
            
                if(p->is_empty)
                {
                    /* 进入深度睡眠其中方法中的一种 */
                    current->state = TASK_UNINTERRUPTIBLE;
                    schedule();
                }
                return count;
            }
    
    六. lseek 大致写法:
        1. loff_t (*llseek) (struct file *, loff_t, int);
        2. 2.6版本内核不实现该方法的话,使用内核默认的llseek函数,当前版本必须实现该方法;
        3. 代码模型:
            static loff_t test_llseek(struct file *file, loff_t offset, int whence)
            {                       
                loff_t pos = file->f_pos;
            
                switch(whence)
                {
                    case SEEK_SET:
                        pos = offset;
                        break;
                    case SEEK_CUR:
                        pos += offset;
                        break;
                    case SEEK_END:
                        pos = BUF_SIZE + offset;
                        break;
                    default:
                        return -EINVAL; //参数非法
                }
            
                file->f_pos = pos;
            
                //返回lseek之后的文件读写位置(相对文件开始处)
                return pos;
            }
    
    七. poll 大致写法:
        1. poll,epoll,select系统调用的后端,都用作查询对一个或多个文件描述符的读写是否阻塞
        2. unsigned int (*poll) (struct file *, struct poll_table_struct *);
        3. 代码模型:
            static unsigned int test_poll(struct file *file, struct poll_table_struct *table)
            {
                unsigned int poll = 0;
                test_t *p = file->private_data;
                /*printk("In test_poll.
    ");*/
            
                //功能: 在设备状态变化,等待队列被唤醒时,唤醒select
                poll_wait(file, &p->wq, table);
            
                //如果可读
                if(!p->is_empty)
                    poll |= POLLIN | POLLRDNORM;
            
                return poll;
            }
    
    
    八. ioctl 大致写法: 
        1. long (*unlocked_ioctl) (struct file *, unsigned int cmd, unsigned long);
        2. cmd的格式:
            ---------------------------------------
            | dir 2 | size 14   |  type 8  |  nr 8 |
            ----------------------------------------
        3. 创建ioctl命令的宏:
            #define _IO(type,nr)                _IOC(_IOC_NONE,(type),(nr),0)
            #define _IOR(type,nr,datatype)     _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(datatype)))
            #define _IOW(type,nr,datatype)     _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(datatype)))
            #define _IOWR(type,nr,datatype)    _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(datatype)))
        4. 代码模型:
            #define __SET_GET_FOO__
            #ifndef __SET_GET_FOO__
                #define SET_FOO _IOW(TYPE, NR, int)
                #define GET_FOO _IOR(TYPE, NR, int)
            #else
                #define SET_FOO _IOC(_IOC_WRITE, TYPE, NR, sizeof(int))
                #define GET_FOO _IOC(_IOC_READ, TYPE, NR, sizeof(int))
            #endif
            
            static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
            {
                int ret;
            
                switch(cmd)
                {
                    case SET_FOO:
                        ret = copy_from_user(&num, (void *)arg, sizeof(int));
                        if(ret)
                            return -EFAULT;
                        break;
                    case GET_FOO:
                        ret = copy_to_user((void *)arg, &num, sizeof(int));
                        if(ret)
                            return -EFAULT;
                        break;
                    default:
                        return -EINVAL;
                }
            
                return 0;
            }
    
    九. close 大致写法:
        1. int (*release) (struct inode *, struct file *);
        2. 代码模型:
            static int test_close(struct inode *inode, struct file *file)
            {
                return 0;
            }
    
    十. fasync 大致写法:
       1. int (*fasync) (int, struct file *, int);
       2. 前提条件:
           1. 应用层(app)设置fd的owner: fcntl(fd, F_SETOWN, getpid()); //设置fd的owner
           2. fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC);        //设置O_ASYNC标志
       3. 当使用fcntl()设置O_ASYNC时,会调用fasync()函数.
       4. 代码模型:
           static int test_fasync(int fd, struct file *file, int on)
           {
               test_t *p = file->private_data;
               printk("In test_fasync: fd = %d, on = %d
    ", fd, on);
           
               // fasync_helper自己会去申请结构体struct fasync_struct的空间,并赋值
               // 所以在这里我们只需要传入指向这个结构体的指针就行了
               return fasync_helper(fd, file, on, &p->fa);
           }
        5. 在设备状态变化时发送信号给使用异步通知的进程: kill_fasync(&p->fa, SIGIO, POLL_IN);
    
     
    十一. 等待队列API: 
        1. 定义: wait_queue_head_t wq; 
        2. 初始化: init_waitqueue_head(&wq); 
        3. 两种把当前进程加入到等待队列睡眠: 
            1. wait_event(wq, cond);               //深度睡眠,top命令下状态标志: D 
            2. wait_event_interruptible(wq, cond); //可被信号中断睡眠,top命令下状态标志: S 
        4. 两种唤醒等待队列:
            1. wake_up(&wq);               //唤醒深度睡眠,普通睡眠:D,S 
            2. wake_up_interruptible(&wq); //唤醒普通睡眠:S 
    
    十二. 驱动wait_queue poll fasync:
        cat > test.c << EOF
            #include <linux/module.h>
            #include <linux/fs.h>
            #include <linux/uaccess.h>
            #include <linux/sched.h>
            #include <linux/poll.h>
    
            //设备名,可以通过命令: cat /proc/devices
            #define DEV_NAME    "test"    
            
            struct test_s {
                struct file_operations fops;
                wait_queue_head_t wq;
                struct fasync_struct *fa;
                int major;
                bool is_empty;
            };
            typedef struct test_s test_t;
            
            static int test_open(struct inode *inode, struct file *file)
            {
                //把驱动私有数据放到file的private_data中
                test_t *p;
                p = container_of(file->f_op, test_t, fops); 
                file->private_data = p;
            
                return 0;
            }
            
            static int test_close(struct inode *inode, struct file *file)
            {
                return 0;
            }
            
            static ssize_t test_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
            {
                test_t *p = file->private_data;
                
                /**
                 * 修正能够输出的数据长度,在本Demo中没有用到.
                 * if(count > BUF_SIZE - *pos)
                 *     count = BUF_SIZE - *pos;
                 */
            
                while(p->is_empty)
                {
                    //判断当前打开文件方式是不是非阻塞方式打开的
                    if(file->f_flags & O_NONBLOCK)
                        return -EAGAIN;
            
                    //如果没有数据,那么就进入睡眠状态,等待被唤醒,同时要求条件为真: !p->is_empty
                    if(wait_event_interruptible(p->wq, !p->is_empty))
                        return -ERESTARTSYS;
                }
            
                printk("Read data.
    ");
                
                p->is_empty = true; //数据读完,重新置为空
                    
                return count; //返回数据的个数
            }
            
            /**
             * 调用这个方法请使用命令: echo 123456 > /dev/test0
             */
            static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
            {
                test_t *p = file->private_data;
            
                p->is_empty = false;    //将状态置为有数据
                printk("Write data.
    ");
            
                /**
                 * 因为下面的kill_fasync()能唤醒可中断睡眠,所以可以不需要这行代码
                 */
                //wake_up_interruptible(&p->wq);     //唤醒等待队列中的进程
    
                kill_fasync(&p->fa, SIGIO, POLL_IN); //发送SIGIO信号量,并且表示数据可读
            
                return count;
            }
            
            
            static unsigned int test_poll(struct file *file, struct poll_table_struct *table)
            {
                unsigned int poll = 0;
                test_t *p = file->private_data;
                /*printk("In test_poll.
    ");*/
            
                /**
                 * 功能: 在设备状态变化,等待队列被唤醒时,唤醒select
                 * poll()可被调用多次,也导致poll_wait()被注册多次,不过没事,正常现象
                 */
                poll_wait(file, &p->wq, table);
            
                //如果可读,将状态返回
                if(!p->is_empty)
                    poll |= POLLIN | POLLRDNORM;
            
                return poll;
            }
            
            static int test_fasync(int fd, struct file *file, int on)
            {
                test_t *p = file->private_data;
                printk("In test_fasync: fd = %d, on = %d
    ", fd, on);
            
                //注册异步事件
                return fasync_helper(fd, file, on, &p->fa);
            }
            
            struct test_s test = {
                .fops = {
                    .owner      = THIS_MODULE,
                    .open       = test_open,
                    .release    = test_close,
                    .read       = test_read,
                    .write      = test_write,
                    .poll       = test_poll,
                    .fasync     = test_fasync,
                },
                .major = 0,
                .is_empty = true,
            };
            
            int __init test_init(void)
            {
                int ret;
            
                // 初始化等待队列
                init_waitqueue_head(&test.wq);
            
                // 注册字符设备驱动
                ret = register_chrdev(test.major, DEV_NAME, &test.fops);
                if(ret > 0)
                {
                    test.major = ret;
                    printk("major = %d
    ", test.major);
                    ret = 0;
                }
            
                return ret;
            }
            
            
            void __exit test_exit(void)
            {
                // 卸载字符设备驱动
                unregister_chrdev(test.major, DEV_NAME);
            }
            
            module_init(test_init);
            module_exit(test_exit);
            
            MODULE_LICENSE("GPL");
        EOF
    
    十三. 应用wait_queue poll fasync:
        cat > app.c << EOF
            #include <stdio.h>
            #include <string.h>
            #include <stdlib.h>
            #include <unistd.h>
            #include <fcntl.h>
            #include <signal.h>
            
            int fd;
            
            void sig_handler(int sig)
            {
                int ret;
                char buf[1024];
                if(sig == SIGIO)
                    printf("got a signal SIGIO
    ");
            
                /**
                 * ret = read(fd, buf, sizeof(buf));
                 * if(-1 == ret)
                 *     perror("read");
                 */
            }
            
            int main(int argc, char **argv)
            {
                int ret;
                fd_set fds;
                char buf[1024] = "hello";
            
                signal(SIGIO, sig_handler);
            
                fd = open(argv[1], O_RDWR);
                if(-1 == fd)
                {
                    perror("open");
                    exit(1);
                }
                
                fcntl(fd, F_SETOWN, getpid());                    //设置fd的owner
                fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_ASYNC); //设置O_ASYNC标志
            
                while(1)
                {
                    ret = read(fd, buf, 1024);
                    if (-1 == ret ) {
                        perror("read");
                    }
                }
            
                close(fd);
                return 0;
            }
        EOF
  • 相关阅读:
    页面登陆系统--带文字验证码
    Django-form表单
    Ajax 异步局部刷新
    Django认证系统auth认证
    cookie与session
    使用lorem在HTML中生成随机文本
    request模块
    java——第五天-面向对象OOP
    java——第四天
    java——第三天
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4328401.html
Copyright © 2020-2023  润新知