2019-02-21 12:14:18 OOFFrankDura 阅读数 1411
  • hadoop全分布式集群配置

    Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性 的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序 的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。本课程讲解搭建hadoop集群的全过程,从安装vmware,centos开始,从本地模式,伪分布式到全分布式,讲解实现经典案例wordcount结束,全方位讲解hadoop的操作细节,带你完成hadoop的安装和入门

    858 人正在学习 去看看 冯文凯

综述

操作系统的理论学习也算结束了,考试其实对自己并不是很满意。后面继续努力吧。先前基地班学长给了一个复习的资料关于文件打开过程的,感觉很不错

正文

文件打开的过程如下图所示(从右往左看)
在这里插入图片描述
首先,操作系统根据文件名a,在系统文件打开表中查找
第一种情况:
如果文件a已经打开,则在进程文件打开表中为文件a分配一个表项,然后将该表项的指针指向系统文件打开表中和文件a对应的一项;
然后再PCB中为文件分配一个文件描述符fd,作为进程文件打开表项的指针,文件打开完成。
第二种情况:
如果文件a没有打开,查看含有文件a信息的目录项是否在内存中,如果不在,将目录表装入到内存中,作为cache;
根据目录表中文件a对应项找到FCB在磁盘中的位置;
将文件a的FCB装入到内存中的Active inode中;
然后在系统文件打开表中为文件a增加新的一个表项,将表项的指针指向Active Inode中文件a的FCB;
然后在进程的文件打开表中分配新的一项,将该表项的指针指向系统文件打开表中文件a对应的表项;
然后在PCB中,为文件a分配一个文件描述符fd,作为进程文件打开表项的指针,文件打开完成。
为了保证进程有独立的内存空间:基地址寄存器、界限地址寄存器

2015-07-19 16:39:19 giantpoplar 阅读数 5740
  • hadoop全分布式集群配置

    Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性 的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序 的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。本课程讲解搭建hadoop集群的全过程,从安装vmware,centos开始,从本地模式,伪分布式到全分布式,讲解实现经典案例wordcount结束,全方位讲解hadoop的操作细节,带你完成hadoop的安装和入门

    858 人正在学习 去看看 冯文凯

fd=fopen()是一个系统调用,用于根据文件名打开一个文件,返回该文件的文件描述符,文件打开后进程便可以根据文件描述符fd进行其他操作,比如读,写,关闭等操作。

各个操作系统打开文件的过程是类似的,本文以Unix为例,介绍打开一个文件操作系统所做的工作,正式介绍这个过程之前先简要介绍几个概念。

PCB(process control block)进程控制块,它是一个内核数据结构,相当于一个档案,是操作系统感知进程存在的唯一标识。包括进程状态,进程id,PC,寄存器,内存信息,文件打开信息等,如下图所示


FCB(file control block)文件控制块,是文件系统的一部分,在磁盘上一般会创建一个文件系统,文件系统中包含目录信息,以及文件的FCB信息。FCB一半包含文件的读写模式,所有者,时间戳,数据块指针等信息,unix的FCB称为inode,其结构如下图所示

本图片来自http://codex.cs.yale.edu/avi/os-book/OS9/slide-dir/os-figures.zip


文件打开的过程如下图所示(从右往左看)



首先,操作系统根据文件名a,在系统文件打开表中查找

第一种情况:

如果文件a已经打开,则在进程文件打开表中为文件a分配一个表项,然后将该表项的指针指向系统文件打开表中和文件a对应的一项;

然后再PCB中为文件分配一个文件描述符fd,作为进程文件打开表项的指针,文件打开完成。

第二种情况:

如果文件a没有打开,查看含有文件a信息的目录项是否在内存中,如果不在,将目录表装入到内存中,作为cache;

根据目录表中文件a对应项找到FCB在磁盘中的位置;

将文件a的FCB装入到内存中的Active inode中;

然后在系统文件打开表中为文件a增加新的一个表项,将表项的指针指向Active Inode中文件a的FCB;

然后在进程的文件打开表中分配新的一项,将该表项的指针指向系统文件打开表中文件a对应的表项;

然后在PCB中,为文件a分配一个文件描述符fd,作为进程文件打开表项的指针,文件打开完成。

2018-08-05 17:45:37 qq_38410730 阅读数 8036
  • hadoop全分布式集群配置

    Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性 的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序 的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。本课程讲解搭建hadoop集群的全过程,从安装vmware,centos开始,从本地模式,伪分布式到全分布式,讲解实现经典案例wordcount结束,全方位讲解hadoop的操作细节,带你完成hadoop的安装和入门

    858 人正在学习 去看看 冯文凯

在虚拟文件系统的支持下,Linux可以支持迄今为止的大多数文件系统。但是,Linux并不是在初始化时就把所有文件系统全部都装入,而只是安装一个文件系统(通常是Ext2)作为根文件系统。根文件系统在整个系统运行过程中是不能被拆卸的,是系统的基本组成部分。通常,根文件系统上主要安装了保证系统正常工作运行的操作系统代码文件以及若干语言编译程序、命令解释程序和相应的命令处理程序等文件。其他文件系统则都定义成可安装模块,以便用户需要时动态地进行安装。

