栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

文件I/O

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

文件I/O

一、fd是什么

    1.为什么读写文件要先open()?

看下面代码,通常我们读写文件,都是要先open,然后读写,处理完业务之后还有close一下。

fd = open(pathname, flags, mode);
rlen = read(fd, buf, count);
wlen = write(fd, buf, count);
close(fd);

为什么读写文件都要先open()一下,读写完又要close()呢?

以前老师只告诉我们要open和一定一定要close,有时候我们忘记close代码也不报错,一样正常运行。

为什么?希望我们带着好奇心,往下寻找答案

2.要理解文件描述符fd,要从启动进程开始

先看图,网友画的特别好,借用一下

  (1)启动进程,系统会创建进程控制块来存储进程信息,task_struct,产生一个pid,叫进程描述符。

struct task_struct {

   //打开文件信息
   struct files_struct *files;

   volatile long state;  //说明了该进程是否可以执行,还是可中断等信息
   unsigned long flags;  //Flage 是进程号,在调用fork()时给出
   int sigpending;    //进程上是否有待处理的信号
   mm_segment_t addr_limit; //进程地址空间,区分内核进程与普通进程在内存存放的位置不同
   //0-0xBFFFFFFF for user-thead
   //0-0xFFFFFFFF for kernel-thread
   //调度标志,表示该进程是否需要重新调度,若非0,则当从内核态返回到用户态,会发生调度
   volatile long need_resched;
   int lock_depth;  //锁深度
   long nice;       //进程的基本时间片
   //进程的调度策略,有三种,实时进程:SCHED_FIFO,SCHED_RR, 分时进程:SCHED_OTHER
   unsigned long policy;
   struct mm_struct *mm; //进程内存管理信息
   int processor;
   //若进程不在任何CPU上运行, cpus_runnable 的值是0,否则是1 这个值在运行队列被锁时更新
   unsigned long cpus_runnable, cpus_allowed;
   struct list_head run_list; //指向运行队列的指针
   unsigned long sleep_time;  //进程的睡眠时间
   //用于将系统中所有的进程连成一个双向循环链表, 其根是init_task
   struct task_struct *next_task, *prev_task;
   struct mm_struct *active_mm;
   struct list_head local_pages;       //指向本地页面
   unsigned int allocation_order, nr_local_pages;
   struct linux_binfmt *binfmt;  //进程所运行的可执行文件的格式
   int exit_code, exit_signal;
   int pdeath_signal;     //父进程终止时向子进程发送的信号
   unsigned long personality;
   //Linux可以运行由其他UNIX操作系统生成的符合iBCS2标准的程序
   int did_exec:1;
   pid_t pid;    //进程标识符,用来代表一个进程
   pid_t pgrp;   //进程组标识,表示进程所属的进程组
   pid_t tty_old_pgrp;  //进程控制终端所在的组标识
   pid_t session;  //进程的会话标识
   pid_t tgid;
   int leader;     //表示进程是否为会话主管
   struct task_struct *p_opptr,*p_pptr,*p_cptr,*p_ysptr,*p_osptr;
   struct list_head thread_group;   //线程链表
   struct task_struct *pidhash_next; //用于将进程链入HASH表
   struct task_struct **pidhash_pprev;
   wait_queue_head_t wait_chldexit;  //供wait4()使用
   struct completion *vfork_done;  //供vfork() 使用
   unsigned long rt_priority; //实时优先级,用它计算实时进程调度时的weight值

