Linux 文件系统与设备文件

来源:互联网 发布:unity3d播放视频卡帧 编辑:程序博客网 时间:2024/05/22 05:05

概述

字符设备和块设备都良好的体现了“一切都是文件”的设计思想,掌握Linux文件系统、设备文件系统的知识就显得相当重要了

Linux 文件操作

  • int creat(const char *pathname, mode_t mode); 创建文件
  • int open( const char *pathname, int flags, mode_t mode); 打开文件
  • ssize_t read(int fd, void *buf, size_t count); 读文件
  • ssize_t write(int fd, const void *buf, size_t count); 写文件
  • off_t lseek(int fildes, off_t offset, int whence); 定位文件
  • int close(int fd); 关闭文件

C 文件操作

  • FILE *fopen(const char *path, const char *mode); 打开文件
  • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 读文件
  • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 写文件
  • int fseek(FILE *stream, long offset, int whence); 定位文件
  • int fclose(FILE *stream); 关闭文件

基础示例

【代码仓库】

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <string.h>#define LENGTH 100void linux_file_sample(void){    int fd, len;    char str[LENGTH];    fd = op den("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);    if(fd)    {        write(fd, "Hello World fromlinux_file_sample\n",strlen("Hello World from linux_file_sample\n"));        close(fd);    }    fd = open("hello.txt", O_RDWR);    len = read(fd, str, LENGTH);    str[len] = '\0';    printf("%s\n", str);    close(fd);  }void c_file_sample(void){    FILE *fd;    char str[LENGTH];    fd = fopen("hello.txt", "w+");    if(fd)    {        fputs("Hello World form c_file_sample\n", fd);        fclose(fd);    }    fd = fopen("hello.txt", "r");    fgets(str, LENGTH, fd);    printf("%s\n", str);    fclose(fd);}int main(void){    linux_file_sample();    c_file_sample();    return 0;}

Makefile

# 可执行文件  TARGET=file  # C文件  SRCS = file.c  # 目标文件  OBJS = $(SRCS:.c=.o)  # 指令编译器和选项  CC=gcc  CFLAGS=-Wall -std=gnu99  $(TARGET):$(OBJS)  #   @echo TARGET:$@  #   @echo OBJECTS:$^      $(CC) -o $@ $^  clean:      rm -rf $(TARGET) $(OBJS)  %.o:%.c      $(CC) $(CFLAGS) -o $@ -c $< 

运行

$make$./fileHello World from linux_file_sampleHello World form c_file_sample

Linux文件系统与设备驱动

image

上图描述了Linux中虚拟文件系统、磁盘/Flash文件系统及一般的设备文件与设备驱动程序之间的关系。
应用程序和VFS之间的接口是系统调用,而VFS与文件系统以及设备文件之间的接口是file_operations结构体成员函数,这个结构体包含对文件进行打开、关闭、读写、控制的一些列成员函数.

file结构体

file 结构体代表一个打开的文件,系统中每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核和驱动源代码中,struct file的指针通常被命名为file或filep。

// include/linux/fs.h:line 835struct file {    union {        struct llist_node   fu_llist;        struct rcu_head     fu_rcuhead;    } f_u;    struct path     f_path;    struct inode        *f_inode;   /* cached value */    const struct file_operations    *f_op;    /*     * Protects f_ep_links, f_flags.     * Must not be taken from IRQ context.     */    spinlock_t      f_lock;    atomic_long_t       f_count;    unsigned int        f_flags;    fmode_t         f_mode;    struct mutex        f_pos_lock;    loff_t          f_pos;    struct fown_struct  f_owner;    const struct cred   *f_cred;    struct file_ra_state    f_ra;    u64         f_version;#ifdef CONFIG_SECURITY    void            *f_security;#endif    /* needed for tty driver, and maybe others */    void            *private_data;#ifdef CONFIG_EPOLL    /* Used by fs/eventpoll.c to link all the hooks to this file */    struct list_head    f_ep_links;    struct list_head    f_tfile_llink;#endif /* #ifdef CONFIG_EPOLL */    struct address_space    *f_mapping;} __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */

inode结构体

VFS inode包含文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息。它是Linux管理文件系统的最基本单位,也是文件系统连接任何子目录、文件的桥梁。

