文件缓冲区和inode详解

文件缓冲区

有时候我们在调用c语言接口如fwrite,fputs,fputc等,但是我们有时候会看到一种特别的现象,我们明明向显示器打印,但是却没有显示,我们先看一段代码

#include
#include
#include
#include
#include
#include

int main()
{
        const char * str = "hello 标准输出\n";
        write(1,str,strlen(str));

        printf("%s",str);
        fprintf(stdout,"%s",str);
        fputs(str,stdout);

        //close(1);

}

这段代码毋庸置疑可以打印4句“hello 标准输出”,我们看结果。

我们将它重定向到文件中查看也是正确的,重定向的原理我在上节已经讲过,不知道小伙伴可以看下。 

  

但是我们将close(1)打开,同样将它运行重定义到文件中,我看到结果如下图:

这是文件中只有一条打印,毋庸置疑这条log是系统调用write产生的,那么问题来了,c语言打印的3条log去哪里了,要弄清这个问题,我们需要明白文件缓冲区的概念。

文件缓冲区和inode详解_第1张图片

 可以看出c语言中有一个自己的缓冲区,当我们打印内容的时候会先向缓冲区中写入,说到这不少小伙伴可能会有疑问,那我们平时printf的时候。看如下代码

#include
#include
#include
#include
#include
#include

int main()
{
        const char * str = "hello 标准输出\n";
//      write(1,str,strlen(str));

        printf("%s",str);
//      fprintf(stdout,"%s",str);
//      fputs(str,stdout);

        close(1);

}

结果

为什么会出现这个情况,我也重定向了,却还是看到了输出log,这是因为缓冲区的缓冲策略不同:

显示器是行缓冲,文件是全缓冲,所以发生重定义之后缓冲策略发生了改变。

 那为什么我们关掉close(1)就会发生上述的情况,原因是因为缓冲区向内核文件缓冲区写入时必须要fd,上节我们说过FILE结构体中分装了fd,当我们把1号文件描述符关掉,就没有办法向内核文件缓冲区写入了。要解决这种情况可以向内核文件系统中强制刷新c语言文件缓冲区,fflsuh提供这种功能。

inode

在将inode我们需要知道磁盘文件系统,我们先看下磁盘的结构

文件缓冲区和inode详解_第2张图片

 我们知道磁盘的结构图

文件缓冲区和inode详解_第3张图片

 我们要找到磁盘上的文件,需要通过柱面号->磁道->扇区。

每个扇区为512字节,我们可以将磁盘想象成线性的结构

文件缓冲区和inode详解_第4张图片

 这是物理结构上的,但是操作系统,为了描述磁盘,会对磁盘进行分区,每个分区会进行分组,每个组会包含超级快(supper block),组描述符(group descritor table),inode位图(inode table),数据块位图(block bitmap),inode节点表,数据库(data block).​​​​​​

文件缓冲区和inode详解_第5张图片

       文件储存在磁盘上,磁盘的最先存储单位为扇区(512字节),磁盘和内存进行交互是数据块4KB ,则为8个扇区。一个文件名对应一个inode,操作系统不认识文件名,只认识inode。

当我们创建一个文件 时,会在指定的文件系统中为我申请一个Inode 节点,写入文件时,会在数据块中写入数据,并在inode中建立数据块和indoe 的关系。我们可以简单理解为在struct indoe结构体中或有一个block数组,数组中存放的是数据块的下标。

        那么我们怎么分配inode号呢,会在Inode的位图中,找到不是1的位置,改为1,该位图就是inode 的号,数据块同理

文件缓冲区和inode详解_第6张图片

 那么用户使用的是文件名,操作系统怎么维护inode和文件名的映射关系,在Linux操作系统中一切皆文件,所以目录也是文件,目录也有对应的inode 号,他的Inode的表中也存放了和数据块的关系,但是他的数据块中存放的不是文件内容,而是文件名和inode的映射关系,所以我们能通过操作文件名来对linux 操作系统的使用,我们创建的一切文件都是在根目录下面进行的,所以根目录的数据块中存放了文件名和inode 的对应关系。