所谓安装,就是在虚拟文件系统中建立一个超级块super_block,并用被安装文件系统超级块中的相关信息填写super_block,然后用被安装文件系统的根目录代替系统现有目录结构的一个空目录,从而把子系统与原文件系统连接起来。

为使系统在安装之前了解待安装文件子系统的基本信息(例如超级块的位置),必须向系统文件注册表提交一个数据结构进行注册。

把一个文件系统安装到根文件系统的一个节点上的示意图如下所示:

 

文件系统的注册

由于不同的文件系统具有不同的特点,因此其安装过程也不尽相同。为了正确地安装一个文件系统,被安装的文件系统在安装之前必须向系统进行注册,以便使系统感知和了解待安装文件系统的类型及安装相关的信息。

系统通过调用文件的初始化例程来填写一个叫做file_system_type的数据结构,并调用函数register_filesystem()把该数据结构加入到由系统维护的已注册文件系统的链表中。如下图所示:

数据结构file_system_type的定义如下:

struct file_system_type {
	const char *name;                //文件系统的名称
	int fs_flags;        
	int (*get_sb) (struct file_system_type *, int,
		       const char *, void *, struct vfsmount *);
	void (*kill_sb) (struct super_block *);
	struct module *owner;
	struct file_system_type * next;            //下一个文件系统的指针
	struct list_head fs_supers;

	struct lock_class_key s_lock_key;
	struct lock_class_key s_umount_key;

	struct lock_class_key i_lock_key;
	struct lock_class_key i_mutex_key;
	struct lock_class_key i_mutex_dir_key;
	struct lock_class_key i_alloc_sem_key;
};

 

文件系统的安装与卸载

当系统试图安装一个子文件系统时,系统会调用函数mount()首先在文件系统注册链表中查找与用户提供名称相匹配的注册文件系统,如果找到,则在该文件系统的file_system_type中获得与文件系统对应的超级块的指针,并用超级块中的相关信息填写系统分配给该系统的VFS超级块,并把这个超级块加入到VFS超级块链表中。

接下来就要填写一个系统分配给该文件系统的vfsmount数据结构,以保存已被安装的文件系统的基本安装信息,从而把该文件系统的根节点安装到选定的VFS索引节点上。vfsmount结构的定义如下:

struct vfsmount {
	struct list_head mnt_hash;
	struct vfsmount *mnt_parent;	/* 本系统所挂接的父文件系统 */
	struct dentry *mnt_mountpoint;	/* 挂接点的dentry结构 */
	struct dentry *mnt_root;	/* 本系统的根目录 */
	struct super_block *mnt_sb;	/* pointer to superblock */
	struct list_head mnt_mounts;	/* list of children, anchored here */
	struct list_head mnt_child;	/* and going through their mnt_child */
	int mnt_flags;
	/* 4 bytes hole on 64bits arches */
	const char *mnt_devname;	/* 设备名称 e.g. /dev/dsk/hda1 */
	struct list_head mnt_list;
	struct list_head mnt_expire;	/* link in fs-specific expiry list */
	struct list_head mnt_share;	/* circular list of shared mounts */
	struct list_head mnt_slave_list;/* list of slave mounts */
	struct list_head mnt_slave;	/* slave list entry */
	struct vfsmount *mnt_master;	/* slave is on master->mnt_slave_list */
	struct mnt_namespace *mnt_ns;	/* containing namespace */
	int mnt_id;			/* mount identifier */
	int mnt_group_id;		/* peer group identifier */
	/*
	 * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount
	 * to let these frequently modified fields in a separate cache line
	 * (so that reads of mnt_flags wont ping-pong on SMP machines)
	 */
	atomic_t mnt_count;
	int mnt_expiry_mark;		/* true if marked for expiry */
	int mnt_pinned;
	int mnt_ghosts;
	/*
	 * This value is not stable unless all of the mnt_writers[] spinlocks
	 * are held, and all mnt_writer[]s on this mount have 0 as their ->count
	 */
	atomic_t __mnt_writers;
};

所有已经被安装的系统都有一个对应的vfsmount数据结构,结构中的指针mnt_mountpoint指向挂接点的目录,而指针mut_root指向被安装系统的根目录,所以结构vfsmount是子系统与父系统之间的桥梁。该数据结构一旦被填写,系统便认为该结构所对应的文件系统已经被安装。

所以,已经被安装的系统的vfsmount数据结构,在系统中也是用一个链表来管理的。

拆卸一个文件子系统的过程基本与安装的过程相反。系统首先验证文件子系统是否为可拆卸的:如果该文件子系统的文件正在被使用,则该系统时不可拆卸的;否则,释放该文件子系统所占用的VFS超级块和安装点,从而卸下该文件子系统。

 