// include/linux/fs.h:line 554/* * Keep mostly read-only and often accessed (especially for * the RCU path lookup and 'stat' data) fields at the beginning * of the 'struct inode' */struct inode {    umode_t         i_mode;    unsigned short      i_opflags;    kuid_t          i_uid;    kgid_t          i_gid;    unsigned int        i_flags;#ifdef CONFIG_FS_POSIX_ACL    struct posix_acl    *i_acl;    struct posix_acl    *i_default_acl;#endif    const struct inode_operations   *i_op;    struct super_block  *i_sb;    struct address_space    *i_mapping;#ifdef CONFIG_SECURITY    void            *i_security;#endif    /* Stat data, not accessed from path walking */    unsigned long       i_ino;    /*     * Filesystems may only read i_nlink directly.  They shall use the     * following functions for modification:     *     *    (set|clear|inc|drop)_nlink     *    inode_(inc|dec)_link_count     */    union {        const unsigned int i_nlink;        unsigned int __i_nlink;    };    dev_t           i_rdev;    loff_t          i_size;    struct timespec     i_atime;    struct timespec     i_mtime;    struct timespec     i_ctime;    spinlock_t      i_lock; /* i_blocks, i_bytes, maybe i_size */    unsigned short          i_bytes;    unsigned int        i_blkbits;    blkcnt_t        i_blocks;#ifdef __NEED_I_SIZE_ORDERED    seqcount_t      i_size_seqcount;#endif    /* Misc */    unsigned long       i_state;    struct rw_semaphore i_rwsem;    unsigned long       dirtied_when;   /* jiffies of first dirtying */    unsigned long       dirtied_time_when;    struct hlist_node   i_hash;    struct list_head    i_io_list;  /* backing dev IO list */#ifdef CONFIG_CGROUP_WRITEBACK    struct bdi_writeback    *i_wb;      /* the associated cgroup wb */    /* foreign inode detection, see wbc_detach_inode() */    int         i_wb_frn_winner;    u16         i_wb_frn_avg_time;    u16         i_wb_frn_history;#endif    struct list_head    i_lru;      /* inode LRU list */    struct list_head    i_sb_list;    struct list_head    i_wb_list;  /* backing dev writeback list */    union {        struct hlist_head   i_dentry;        struct rcu_head     i_rcu;    };    u64         i_version;    atomic_t        i_count;    atomic_t        i_dio_count;    atomic_t        i_writecount;#ifdef CONFIG_IMA    atomic_t        i_readcount; /* struct files open RO */#endif    const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */    struct file_lock_context    *i_flctx;    struct address_space    i_data;    struct list_head    i_devices;    union {        struct pipe_inode_info  *i_pipe;        struct block_device *i_bdev;        struct cdev     *i_cdev;        char            *i_link;        unsigned        i_dir_seq;    };    __u32           i_generation;#ifdef CONFIG_FSNOTIFY    __u32           i_fsnotify_mask; /* all events this inode cares about */    struct fsnotify_mark_connector __rcu    *i_fsnotify_marks;#endif#if IS_ENABLED(CONFIG_FS_ENCRYPTION)    struct fscrypt_info *i_crypt_info;#endif    void            *i_private; /* fs or device private pointer */};

字符设备

字符设备是能够像字节流(类似文件)一样被访问的设备,有字符设备驱动程序来实现这种特性。字符设备驱动程序通常至少要实现open、close、read、write系统调用。字符设备可以通过文件系统节点来访问,这些设备文件和普通文件之间的唯一差别在于对普通文件的访问可以前后移动访问位置,而大多数字符设备是一个只能顺序访问的数据通道。一个字符设备是一种字节流设备,对设备的存取只能按顺序按字节的存取而不能随机访问,字符设备没有请求缓冲区,所有的访问请求都是按顺序执行的。但事实上现在一些高级字符设备也可以从指定位置一次读取一块数据。

块设备

块设备也是通过设备节点来访问。块设备上能够容纳文件系统。在大多数unix系统中,进行I/O操作时块设备每次只能传输一个或多个完整的块,而每块包含512字节(或更2的更高次幂字节的数据)。linux可以让应用程序向字符设备一样读写块设备,允许一次传递任意多字节的数据。因而,块设备和字符设备的区别仅仅在于内核内部管理数据的方式,也就是内核及驱动程序之间的软件接口,而这些不同对用户来讲是透明的。在内核中,和字符驱动程序相比,块驱动程序具有完全不同的接口。存储设备一 般属于块设备,块设备有请求缓冲区,并且支持随机访问而不必按照顺序去存取数据,比如你可以 先存取后面的数据,然后在存取前面的数据,这对字符设备来说是不可能的。Linux下的磁盘设备都是块设备,尽管在Linux下有块设备节点,但应用程序一般是通过文件系统及其高速缓存来访问块设备的,而不是直接通过设备节点来读写块设备上的数据。

网络设备

网络设备不同于字符设备和块设备,它是面向报文的而不是面向流的,它不支持随机访问,也没有请求缓冲区。由于不是面向流的设备,因此将网络接口映射到文件系统中的节点比较困难。内核和网络设备驱动程序间的通讯,完全不同于内核和字符以及块驱动程序之间的通讯,内核调用一套和数据包传输相关的函数而不是read,write。网络接口没有像字符设备和块设备一样的设备号,只有一个唯一的名字,如eth0、eth1等,而这个名字也不需要与设备文件节点对应。

字符设备与块设备的区别

  • 字符设备是面向流的,最小访问单位是字节;而块设备是面向块的,最小访问单位是512字节或2的更高次幂。
  • 字符设备只能顺序按字节访问,而块设备可随机访问。
  • 块设备上可容纳文件系统,访问形式上,字符设备通过设备节点访问,而块设备虽然也可通过设备节点访问,但一般是通过文件系统来访问数据的。

网络设备没有设备节点是因为网络设备是面向报文的,很难实现相关read、write等文件读写函数。所以驱动的实现也与字符设备和块设备不同。

学习资源

  • 《Linux C API 参考手册》
  • 《Linux 设备驱动开发详解》
阅读全文
0 0