struct inode {
struct list_headi_hash;
struct list_headi_list;
struct list_headi_dentry;
struct list_headi_dirty_buffers;
unsigned longi_ino; /*每一个inode都有一个序号,经由super block结构和其序号,我们可以很轻易的找到这个inode。*/
atomic_t i_count; /*在Kernel里,很多的结构都会记录其reference count,以确保如果某个结构正在使用,它不会被不小心释放掉,i_count就是其reference count。*/
kdev_t i_dev; /* inode所在的device代码 */
umode_t i_mode; /* inode的权限 */
nlink_t i_nlink; /* hard link的个数 */
uid_t i_uid; /* inode拥有者的id */
gid_t i_gid; /* inode所属的群组id */
kdev_t i_rdev; /* 如果inode代表的是device的话,那此字段将记录device的代码 */ 
off_t i_size; /* inode所代表的档案大小 */
time_t i_atime; /* inode最近一次的存取时间 */
time_t i_mtime; /* inode最近一次的修改时间 */
time_t i_ctime; /* inode的产生时间 */ 
unsigned long i_blksize; /* inode在做IO时的区块大小 */
unsigned long i_blocks; /* inode所使用的block数,一个block为512 byte*/
unsigned long i_version; /* 版本号码 */ 
unsigned short i_bytes;
struct semaphore i_sem;
struct rw_semaphore i_truncate_sem;
struct semaphore i_zombie;
struct inode_operations *i_op;
struct file_operations *i_fop;/* former ->i_op->default_file_ops */
struct super_block *i_sb; /* inode所属档案系统的super block */
wait_queue_head_t i_wait;
struct file_lock *i_flock; /* 用来做file lock */
struct address_space *i_mapping;
struct address_space i_data;
struct dquot *i_dquot [MAXQUOTAS];
/* These three should probably be a union */
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct char_device *i_cdev;
unsigned longi_dnotify_mask; /* Directory notify events */
struct dnotify_struct *i_dnotify; /* for directory notifications */
unsigned long i_state; /* inode目前的状态,可以是I_DIRTY,I_LOCK和 I_FREEING的OR组合 */ 
unsigned int i_flags; /* 记录此inode的参数 */ 
unsigned char i_sock; /* 用来记录此inode是否为socket */ 
atomic_t i_write count;
unsigned int i_attr_flags; /* 用来记录此inode的属性参数 */ 
__u32 i_generation;
union {
struct minix_inode_info minix_i;
struct ext2_inode_info ext2_i;
struct ext3_inode_info ext3_i;
struct hpfs_inode_info hpfs_i;
struct ntfs_inode_info ntfs_i;
struct msdos_inode_info msdos_i;
struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
struct sysv_inode_info sysv_i;
struct affs_inode_info affs_i;
struct ufs_inode_info ufs_i;
struct efs_inode_info efs_i;
struct romfs_inode_info romfs_i;
struct shmem_inode_info shmem_i;
struct coda_inode_info coda_i;
struct smb_inode_info smbfs_i;
struct hfs_inode_info hfs_i;
struct adfs_inode_info adfs_i;
struct qnx4_inode_info qnx4_i;
struct reiserfs_inode_info reiserfs_i;
struct bfs_inode_info bfs_i;
struct udf_inode_info udf_i;
struct ncp_inode_info ncpfs_i;
struct proc_inode_info proc_i;
struct socketsocket_i;
struct usbdev_inode_info usbdev_i;
struct jffs2_inode_infojffs2_i;
void *generic_ip;
} u;
};

 这是linux 内核inode结构体中定义的变量,可以看到一个inode可以对应多个数据块。

有了Inode我们理解符号链接和硬链接的关系十分容易

符号链接:本质上是创建一个新的inode ,这个inode中记录了链接文件的inode所有属性

符号链接是独立的inode

硬链接:本质是同一个文件,inode一样,则说明inode中数据块中的映射关系一样。 但是建立一个硬链接inode的中i_count++;这是引用计数的原理。

所以在我们创建一个目录的时候,默认的硬链接数是2,为什么呢; 

文件缓冲区和inode详解_第7张图片

 文件缓冲区和inode详解_第8张图片

 我们可以看出dir 和 . 的inode是一样的。

另外小编推荐可以在线看所有版本的linux内核原因的网址

sched.h - include/linux/sched.h - Linux source code (1.0) - Bootlin

你可能感兴趣的:(linux)