2013-01-03 00:40:56 weixin_33985507 阅读数 18
  • hadoop全分布式集群配置

    Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性 的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序 的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。本课程讲解搭建hadoop集群的全过程,从安装vmware,centos开始,从本地模式,伪分布式到全分布式,讲解实现经典案例wordcount结束,全方位讲解hadoop的操作细节,带你完成hadoop的安装和入门

    858 人正在学习 去看看 冯文凯

 

Ext3文件读写流程概述
 
Ext3文件系统在进行读写操作的时候,首先需要open相应的文件,然后再进行读写操作。在open操作时,Linux kernel会创建一个file对象描述这个文件。File对象和文件的dentry和inode对象建立联系,并且将ext3的文件操作方法、映射处理方法(address space)注册到file对象中。
 
Ext3文件读写过程会涉及到VFS层的page cache,并且通常的读写操作都会使用到这层page cache,目的是提高磁盘的IO性能。在Linux中后台会运行writeback线程定时同步pagecache和设备之间的数据。Page cache的方式虽然能够提高IO性能,但是也对数据的安全性带来了潜在影响。
 
本文的目的是分析ext3文件系统读写流程中的关键函数,对于page cache原理以及writeback机制将在后继文章中做深入分析。
 
关键数据结构
 
File数据结构是Linux用来描述文件的关键数据结构,该对象在一个文件被进程打开的时候被创建。当一个文件被关闭的时候,file对象也会被立即销毁。file数据结构不会被作为元数据信息持久化保存至设备。该数据结构定义如下:
 
  1. struct file {  
  2.     /*  
  3.      * fu_list becomes invalid after file_free is called and queued via  
  4.      * fu_rcuhead for RCU freeing  
  5.      */  
  6.     union {  
  7.         struct list_head    fu_list;  
  8.         struct rcu_head     fu_rcuhead;  
  9.     } f_u;  
  10.     struct path     f_path;     /* 文件路径,包含文件dentry目录项和vfsmount信息 */  
  11. #define f_dentry    f_path.dentry  
  12. #define f_vfsmnt    f_path.mnt  
  13.     const struct file_operations    *f_op;  /* 文件操作函数集 */  
  14.  
  15.     /*  
  16.      * Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR.  
  17.      * Must not be taken from IRQ context.  
  18.      */  
  19.     spinlock_t      f_lock;  
  20. #ifdef CONFIG_SMP  
  21.     int         f_sb_list_cpu;  
  22. #endif  
  23.     atomic_long_t       f_count;  
  24.     unsigned int        f_flags;  
  25.     fmode_t         f_mode; /* 文件操作模式 */  
  26.     loff_t          f_pos;  
  27.     struct fown_struct  f_owner;  
  28.     const struct cred   *f_cred;  
  29.     struct file_ra_state    f_ra;  
  30.  
  31.     u64         f_version;  
  32. #ifdef CONFIG_SECURITY  
  33.     void            *f_security;  
  34. #endif  
  35.     /* needed for tty driver, and maybe others */  
  36.     void            *private_data;  
  37.  
  38. #ifdef CONFIG_EPOLL  
  39.     /* Used by fs/eventpoll.c to link all the hooks to this file */  
  40.     struct list_head    f_ep_links;  
  41.     struct list_head    f_tfile_llink;  
  42. #endif /* #ifdef CONFIG_EPOLL */  
  43.     struct address_space    *f_mapping; /* address space映射信息,指向inode中的i_mapping */  
  44. #ifdef CONFIG_DEBUG_WRITECOUNT  
  45.     unsigned long f_mnt_write_state;  
  46. #endif  
  47. }; 

