【Linux驱动】文件描述符以及相关知识

来源:互联网 发布:钢结构棚子算法 编辑:程序博客网 时间:2024/06/07 23:53

1、文件描述符

Linux操作系统中,几乎所有的设备都被抽象成为设备文件。因此,当我们想对设备进行操作的时候可以直接去操作其相应的设备文件。设备文件即是文件,要想对文件进行操作,无非就是:打开文件、关闭文件、写入数据、读出数据等,它们分别对应的函数有open(),close(),write(),read(),就以其中的open()函数做一个分析。open函数的作用是打开一个文件。

(1)它的定义:int open( const char * pathname, int flags);
                              int open( const char * pathname,int flags, mode_t mode);

对于 open 函数来说,第三个参数(...)仅当创建新文件时才使用,用于指定文件的访问权限。

pathname 是待打开/创建文件的路径名;

oflag 用于指定文件的打开/创建模式,这个参数可由以下常量(定义于 fcntl.h)通过逻辑或构成。

常用的三种模式:

O_RDONLY       只读模式 

 O_WRONLY      只写模式 

 O_RDWR          读写模式

这三种模式是不可以同时使用的。

(2)返回值:成功后就返回文件描述符(整型变量0~255),不成功就返回-1。

那么到底什么是文件描述符??它的作用是什么?

文件描述符其实就是一个整数值,这个值的范围 在0~255之间。当程序新建或者打开一个文件的时候,内核就会向该进程返回一个文件描述符。内核可以通过文件描述符找到相应的文件。

2、文件描述符表

那么内核是怎样通过文件描述符找到相应文件的呢??

在Linux内核中,每个进程有一个task_struct结构体来维护相关的进程,被称为进程描述符。它定义在linux源码包跟目录下的/include/linux/sched.h文件中。每个进程PCB(Process Control Block)即进程控制块中都保存着一份文件描述符表。在task_struct结构体中的代码为struct files_struct *files,截取部分task_struct结构体的代码:


其中蓝色部分为 files_struct的结构体指针变量,用于指向文件描述符表

下面是files_struct结构体的定义:

struct files_struct {atomic_t count; /* 共享该表的进程数 */rwlock_t file_lock; /* 保护以下的所有域,以免在tsk->alloc_lock中的嵌套*/int max_fds; /*当前文件对象的最大数*/int max_fdset; /*当前文件描述符的最大数*/int next_fd; /*已分配的文件描述符加1*/struct file ** fd; /* 指向文件对象指针数组的指针 */fd_set *close_on_exec; /*指向执行exec( )时需要关闭的文件描述符*/fd_set *open_fds; /*指向打开文件描述符的指针*/fd_set close_on_exec_init;/* 执行exec( )时需要关闭的文件描述符的初 值集合*/        fd_set open_fds_init; /*文件描述符的初值集合*/<span style="color:#ff0000;">struct file * fd_array[32];/* 文件对象指针的初始化数组*/</span>};
在files_struct结构体的定义中,可以看到struct file *fd_array[32];这句代码的作用是用来指向file结构体的。还可以看到指向文件对象的指针是一个数组。文件描述符就是文件描述符表的索引或者是该数组的下标,文件描述表中每个表项都有一个指向已打开文件的指针,已打开的文件在内核中用file结构体表示文件描述符表中的指针指向file结构体


3、file结构体

struct file     {     struct list_head        f_list;    /*所有打开的文件形成一个链表*/     struct dentry           *f_dentry; /*指向相关目录项的指针*/     struct vfsmount         *f_vfsmnt; /*指向VFS安装点的指针*/    <span style="color:#ff0000;"> struct file_operations  *f_op;     /*指向文件操作表的指针*/ </span>    mode_t f_mode;                                  /*文件的打开模式*/     loff_t f_pos;                                   /*文件的当前位置*/     unsigned short f_flags;                         /*打开文件时所指定的标志*/     unsigned short f_count;                           /*使用该结构的进程数*/     unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;     /*预读标志、要预读的最多页面数、上次预读后的文件指针、预读的字节数以及预读的页面数*/     int f_owner;                  /* 通过信号进行异步I/O数据的传送*/     unsigned int         f_uid, f_gid;  /*用户的UID和GID*/     int                 f_error;       /*网络写操作的错误码*/          unsigned long f_version;           /*版本号*/     void *private_data;                      /* tty驱动程序所需 */    };  
从上面可以知道,文件描述符表中的指针指向file结构体,从file结构体的定义中有可以知道它指向一个file_operations结构体,这个结构体的成员都是函数指针,指向实现各种文件操作的内核函数。
file_operations结构体定义如下:(头文件 linux/fs.h中定义

struct file_operations {struct module *owner;loff_t(*llseek) (struct file *, loff_t, int);ssize_t(*read) (struct file *, char __user *, size_t, loff_t *);ssize_t(*aio_read) (struct kiocb *, char __user *, size_t, loff_t);ssize_t(*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t(*aio_write) (struct kiocb *, const char __user *, size_t,         loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);int (*ioctl) (struct inode *, struct file *, unsigned int,        unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, struct dentry *, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);ssize_t(*readv) (struct file *, const struct iovec *, unsigned long,     loff_t *);ssize_t(*writev) (struct file *, const struct iovec *, unsigned long,      loff_t *);ssize_t(*sendfile) (struct file *, loff_t *, size_t, read_actor_t,        void __user *);ssize_t(*sendpage) (struct file *, struct page *, int, size_t,        loff_t *, int);unsigned long (*get_unmapped_area) (struct file *, unsigned long,         unsigned long, unsigned long,         unsigned long);};
比如在用户程序中read一个文件描述符,read通过系统调用进入内核,然后找到这个文件描述符所指向的file结构体,找到file结构体所指向的file_operations结构体,调用它的read成员所指向的内核函数以完成用户请求。在用户程序中调用lseek、read、write、ioctl、open等函数,最终都由内核调用file_operations的各成员所指向的内核函数完成用户请求。

它们的大体过程应该:


4、ioctl()函数

ioctl的用处:一些没办法归类的函数就统一放在ioctl这个函数操作中,通过指定的命令来实现对应的操作。所以,ioctl函数里面都实现了多个的对硬件的操作,通过应用层传入的命令来调用相应的操作。一般在这个函数中会有一个switch……case函数进行选择。







2 0