   //it_real_value,it_real_incr用于REAL定时器,单位为jiffies, 系统根据it_real_value
   //设置定时器的第一个终止时间. 在定时器到期时,向进程发送SIGALRM信号,同时根据
   //it_real_incr重置终止时间,it_prof_value,it_prof_incr用于Profile定时器,单位为jiffies。
   //当进程运行时,不管在何种状态下,每个tick都使it_prof_value值减一,当减到0时,向进程发送
   //信号SIGPROF,并根据it_prof_incr重置时间.
   //it_virt_value,it_virt_value用于Virtual定时器,单位为jiffies。当进程运行时,不管在何种
   //状态下,每个tick都使it_virt_value值减一当减到0时,向进程发送信号SIGVTALRM,根据
   //it_virt_incr重置初值。
   unsigned long it_real_value, it_prof_value, it_virt_value;
   unsigned long it_real_incr, it_prof_incr, it_virt_value;
   struct timer_list real_timer;   //指向实时定时器的指针
   struct tms times;      //记录进程消耗的时间
   unsigned long start_time;  //进程创建的时间
   //记录进程在每个CPU上所消耗的用户态时间和核心态时间
   long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
   //内存缺页和交换信息:
   //min_flt, maj_flt累计进程的次缺页数(Copy on Write页和匿名页)和主缺页数(从映射文件或交换
   //设备读入的页面数); nswap记录进程累计换出的页面数,即写到交换设备上的页面数。
   //cmin_flt, cmaj_flt, cnswap记录本进程为祖先的所有子孙进程的累计次缺页数,主缺页数和换出页面数。
   //在父进程回收终止的子进程时,父进程会将子进程的这些信息累计到自己结构的这些域中
   unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
   int swappable:1; //表示进程的虚拟地址空间是否允许换出
   //进程认证信息
   //uid,gid为运行该进程的用户的用户标识符和组标识符,通常是进程创建者的uid,gid
   //euid,egid为有效uid,gid
   //fsuid,fsgid为文件系统uid,gid,这两个ID号通常与有效uid,gid相等,在检查对于文件
   //系统的访问权限时使用他们。
   //suid,sgid为备份uid,gid
   uid_t uid,euid,suid,fsuid;
   gid_t gid,egid,sgid,fsgid;
   int ngroups; //记录进程在多少个用户组中
   gid_t groups[NGROUPS]; //记录进程所在的组
   //进程的权能,分别是有效位集合,继承位集合,允许位集合
   kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
   int keep_capabilities:1;
   struct user_struct *user;
   struct rlimit rlim[RLIM_NLIMITS];  //与进程相关的资源限制信息
   unsigned short used_math;   //是否使用FPU
   char comm[16];   //进程正在运行的可执行文件名
   //文件系统信息
   int link_count, total_link_count;
   //NULL if no tty 进程所在的控制终端,如果不需要控制终端,则该指针为空
   struct tty_struct *tty;
   unsigned int locks;
   //进程间通信信息
   struct sem_undo *semundo;  //进程在信号灯上的所有undo操作
   struct sem_queue *semsleeping; //当进程因为信号灯操作而挂起时,他在该队列中记录等待的操作
   //进程的CPU状态,切换时,要保存到停止进程的task_struct中
   struct thread_struct thread;
   //文件系统信息
   struct fs_struct *fs;

   //信号处理函数
   spinlock_t sigmask_lock;
   struct signal_struct *sig; //信号处理函数
   sigset_t blocked;  //进程当前要阻塞的信号,每个信号对应一位
   struct sigpending pending;  //进程上是否有待处理的信号
   unsigned long sas_ss_sp;
   size_t sas_ss_size;
   int (*notifier)(void *priv);
   void *notifier_data;
   sigset_t *notifier_mask;
   u32 parent_exec_id;
   u32 self_exec_id;

   spinlock_t alloc_lock;
   void *journal_info;
}
struct files_struct {

   
   int next_fd;
   
   struct file __rcu * fd_array[NR_OPEN_DEFAULT];

   
   atomic_t count;
   
   
   
   spinlock_t file_lock ____cacheline_aligned_in_smp;

   
   struct embedded_fd_set close_on_exec_init;
   
   struct embedded_fd_set open_fds_init;
   
   struct address_space    *i_mapping;

   #ifdef CONFIG_SECURITY
   void            *i_security;
   #endif

   
   unsigned long       i_ino;//inode号
   
   union {
   const unsigned int i_nlink;//硬链接个数
   unsigned int __i_nlink;
   };
   dev_t           i_rdev;//如果inode代表设备,i_rdev表示该设备的设备号
   loff_t          i_size;//文件大小
   struct timespec     i_atime;//最近一次访问文件的时间
   struct timespec     i_mtime;//最近一次修改文件的时间
   struct timespec     i_ctime;//最近一次修改inode的时间
   spinlock_t      i_lock; 
   unsigned short          i_bytes;//文件中位于最后一个块的字节数
   unsigned int        i_blkbits;//以bit为单位的块的大小
   blkcnt_t        i_blocks;//文件使用块的数目

   #ifdef __NEED_I_SIZE_ORDERED
   seqcount_t      i_size_seqcount;//对i_size进行串行计数
   #endif

   
   unsigned long       i_state;//inode状态,可以是I_NEW,I_LOCK,I_FREEING等
   struct mutex        i_mutex;//保护inode的互斥锁