每个文件在内存中都会对应一个inode对象。在设备上也会保存每个文件的inode元数据信息,通过inode元数据信息可以找到该文件所占用的所有文件数据块(block)。VFS定义了一个通用的inode数据结构,同时ext3定义了ext3_inode元数据结构。在创建内存inode对象时,需要采用ext3_inode元数据信息初始化inode对象。Inode数据结构定义如下:

  1. struct inode {  
  2.     umode_t         i_mode;  
  3.     unsigned short      i_opflags;  
  4.     uid_t           i_uid;  
  5.     gid_t           i_gid;  
  6.     unsigned int        i_flags;  
  7.  
  8. #ifdef CONFIG_FS_POSIX_ACL  
  9.     struct posix_acl    *i_acl;  
  10.     struct posix_acl    *i_default_acl;  
  11. #endif  
  12.  
  13.     const struct inode_operations   *i_op;  /* inode操作函数集 */  
  14.     struct super_block  *i_sb;      /* 指向superblock */  
  15.     struct address_space    *i_mapping; /* 指向当前使用的页缓存的映射信息 */  
  16.  
  17. #ifdef CONFIG_SECURITY  
  18.     void            *i_security;  
  19. #endif  
  20.  
  21.     /* Stat data, not accessed from path walking */  
  22.     unsigned long       i_ino;  
  23.     /*  
  24.      * Filesystems may only read i_nlink directly.  They shall use the  
  25.      * following functions for modification:  
  26.      *  
  27.      *    (set|clear|inc|drop)_nlink  
  28.      *    inode_(inc|dec)_link_count  
  29.      */  
  30.     union {  
  31.         const unsigned int i_nlink;  
  32.         unsigned int __i_nlink;  
  33.     };  
  34.     dev_t           i_rdev;     /* 设备号,major&minor */  
  35.     struct timespec     i_atime;  
  36.     struct timespec     i_mtime;  
  37.     struct timespec     i_ctime;  
  38.     spinlock_t      i_lock; /* i_blocks, i_bytes, maybe i_size */  
  39.     unsigned short          i_bytes;  
  40.     blkcnt_t        i_blocks;   /* 文件块数量 */  
  41.     loff_t          i_size;  
  42.  
  43. #ifdef __NEED_I_SIZE_ORDERED  
  44.     seqcount_t      i_size_seqcount;  
  45. #endif  
  46.  
  47.     /* Misc */  
  48.     unsigned long       i_state;  
  49.     struct mutex        i_mutex;  
  50.  
  51.     unsigned long       dirtied_when;   /* jiffies of first dirtying */  
  52.  
  53.     struct hlist_node   i_hash; /* 连接到inode Hash Table中 */  
  54.     struct list_head    i_wb_list;  /* backing dev IO list */  
  55.     struct list_head    i_lru;      /* inode LRU list */  
  56.     struct list_head    i_sb_list;  
  57.     union {  
  58.         struct list_head    i_dentry;  
  59.         struct rcu_head     i_rcu;  
  60.     };  
  61.     atomic_t        i_count;  
  62.     unsigned int        i_blkbits;  /* 块大小,通常磁盘块大小为512字节,因此i_blkbits为9 */  
  63.     u64         i_version;  
  64.     atomic_t        i_dio_count;  
  65.     atomic_t        i_writecount;  
  66.     const struct file_operations    *i_fop; /* former ->i_op->default_file_ops,文件操作函数集 */  
  67.     struct file_lock    *i_flock;  
  68.     struct address_space    i_data; /* 页高速缓存映射信息 */  
  69. #ifdef CONFIG_QUOTA  
  70.     struct dquot        *i_dquot[MAXQUOTAS];  
  71. #endif  
  72.     struct list_head    i_devices;  
  73.     union {  
  74.         struct pipe_inode_info  *i_pipe;        /* 管道设备 */  
  75.         struct block_device *i_bdev;    /* block device块设备 */  
  76.         struct cdev     *i_cdev;    /* 字符设备 */  
  77.     };  
  78.  
  79.     __u32           i_generation;  
  80.  
  81. #ifdef CONFIG_FSNOTIFY  
  82.     __u32           i_fsnotify_mask; /* all events this inode cares about */  
  83.     struct hlist_head   i_fsnotify_marks;  
  84. #endif  
  85.  
  86. #ifdef CONFIG_IMA  
  87.     atomic_t        i_readcount; /* struct files open RO */  
  88. #endif  
  89.     void            *i_private; /* fs or device private pointer */  
  90. }; 

 

 

读过程源码分析
 
Ext3文件系统读过程相对比较简单,函数调用关系如下图所示:

 

 
读过程可以分为两大类:Direct_io方式和page_cache方式。对于Direct_io方式,首先通过filemap_write_and_wait_range函数将page cache中的数据与设备同步并且无效掉page cache中的内容,然后再通过ext3提供的direct_io方法从设备读取数据。
另一种是直接从page cache中获取数据,通过do_generic_file_read函数实现该方式。该函数的主要流程说明如下:
1,通过读地址从page cache的radix树中获取相应的page页。
2,如果对应的page页不存在,那么需要创建一个page,然后再从设备读取相应的数据更新至page页。
3,当page页准备完毕之后,从页中拷贝数据至用户空间,page_cache方式的读操作完成。
 
写过程源码分析
 
