Linux内核之文件描述符、文件表、i-node table

前言

1、文件描述符是一个int型整数
2、文件描述符创建是有一定的规则。在创建文件描述符的时候系统会在files_struct数组中,去找到一个当前没有被使用的一个最小下标,作为一个新的文件描述符。
3、程序开始运行时,有三个文件被自动打开了,打开时分别使用了这三个文件描述符

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误输出

4、同一个程序中,两次打开同样的文件,返回的描述符会不相同。
5、fork()进程的时候,进程的文件描述符也会被复制



1、文件描述符表、文件表、i-node table

​ ​ 在理解文件描述符表、文件表、v-node表的关系之前,知道它们存放的位置至关重要~

  • 每个进程都有一个属于自己的文件描述符表。
  • 文件表存放在内核空间,由系统里的所有进程共享。
  • 索引结点表也存放在内核空间,由所有进程所共享。

​ ​ 文件描述符表:该表记录进程打开的文件。它的表项里面有一个指针,指向存放在内核空间的文件表中的一个表项。它向用户提供一个简单的文件描述符(文件描述符表的下标),使得用户可以通过方便地访问一个文件。
​ ​  当进程使用open打开一个文件时,内核就会在这个表中添加一个表项。如果对同一个文件打开多次,那么将有多个表项。使用dup时,也会增加一个表项,后面都会讲到。
​ ​ 说到现在,那到底什么是文件描述符呢?打开文件后,进程得到的文件描述符实质上就是文件描述符表的下标,内核根据这个下标值去访问相应的文件对象,从而实现对文件的操作。



​ ​ 文件表:文件表保存了进程对文件读写的偏移量。该表还保存了进程对文件的存取权限。比如,进程以O_RDONLY方式打开文件,这将记录到对应的文件表表项中。每一个打开的文件都对应于一个file结构体(可以理解为file对象)。file对象有引用计数short f_count,记录了引用这个对象的文件描述符个数,只有当引用计数为0时,内核才销毁file对象,因此某个进程关闭文件,不影响与之共享同一个file对象的进程。

​ ​ 每个文件都有一个32位的数字来表示下一个读写的字节位置,这个数字叫做文件位置(f_pos)。每次打开一个文件,除非明确要求,否则文件位置都被置为0,即文件的开始处,此后的读或写操作都将从文件的开始处执行,但你可以通过执行系统调用lessk函数对这个文件位置进行修改。

struct file 

{

 struct list_head        f_list;    /*所有打开的文件形成一个链表*/

 struct dentry           *f_dentry; /*指向相关目录项的指针*/

 struct vfsmount         *f_vfsmnt; /*指向VFS安装点的指针*/

 struct file_operations  *f_op;     /*指向文件操作表的指针*/

 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驱动程序所需 */

};


​ ​ v-node/I-node 表(索引节点表):同文件表一样,所有的进程共享这张v-node表。每个表项包含stat结构中的大多数信息,包括st_mode和st_size成员等。

struct inode {
	/* RCU path lookup touches following: */
	umode_t			i_mode;     //权限
	uid_t			i_uid;      //用户id
	gid_t			i_gid;      //组id
	const struct inode_operations	*i_op;
	struct super_block	*i_sb;
 
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	unsigned int		i_flags;
	struct mutex		i_mutex;
 
	unsigned long		i_state;
	unsigned long		dirtied_when;	/* jiffies of first dirtying */
 
	struct hlist_node	i_hash;
	struct list_head	i_wb_list;	/* backing dev IO list */
	struct list_head	i_lru;		/* inode LRU list */
	struct list_head	i_sb_list;
	union {
		struct list_head	i_dentry;
		struct rcu_head		i_rcu;
	};
	unsigned long		i_ino;   //inode节点号
	atomic_t		i_count;
	unsigned int		i_nlink;
	dev_t			i_rdev;
	unsigned int		i_blkbits;
	u64			i_version;
	loff_t			i_size;   //文件大小
#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif
	struct timespec		i_atime;  //最后一次访问(access)的时间
	struct timespec		i_mtime;  //最后一次修改(modify)的时间
	struct timespec		i_ctime;  //最后一次改变(change)的时间
	blkcnt_t		i_blocks;    //块数
	unsigned short          i_bytes;
	struct rw_semaphore	i_alloc_sem;
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	struct file_lock	*i_flock;
	struct address_space	*i_mapping;   //块地址映射
	struct address_space	i_data;
#ifdef CONFIG_QUOTA
	struct dquot		*i_dquot[MAXQUOTAS];
#endif
	struct list_head	i_devices;
	union {
		struct pipe_inode_info	*i_pipe;
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;
	};
 
	__u32			i_generation;
 
#ifdef CONFIG_FSNOTIFY
	__u32			i_fsnotify_mask; /* all events this inode cares about */
	struct hlist_head	i_fsnotify_marks;
#endif
 
#ifdef CONFIG_IMA
	atomic_t		i_readcount; /* struct files open RO */
#endif
	atomic_t		i_writecount;
#ifdef CONFIG_SECURITY
	void			*i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
	struct posix_acl	*i_acl;
	struct posix_acl	*i_default_acl;
#endif
	void			*i_private; /* fs or device private pointer */
};


在这里插入图片描述



2、常见的编程误区

​ ​ 2.1 父进程使用fork创建子进程

 ​ 当程序调用 fork() 函数时, 则会出现(不同进程的)多个描述符对应于同一个文件表表项的情况。子进程在创建时会拷贝父进程的打开文件描述符表,因此父子进程是共享文件表项的。
在这里插入图片描述

​ ​ 2.2 同一个进程多次打开同一个文件

 ​ 每打开一次同一个文件,内核就会在文件表中增加一个表项。这是因为每次open文件时使用了不同的读写权限,而读写权限是保存在文件表表项里面的。


​ ​ 2.3 使用dup函数复制一个文件描述符

 ​ dup函数是用来复制一个文件描述符的。复制得到的文件描述符和原描述符虽然数字不同,但是共享文件偏移量和一些状态(共享文件表项)。所以dup的作用仅仅是复制一个文件描述符表项,而不会复制一个文件表表项。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值