   //inode第一次为脏的时间 以jiffies为单位
   unsigned long       dirtied_when;   

   struct hlist_node   i_hash;//散列表
   struct list_head    i_wb_list;  
   struct list_head    i_lru;      
   struct list_head    i_sb_list;//超级块链表
   union {
   struct hlist_head   i_dentry;//所有引用该inode的目录项形成的链表
   struct rcu_head     i_rcu;
   };
   u64         i_version;//版本号 inode每次修改后递增
   atomic_t        i_count;//引用计数
   atomic_t        i_dio_count;
   atomic_t        i_writecount;//记录有多少个进程以可写的方式打开此文件
   const struct file_operations    *i_fop; 
   struct file_lock    *i_flock;//文件锁链表
   struct address_space    i_data;
   #ifdef CONFIG_QUOTA
   struct dquot        *i_dquot[MAXQUOTAS];//inode磁盘限额
   #endif
   
   struct list_head    i_devices;,
   union {
   struct pipe_inode_info  *i_pipe;//如果文件是一个管道则使用i_pipe
   struct block_device *i_bdev;//如果文件是一个块设备则使用i_bdev
   struct cdev     *i_cdev;//如果文件是一个字符设备这使用i_cdev
   };

   __u32           i_generation;

   #ifdef CONFIG_FSNOTIFY
   //目录通知事件掩码
   __u32           i_fsnotify_mask; 
   struct hlist_head   i_fsnotify_marks;
   #endif

   #ifdef CONFIG_IMA
   atomic_t        i_readcount; 
   #endif
   //存储文件系统或者设备的私有信息
   void            *i_private; 
}

3、一个文件一个inode结构体,里面对应一个系统唯一inode号

struct inode的结构体就是存储文件属性的结构体。注意inode只是存储文件的属性,文件属性和文件存储的内容是分开存储的。

硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。

inode结构体很多数据,有兴趣可以详细研读,我们现在只关心(unsigned long i_ino;//inode号):一个文件一个inode结构体,里面对应一个系统唯一inode号。

4、查看文件inode信息

#stat rongshu.txt

file:文件名

size:大小

blocks:文件使用的数据块总数 8

io blocks:IO块大小4096

device:fd01h/64769d:磁盘位置

inode:786486:索引节点(这里看到每个文件都有一个inode号)

link:链接数,即有多少文件名指向这个inode

5、fd的node和inode struct里面的inode

细心的你可能发现,fd的node和inode struct里面的inode都是一个数字,名字也是类似,那是不是同一个东西?答案:是的,是同一个东西,可以自己验证

#exec 8<  io.txt

#lsof  -p  $$

#stat io.txt

6.inode大小

(1)#df -i

(2)一般是128字节或256字节

7.inode是VFS使用的一个对象

(1)刚有说到,inode只是文件的属性,和内容是分开的,实际上inode是VFS使用的一个对象,是linux虚拟文件系统,挂载磁盘真实文件而创建的一个东西。

索引节点有两种:

一种是这里所说的VFS索引节点,存在内存中

一种是具体文件系统的索引节点,存在于磁盘上(可以理解为文件存储块的起始位置)

(2)打开一个文件时,操作系统就会把对应的文件的inode的部分属性摘录下来放在内存里,存在fd_array中

fd_array,可以理解是个二维数组,key=fd,value=node,node是inode号,这时返回个fd给用户,就可以操作文件了

(3)操作文件的时候,就可以快速通过fd找到node(inode号),通过inode号可以找到文件具体位置

三、了解fd有啥用

最后了解文件描述符有啥用呢?我理解

1.我们要做系统性能优化,I/O就是很重要的一块,I/O又分文件I/O,网络I/O。要想理解操作系统I/O工作原理,文件描述符是第一个要掌握的。这里是为后面学习I/O知识打基础。

2.理解这个更好理解操作系统抽象统一的思想,vfs就是个很好的例子,万物可以挂载。内核通过fd就可以高效管理系统资源

3.扩展:操作文件调用open可以拿到一个fd,启动服务调用socket函数可以拿到一个fd,进程间通信调用pipe可以拿到2个fd。

关于I/O学习是很重要的,文件描述符就写到这。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/999990.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号