Ext3的写过程主要分为direct_io写过程和page cache写过程两大类,整个写过程的函数调用关系如下图所示:

 

 
写操作的核心函数是__generic_file_aio_write,该函数实现如下:
  1. ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,  
  2.                  unsigned long nr_segs, loff_t *ppos)  
  3. {  
  4.     struct file *file = iocb->ki_filp;  
  5.     /* 获取address space映射信息 */  
  6.     struct address_space * mapping = file->f_mapping;  
  7.     size_t ocount;      /* original count */  
  8.     size_t count;       /* after file limit checks */  
  9.     struct inode    *inode = mapping->host; /* 获取文件inode索引节点 */  
  10.     loff_t      pos;  
  11.     ssize_t     written;  
  12.     ssize_t     err;  
  13.  
  14.     ocount = 0;  
  15.     /* 检验数据区域是否存在问题,数据由iov数据结构管理 */  
  16.     err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ);  
  17.     if (err)  
  18.         return err;  
  19.     /* ocount为可以写入的数据长度 */  
  20.     count = ocount;  
  21.     pos = *ppos;  
  22.  
  23.     vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);  
  24.  
  25.     /* We can write back this queue in page reclaim */  
  26.     current->backing_dev_info = mapping->backing_dev_info;  
  27.     written = 0;  
  28.     /* 边界检查,需要判断写入数据是否超界、小文件边界检查以及设备是否是read-only。如果超界,那么降低写入数据长度 */  
  29.     err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));  
  30.     if (err)  
  31.         goto out;  
  32.     /* count为实际可以写入的数据长度,如果写入数据长度为0,直接结束 */  
  33.     if (count == 0)  
  34.         goto out;  
  35.  
  36.     err = file_remove_suid(file);  
  37.     if (err)  
  38.         goto out;  
  39.  
  40.     file_update_time(file);  
  41.  
  42.     /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */  
  43.     if (unlikely(file->f_flags & O_DIRECT)) {  
  44.         /* Direct IO操作模式,该模式会bypass Page Cache,直接将数据写入磁盘设备 */  
  45.         loff_t endbyte;  
  46.         ssize_t written_buffered;  
  47.         /* 将对应page cache无效掉,然后将数据直接写入磁盘 */  
  48.         written = generic_file_direct_write(iocb, iov, &nr_segs, pos,  
  49.                             ppos, count, ocount);  
  50.         if (written < 0 || written == count)  
  51.             /* 所有数据已经写入磁盘,正确返回 */  
  52.             goto out;  
  53.         /*  
  54.          * direct-io write to a hole: fall through to buffered I/O  
  55.          * for completing the rest of the request.  
  56.          */  
  57.         pos += written;  
  58.         count -written;  
  59.         /* 有些请求由于没有和块大小(通常为512字节)对齐,那么将无法正确完成direct-io操作。在__blockdev_direct_IO 函数中会检查逻辑地址是否和块大小对齐,__blockdev_direct_IO无法处理不对齐的请求。另外,在ext3逻辑地址和物理块地址映射操作函数ext3_get_block返回失败时,无法完成buffer_head的映射,那么request请求也将无法得到正确处理。所有没有得到处理的请求通过 buffer写的方式得到处理。从这点来看,direct_io并没有完全bypass page cache,在有些情况下是一种写无效模式。generic_file_buffered_write函数完成buffer写,将数据直接写入page cache */  
  60.         written_buffered = generic_file_buffered_write(iocb, iov,  
  61.                         nr_segs, pos, ppos, count,  
  62.                         written);  
  63.         /*  
  64.          * If generic_file_buffered_write() retuned a synchronous error  
  65.          * then we want to return the number of bytes which were  
  66.          * direct-written, or the error code if that was zero.  Note  
  67.          * that this differs from normal direct-io semantics, which  
  68.          * will return -EFOO even if some bytes were written.  
  69.          */  
  70.         if (written_buffered < 0) {  
  71.             /* 如果page cache写失败,那么返回写成功的数据长度 */  
  72.             err = written_buffered;  
  73.             goto out;  
  74.         }  
  75.  
  76.         /*  
  77.          * We need to ensure that the page cache pages are written to  
  78.          * disk and invalidated to preserve the expected O_DIRECT  
  79.          * semantics.  
  80.          */  
  81.         endbyte = pos + written_buffered - written - 1;  
  82.         /* 将page cache中的数据同步到磁盘 */  
  83.         err = filemap_write_and_wait_range(file->f_mapping, pos, endbyte);  
  84.         if (err == 0) {  
  85.             written = written_buffered;  
  86.             /* 将page cache无效掉,保证下次读操作从磁盘获取数据 */  
  87.             invalidate_mapping_pages(mapping,  
  88.                          pos >> PAGE_CACHE_SHIFT,  
  89.                          endbyte >> PAGE_CACHE_SHIFT);  
  90.         } else {  
  91.             /*  
  92.              * We don't know how much we wrote, so just return  
  93.              * the number of bytes which were direct-written  
  94.              */  
  95.         }  
  96.     } else {  
  97.         /* 将数据写入page cache。绝大多数的ext3写操作都会采用page cache写方式,通过后台writeback线程将page cache同步到硬盘 */  
  98.         written = generic_file_buffered_write(iocb, iov, nr_segs,  
  99.                 pos, ppos, count, written);  
  100.     }  
  101. out:  
  102.     current->backing_dev_info = NULL;  
  103.     return written ? written : err;  

 

从__generic_file_aio_write函数可以看出,ext3写操作主要分为两大类:一类为direct_io;另一类为buffer_io (page cache write)。Direct IO可以bypass page cache,直接将数据写入设备。下面首先分析一下direct_io的处理流程。

如果操作地址对应的page页存在于page cache中,那么首先需要将这些page页中的数据同磁盘进行同步,然后将这些page缓存页无效掉,从而保证后继读操作能够从磁盘获取最新数据。在代码实现过程中,还需要考虑预读机制引入的page缓存页,所以在数据写入磁盘之后,需要再次查找page cache的radix树,保证写入的地址范围没有数据被缓存。
 
