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]\n", task->pid, task->comm); /* task->comm 运行的程序名 */
    
    // current 当前进程的PCB指针,系统自带提供的全局变量
    printk("pid = %d, [%s]\n", 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.\n");*/
        
            //功能: 在设备状态变化,等待队列被唤醒时,唤醒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\n", 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.\n");
            
            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.\n");
        
            /**
             * 因为下面的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.\n");*/
        
            /**
             * 功能: 在设备状态变化,等待队列被唤醒时,唤醒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\n", 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\n", 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\n");
        
            /**
             * 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

 

上一篇:Debian For ARM mysql-server install information


下一篇:ecshop网站漏洞如何修复针对于外贸网站的漏洞修复