首页 > 代码库 > 【Linux驱动】文件描述符以及相关知识

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

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函数进行选择。