Generic_file_direct_write是处理direct_io的主要函数,该函数的实现如下:
  1. ssize_t  
  2. generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,  
  3.         unsigned long *nr_segs, loff_t pos, loff_t *ppos,  
  4.         size_t count, size_t ocount)  
  5. {  
  6.     struct file *file = iocb->ki_filp;  
  7.     struct address_space *mapping = file->f_mapping;  
  8.     struct inode    *inode = mapping->host;  
  9.     ssize_t     written;  
  10.     size_t      write_len;  
  11.     pgoff_t     end;  
  12.  
  13.     if (count != ocount)  
  14.         *nr_segs = iov_shorten((struct iovec *)iov, *nr_segs, count);  
  15.  
  16.     write_len = iov_length(iov, *nr_segs);  
  17.     end = (pos + write_len - 1) >> PAGE_CACHE_SHIFT;  
  18.     /* 将对应区域page cache中的新数据页刷新到设备,这个操作是同步的 */  
  19.     written = filemap_write_and_wait_range(mapping, pos, pos + write_len - 1);  
  20.     if (written)  
  21.         goto out;  
  22.  
  23.     /*  
  24.      * After a write we want buffered reads to be sure to go to disk to get  
  25.      * the new data.  We invalidate clean cached page from the region we're  
  26.      * about to write.  We do this *before* the write so that we can return  
  27.      * without clobbering -EIOCBQUEUED from ->direct_IO().  
  28.      */  
  29.     /* 将page cache对应page 缓存无效掉,这样可以保证后继的读操作能从磁盘获取最新数据 */  
  30.     if (mapping->nrpages) {  
  31.         /* 无效对应的page缓存 */  
  32.         written = invalidate_inode_pages2_range(mapping,  
  33.                     pos >> PAGE_CACHE_SHIFT, end);  
  34.         /*  
  35.          * If a page can not be invalidated, return 0 to fall back  
  36.          * to buffered write.  
  37.          */  
  38.         if (written) {  
  39.             if (written == -EBUSY)  
  40.                 return 0;  
  41.             goto out;  
  42.         }  
  43.     }  
  44.     /* 调用ext3文件系统的direct io方法,将数据写入磁盘 */  
  45.     written = mapping->a_ops->direct_IO(WRITE, iocb, iov, pos, *nr_segs);  
  46.  
  47.     /*  
  48.      * Finally, try again to invalidate clean pages which might have been  
  49.      * cached by non-direct readahead, or faulted in by get_user_pages()  
  50.      * if the source of the write was an mmap'ed region of the file  
  51.      * we're writing.  Either one is a pretty crazy thing to do,  
  52.      * so we don't support it 100%.  If this invalidation  
  53.      * fails, tough, the write still worked...  
  54.      */  
  55.     /* 再次无效掉由于预读操作导致的对应地址的page cache缓存页 */  
  56.     if (mapping->nrpages) {  
  57.         invalidate_inode_pages2_range(mapping,  
  58.                           pos >> PAGE_CACHE_SHIFT, end);  
  59.     }  
  60.  
  61.     if (written > 0) {  
  62.         pos += written;  
  63.         if (pos > i_size_read(inode) && !S_ISBLK(inode->i_mode)) {  
  64.             i_size_write(inode, pos);  
  65.             mark_inode_dirty(inode);  
  66.         }  
  67.         *ppos = pos;  
  68.     }  
  69. out:  
  70.     return written;  

 

generic_file_direct_write函数中刷新page cache的函数调用关系描述如下:

filemap_write_and_wait_range à__filemap_fdatawrite_rangeà do_writepages
do_writepages函数的作用是将page页中的数据同步到设备,该函数实现如下:
 
  1. int do_writepages(struct address_space *mapping, struct writeback_control *wbc)  
  2. {  
  3.     int ret;  
  4.  
  5.     if (wbc->nr_to_write <= 0)  
  6.         return 0;  
  7.     if (mapping->a_ops->writepages)  
  8.         /* 如果文件系统定义了writepages方法,调用该方法刷新page cache页 */  
  9.         ret = mapping->a_ops->writepages(mapping, wbc);  
  10.     else  
  11.         /* ext3没有定义writepages方法,因此调用generic_writepages()函数将page cache中的脏页刷新到磁盘 */  
  12.         ret = generic_writepages(mapping, wbc);  
  13.     return ret;  

从上述分析可以看出,direct_io需要块大小对齐,否则还会调用page cache的路径。为了提高I/O性能,通常情况下ext3都会采用page cache异步写的方式。这也就是ext3的第二种写操作方式,该方式实现的关键函数是generic_file_buffered_write,其实现如下:

  1. ssize_t  
  2. generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,  
  3.         unsigned long nr_segs, loff_t pos, loff_t *ppos,  
  4.         size_t count, ssize_t written)  
  5. {  
  6.     struct file *file = iocb->ki_filp;  
  7.     ssize_t status;  
  8.     struct iov_iter i;  
  9.  
  10.     iov_iter_init(&i, iov, nr_segs, count, written);  
  11.     /* 执行page cache写操作 */  
  12.     status = generic_perform_write(file, &i, pos);  
  13.  
  14.     if (likely(status >= 0)) {  
  15.         written += status;  
  16.         *ppos = pos + status;  
  17.     }  
  18.       
  19.     return written ? written : status;  

generic_file_buffered_write其实是对generic_perform_write函数的封装,generic_perform_write实现了page cache写的所有流程,该函数实现如下:

  1. static ssize_t generic_perform_write(struct file *file,  
  2.                 struct iov_iter *i, loff_t pos)  
  3. {  
  4.     struct address_space *mapping = file->f_mapping;  
  5.     const struct address_space_operations *a_ops = mapping->a_ops;  /* 映射处理函数集 */  
  6.     long status = 0;  
  7.     ssize_t written = 0;  
  8.     unsigned int flags = 0;  
  9.  
  10.     /*  
  11.      * Copies from kernel address space cannot fail (NFSD is a big user).  
  12.      */  
  13.     if (segment_eq(get_fs(), KERNEL_DS))  
  14.         flags |= AOP_FLAG_UNINTERRUPTIBLE;  
  15.  
  16.     do {  
  17.         struct page *page;  
  18.         unsigned long offset;   /* Offset into pagecache page */  
  19.         unsigned long bytes;    /* Bytes to write to page */  
  20.         size_t copied;      /* Bytes copied from user */  
  21.         void *fsdata;  
  22.  
  23.         offset = (pos & (PAGE_CACHE_SIZE - 1));  
  24.         bytes = min_t(unsigned long, PAGE_CACHE_SIZE - offset,  
  25.                         iov_iter_count(i));  
  26.  
  27. again:  
  28.         /*  
  29.          * Bring in the user page that we will copy from _first_.  
  30.          * Otherwise there's a nasty deadlock on copying from the  
  31.          * same page as we're writing to, without it being marked  
  32.          * up-to-date.  
  33.          *  
  34.          * Not only is this an optimisation, but it is also required  
  35.          * to check that the address is actually valid, when atomic  
  36.          * usercopies are used, below.  
  37.          */  
  38.         if (unlikely(iov_iter_fault_in_readable(i, bytes))) {  
  39.             status = -EFAULT;  
  40.             break;  
  41.         }  
  42.         /* 调用ext3中的write_begin函数(inode.c中)ext3_write_begin, 如果写入的page页不存在,那么ext3_write_begin会创建一个Page页,然后从硬盘中读入相应的数据 */  
  43.         status = a_ops->write_begin(file, mapping, pos, bytes, flags,  
  44.                         &page, &fsdata);  
  45.         if (unlikely(status))  
  46.             break;  
  47.  
  48.         if (mapping_writably_mapped(mapping))  
  49.             flush_dcache_page(page);  
  50.  
  51.         pagefault_disable();  
  52.         /* 将数据拷贝到page cache中 */  
  53.         copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);  
  54.         pagefault_enable();  
  55.         flush_dcache_page(page);  
  56.  
  57.         mark_page_accessed(page);  
  58.         /* 调用ext3的write_end函数(inode.c中),写完数据之后会将page页标识为dirty,后台writeback线程会将dirty page刷新到设备 */  
  59.         status = a_ops->write_end(file, mapping, pos, bytes, copied,  
  60.                         page, fsdata);  
  61.         if (unlikely(status < 0))  
  62.             break;  
  63.         copied = status;  
  64.  
  65.         cond_resched();  
  66.  
  67.         iov_iter_advance(i, copied);  
  68.         if (unlikely(copied == 0)) {  
  69.             /*  
  70.              * If we were unable to copy any data at all, we must  
  71.              * fall back to a single segment length write.  
  72.              *  
  73.              * If we didn't fallback here, we could livelock  
  74.              * because not all segments in the iov can be copied at  
  75.              * once without a pagefault.  
  76.              */  
  77.             bytes = min_t(unsigned long, PAGE_CACHE_SIZE - offset,  
  78.                         iov_iter_single_seg_count(i));  
  79.             goto again;  
  80.         }  
  81.         pos += copied;  
  82.         written += copied;  
  83.  
  84.         balance_dirty_pages_ratelimited(mapping);  
  85.         if (fatal_signal_pending(current)) {  
  86.             status = -EINTR;  
  87.             break;  
  88.         }  
  89.     } while (iov_iter_count(i));  
  90.  
  91.     return written ? written : status;  

 

2016-12-27 18:06:23 ZDF19 阅读数 452
  • hadoop全分布式集群配置

    Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性 的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序 的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。本课程讲解搭建hadoop集群的全过程,从安装vmware,centos开始,从本地模式,伪分布式到全分布式,讲解实现经典案例wordcount结束,全方位讲解hadoop的操作细节,带你完成hadoop的安装和入门

    858 人正在学习 去看看 冯文凯

5.2打开文件

  在操作系统中就是确定进程操作哪个文件。这个确定过程由两个事件构成:

  1.将用户进程task_struct中的*filp[20]与内核中的file_table[64]进行挂接。

  2.将用户进程需要打开的文件对应的i节点在file_table[64]中进行登记。

  *filp[20]:掌控一个进程可以打开的文件,既可以打开多个不同的文件,也可以同一个文件多次打开,每打开一次文件(不论是否是同一个),就要在*filp[20]中占用一个项记录指针。一个进程可以同时打开的文件次数不能超过20次。

  file_table[64]是管理所有进程打开文件的数据结构,不但记录了不同的进程打开不同的文件,也记录了不同的进程打开同一个文件,还记录了同一个进程多次打开同一个文件。与*filp[20]类似,只要打开一次文件,就要在file_able[64]中记录.(所以一个进程最多打开20次文件,但所有进程打开文件的总和不能超过64?)

  文件的i节点:是记载文件属性的最关键的数据结构。在操作系统中i节点和文件是一一对应的,找到i节点就能唯一确定文件。

  inode_table[32]:内核通过inode_table[32]掌控正在使用的文件i节点数,每个被使用的文件i节点都要记录在其中。

  打开文件的本质就是要建立*filp[20],file_table[64],inode_table[32]三者之间的关系。

  建立关系的这个过程分为三个步骤:

   1.将用户进程task_struct中的*filp[20]与内核中的file_table进行挂接。

   2.以用户给定的路径名“/mnt/user/user1/user2/hello.txt”为线索,找到hello.txt文件的i节点。

   3.将hello.txt对应的i节点在file_table[64]中进行登记。

  具体的操作是在进程中调用open()函数实现打开文件,该函数最终映射到sys_open()系统调用函数执行。映射过程以及sys_open()函数的基本执行情况已经在4.4.1节。

 5.2.1将进程的*filp[20]与file_table[64]挂接

 5.2.2获取文件i节点

 5.2.3将文件i节点与file_table[64]挂接

5.3读文件

  读文件就是从用户进程打开的文件中读取数据,读文件由read函数完成。

 5.3.1确定数据块在外设中的位置

 5.3.2将数据块读入缓冲块

 5.3.3将缓冲块中的数据复制到进程空间

5.4新建文件

  新建文件就是根据用户进程要求,创建一个文件系统中不存在的文件。新建文件由creat()函数实现。

 5.4.1 查找文件

 5.4.2 新建文件i节点

  新建hello.txt 文件 i 节点,将会在user2目录文件(作出目录文件的示意图)中写入hello.txt文件对应的新目录项信息。

 5.4.3 新建文件目录项

  只要在user2目录文件中寻找到空闲项,就在此位置加载新目录项,如果缺失找不到空闲项,就在外设上创建新的数据块来加载(此过程示意图类似P234所示)。

5.5 写文件

  操作系统对写文件操作的规定是:进程空间的数据先要写入缓冲区中,然后操作系统在适当条件下,将缓冲区中的数据同步到外设上。而且,操作系统只能以数据块(1 KB)为单位,将缓冲区中的缓冲块(1 KB) 的数据同步到外设上。这就需要在同步之前,缓冲块与外设上要写入的逻辑块进行一对一绑定,确定外设上的写入位置,以此保证用户空间写入缓冲块的数据能够准确地同步到指定逻辑块中。

  首先介绍如何确定绑定关系

 5.5.1 确定文件的写入位置

 5.5.2 申请缓冲块

 5.5.3 将指定的数据从进程空间复制到缓冲块

 5.5.4 数据同步到外设的两种方法

5.6 修改文件

 5.6.1 重定位文件的当前操作指针

 5.6.2 修改文件

5.7 关闭文件

  关闭文件对应的是打开文件,是在close()函数中完成的。

 5.7.1 当前进程的filp与file_table[64]脱钩

 5.7.2 文件i节点被释放

5.8 删除文件

  删除文件对应的是新建文件。删除文件与关闭文件有所不同:关闭文件只是解除当前进程与hello.txt文件在file_table[64]中指定挂接点的关系,而删除操作的效果表现为所有进程都无法访问到hello.txt这个文件。

 5.8.1 对文件的删除条件进行检查

 5.8.2 进行具体的删除工作

5.9 本章小节

  操作系统对文件的一切操作,都可以分为两个方面:对super_block,d_super_block,m_inode,d_inode,i节点位图,逻辑块位图这类文件管理信息的操作以及对文件数据内容的操作。新建,打开,关闭,删除文件属于对文件管理信息的操作。读文件,写文件和修改文件则主要是操作文件数据内容。

  操作系统管理信息就是建立或解除进程与文件的关系链条,链条的主干为task_struct中的*filp[20]——file_table[64]——inode_table[32].进程就可以沿着关系链条,依托缓冲区与硬盘进行数据交互。当关系链条解除后,进程则不再具备操作指定文件的能力。如果文件管理被更改,则操作系统要将此更改落实在硬盘上,以免失去对文件数据内容的控制。












没有更多推荐了,返回首页