July 14, 2012 / filesystem, Hadoop, HDFS

前提 Hadoop版本:hadoop-0.20.2 概述 之前已对HDFS的datanode部分的源码进行了分析,还剩client和namenode这个最最重要的部分,本着从简单入手,打算继续把namenode当成是一个黑盒,先分析client的代码,毕竟client的代码行数与namenode相比要少得多。 很粗地把client的代码浏览了一下,发现client暴露给用户的接口是DistributedFileSystem这个东东,该类实现了FileSystem这个通用文件系统的抽象类。FileSystem位于core中,并不是HDFS专用的,先对FileSystem进行分析,有助于从宏观上去剖析DFS。 本以为把FileSystem这个类的代码看一遍应该就差不多了,看着看着才发觉FileSystem这个类好庞大、关联依赖的类好多,从下面的类图就可以看出来。虽说类多、方法多,但逻辑相对简单,比较容易理解。 本文所涉及到的类的包结构如下: org.apache.hadoop.fs BlockLocation ContentSummary FileChecksum FileSystem FileStatus FSDataInputStream FSDataOutputStream FSPermission GlobExpander Path PositionedReadable Seekable Syncable org.apache.hadoop.fs.permission FsAction FsPermission 下面将简单介绍几个重要的类。 FileSystem 上文已说过FileSystem是一个通用文件系统的抽象基类,它可能被实现为分布式文件系统或本地文件系统。 先来看其重要成员: CACHE: 静态成员,对打开的文件系统实例做cache,在计算机领域里面,cache是非常重要的,做文件系统怎么能少得了它呢! statisticsTable: 静态成员,保存各文件系统实例的统计信息 key: 文件系统实例在CACHE中的键 statistics: 文件系统实例在读写统计 deleteOnExit: 退出时需要删除的文件,这个功能很实用,Java里的文件也有这么个功能 clientFinalizer: 一个线程,用于在退出时关闭所有缓存的文件系统 从其成员,我们可以看到FileSystem有两个功能:缓存和统计。 下面我们来瞧瞧FileSystem的方法,总共有70多个,够吓人的,不过不能被吓倒了,要硬着头皮去看看究竟在做些什么事情,传统的文件系统里都会有的创建目录、创建文件、打开文件、列举目录的文件、重命名文件、关闭文件等功能都覆盖到,除此还有其它一些重要的方法: getFileBlockLocations: 取得文件中某个区域的内容所在块(可能会存储在多个块中)的位置 exists: 检查路径是否存在 isFile: 检查给定路径是否是一个文件 getContentSummary: 取得给定路径的统计情况,包括文件总大小、文件数目和目录数目,会递归统计子目录的情况 listStatus: 如果给定路径是目录,列举该目录的文件和子目录的状态 globStatus: 返回匹配特定模式的所有文件,跟Linux的命令行很像,可以使用通配来扩展 getHomeDirectory: 取得用户的主目录 *etWorkingDirectory: 设置和取得当前工作目录 copyFromLocalFile:

read more »
July 5, 2012 / Driver, Linux, Reading Note, TTY

简介 tty设备的名称是从过去的电传打字机缩写而来,最初是指连接到Unix系统上的物理或虚拟终端 Linux tty驱动程序的核心紧挨在标准字符设备驱动层之下,并提供了一系列的功能,作为接口被终端类型设备使用 有三种类型的tty驱动程序:控制台、串口和pty /proc/tty/drivers 当前注册并存在于内核的tty设备在/sys/class/tty下都有自己的子目录 小型TTY驱动程序 <linux/tty_driver.h> struct tty_driver tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS); static struct tty_operations serial_ops = {.open=tiny_open, .close=tiny_close, .write=tiny_write, .write_room=tiny_write_room, .set_termios=tiny_set_termios,} tiny_tty_driver->owner=THIS_MODULE; tiny_tty_driver->driver_name=“tiny_tty”; tiny_tty_driver->name=“ttty”; tiny_tty_driver->devfs_name=“tty/ttty%d”; tiny_tty_driver->major=TINY_TTY_MAJOR; tiny_tty_driver->type=TTY_DRIVER_TYPE_SERIAL; tiny_tty_driver->subtype=SERIAL_TYPE_NORMAL; tiny_tty_driver->flags=TTY_DRIVER_REAL_RAW|TTY_DRIVER_NO_DEVFS; tiny_tty_driver->init_termios=tty_std_termios; tiny_tty_driver->init_termios.c_cflag=B9600|CS8|CREAD|HUPCL|CLOCAL; tty_set_operaions(tiny_tty_driver, &serial_ops); retval = tty_register_driver(tiny_tty_driver); for (i=0;i<TINY_TTY_MINORS; ++i) tty_unregister_device(tiny_tty_driver, i); tty_unregister_driver(tiny_tty_driver); termios结构 用来提供一系列安全的设置值 struct termios tcflag_t c_iflag; tcflag_t c_oflag; tcflag_t c_cflag; tcflag_c c_lflag; cc_t c_line;

read more »
July 5, 2012 / Driver, Linux, network, Reading Note

简介 网络接口是第三类标准Linux设备,本章将描述网络接口是如何与内核其余的部分交互的 网络接口必须使用特定的内核数据结构注册自身,以备与外界进行数据线包交换时调用 对网络接口的常用文件操作是没有意义的,因此在它们身上无法体现Unix的“一切都是文件”的思想 网络驱动程序异步自外部世界的数据包 网络设备向内核请求把外部获得的数据包发送给内核 Linux内核中的网络子系统被设计成完全与协议无关 在网络世界中使用术语“octet”指一组8个的数据位,它是能为网络设备和协议所能理解的最小单位 协议头(header)是在数据包中的一系列字节,它将通过网络子系统的不同层 连接到内核 loopback.c、plip.c和e100.c 设备注册 驱动程序对每个新检测到的接口,向全局的网络设备链表中插入一个数据结构 <linux/netdevice.h> struct net_device struct net_device alloc_netdev(int sizeof_priv, const char name, void (setup) (struct net_device )); name是接口的名字,这个名字可以使用类似printf中%d的格式,内核将用下一个可用的接口号替代%d <linux/etherdevie.h> struct net_device *alloc_etherdev(int sizeof_priv); 光纤通道设备使用alloc_fcdev(<linux/fcdevice.h>) FDDI设备使用alloc_fddidev(<linux/fddidevice.h>) 令牌环设备使用alloc_trdev(<linux/trdevice.h>) register_netdev函数 初始化每个设备 example ether_setup(dev); dev->open = open_function; dev->stop = release_function; dev->set_config = config_function; dev->hard_start_xmid = tx_function; dev->do_ioctl = ioctl_function; dev->get_stats = stats_function; dev->rebuild_header

read more »
July 4, 2012 / Driver, Linux, Reading Note, USB

简介 通用串行总线(USB)是主机和外围设备之间的一种连接 从拓扑上来看,一个USB子系统并不是以总线的方式来布置的;它是一棵由几个点对点的连接构建而成的树 这些连接是连接设备和集线器(hub)的四线电缆(地线、电源线和两根信号线) USB协议规范定义了一套任何特定类型的设备都可以遵循的标准,如果一个设备遵循该标准,就不需要一个特殊的驱动程序 Linux内核支持两种主要类型的USB驱动程序 宿主系统上的驱动程序 控制插入其中的USB设备 设备上的驱动程序 控制该设备如何作为一个USB设备和主机通信 USB设备基础 Linux内核提供了一个称为USB核心的子系统来处理大部分的复杂性 端点 USB通信最基本的形式是通过一个名为端点(endpoint)的东西 USB端点只能往一个方向传送数据,从主机到设备(输出端点)或者从设备到主机(输入端点) USB端点有四种不同的类型 控制 控制端点用来控制对USB设备不同部分的访问 每个USB设备都有一个名为“端点0”的控制端点 中断 每当USB宿主要求设备传输数据时,中断端点就以一个固定的速率来传送少量的数据 批量 批量(bulk)端点传输大批量的数据 等时 等时(isochronous)端点同样可以传送大批量的数据,但数据是否到达是没有保证的 控制和批量端点用于异步的数据传输,只要驱动程序决定使用它们 内核中使用struct usb_host_endpoint结构体来描述USB端点,该结构体在另一个名为struct usb_endpoint_descriptor的结构体中包含了真正的端点信息 struct usb_host_endpoint bEndpointAddress bmAttributes wMaxPacketSize bInterval 接口 USB端口被捆绑为接口 USB接口只处理一种USB逻辑连接 struct usb_interface struct usb_host_interface *altsetting unsigned num_altsetting struct usb_host_interface *cur_altsetting int minor 配置 USB接口本身被捆绑为配置 一个USB设备可以有多个配置,而且可以配置之间切换以改变设备的状态 struct usb_host_config struct usb_device include/linux/usb.h. USB设备是非常复杂的,它由许多不同的逻辑单元组成 设备通常具有一个或者更多的配置

read more »

简介 一个块设备驱动程序主要通过传输固定大小的随机数据来访问设备 Linux内核视块设备为与字符设备相异的基本设备类型 Linux块设备驱动程序接口使得块设备可以发挥其最大的功效,但是其复杂程序又是编程者必须面对的一个问题 一个数据块指的是固定大小的数据,而大小的值由内核确定 数据块的大小通常是4096个字节,但是可以根据体系结构和所使用的文件系统进行改变 与数据块对应的是扇区,它是由底层硬件决定大小的一个块,内核所处理的设备扇区大小是512字节 如果要使用不同的硬件扇区大小,用户必须对内核的扇区数做相应的修改 注册 注册块设备驱动程序 <linux/fs.h> int register_blkdev(unsigned int major, const char *name); 如果需要的话分配一个动态的主设备号 在/proc/devices中创建一个入口项 int unregister_blkdev(unsigned int major, const char *name); 注册磁盘 struct block_device_operations int (open) (struct inode inode, struct file *filp); int (release) (struct inode inode, struct file *filp); int (ioctl) (struct inode inode, struct file *filp, unsigned int cmd, unsigned long arg);

read more »

简介 许多类型的驱动程序编程都需要了解一些虚拟内存子系统如何工作的知识 当遇到更为复杂、性能要求更为苛刻的子系统时,本章所讨论的内容迟早都要用到 本章的内容分成三个部分 讲述mmap系统调用的实现过程 讲述如何跨越边界直接访问用户空间的内存页 讲述了直接内存访问(DMA)I/O操作,它使得外设具有直接访问系统内存的能力 Linux的内存管理 地址类型 Linux是一个虚拟内存系统,这意味着用户程序所使用的地址与硬件使用的物理地址是不等同的 有了虚拟内存,在系统中运行的程序可以分配比物理内存更多的内存,甚至一个单独的进程都能拥有比系统物理内存更多的虚拟地址空间 下面是一个Linux使用的地址类型列表 用户虚拟地址 这是在用户空间程序所能看到的常规地址 物理地址 该地址在处理器和系统内存之间使用 总线地址 该地址在外围总线和内存之间使用,通常它们与处理器使用的物理地址相同 内核逻辑地址 内核逻辑地址组成了内核的常规地址空间 在大多数体系架构中,逻辑地址和与其相关联的物理地址不同,仅仅在它们之间存在一个固定的偏移量 kmalloc返回的内存就是内核逻辑地址 内核虚拟地址 和内核逻辑地址的相同之处在于,它们都将内核空间的地址映射到物理地址上 内核虚拟地址与物理地址的映射不必是线性的一对一的 所有的逻辑地址都是内核虚拟地址,但是很多内核虚拟地址不是逻辑地址 vmalloc分配的内存具有一个虚拟地址 <asm/page.h> __pa() 返回其对应的物理地址 __va() 将物理地址逆向映射到逻辑地址,但这只对低端内存页有效 物理地址和页 物理地址被分成离散的单元,称之为页 <asm/page.h> PAGE_SIZE 目前大多数系统都使用每页4096个字节 高端与低端内存 使用32位系统只能在4GB的内存中寻址 内核将4GB的虚拟地址空间分割为用户空间和内核空间,一个典型的分割是将3GB分配给用户空间,1GB分配给内核空间 低端内存 存在于内核空间上的逻辑地址内存 高端内存 那些不存在逻辑地址的内存 内存映射和页结构 <linux/mm.h> struct page atomic_t count; 对该页的访问计数。当计数值为0时,该页将返回给空闲链表 void *virtual; 如果页面被映射,则指向页的内核虚拟地址;如果未被映射则为NULL unsigned long flags; 描述页状态的一系列标志 PG_locked表示内存中的页已经被锁住 PG_reserved表示禁止内存管理系统访问该页

read more »

简介 2.6内核的设备模型提供一个对系统结构的一般性抽象描述,用以支持多种不同的任务 电源管理和系统关机 与用户空间通信 热插拔设备 设备类型 对象生命周期 kobject、kset和子系统 kobject是组成设备模型的基本结构 对象的引用计数 sysfs表述 数据结构关联 热插拔事件处理 kobject基础知识 <linux/kobject.h> 嵌入的kobject 内核代码很少去创建一个单独的kobject对象,kobject用于控制对大型域相关对象的访问 kobject的初始化 首先将kobject设置为0,通常使用memset void kobject_init(struct kobject *kobj); int kobject_set_name(struct kobject kobj, const char format, …); ktype、kset和parent 对引用计数的操作 struct kobject kobjct_get(struct kobject kobj); void kobject_put(struct kobject *kobj); release函数和kobject类型 void my_object_readse(struct kobject *kobj) struct my_object *mine = container_of(kobj, struct my_object, kobj); kfree(mine); struct kobj_type void (release)

read more »
June 29, 2012 / Driver, Linux, PCI, Reading Note

简介 本章给出一个高层总线架构的综述 讨论重点是用于访问Peripheral Component Interconnect(PCI,外围设备互联)外设的内核函数 PCI总线是内核中得到最好支持的总线 本章主要介绍PCI驱动程序如果寻找其硬件和获得对它的访问 本章也会介绍ISA总线 PCI接口 PCI是一组完整的规范,定义了计算机的各个不同部分之间应该如何交互 PCI规范涵盖了与计算机接口相关的大部分问题 PCI架构被设计为ISA标准的替代品,有三个主要目标 获得在计算机和外设之间传输数据时更好的性能 通过使用比ISA更高的时钟频率,PCI总线获得了更好的性能,它的时钟频率一般是25或者33MHz(实际的频率是系统时钟的系数),最新的实现达到了66MHz甚至133MHz 配备了32位的数据总线,而且规范已经包括了64位的扩展 尽可能的平台无关性 简化往系统中添加和删除外设的工作 PCI设备是无跳线设备,可以引导阶段自动配置 * 每个PCI外设由一个总线编号、一个设备编号及一个功能编号来标识 * PCI规范允许单个系统拥有高达256个总线,但是256个总线对于许多大型系统而言是不够的,因此,Linux目前支持PCI域 * 每个PCI域可以拥有最多256个总线 * 每个总线上可支持32个设备,每个设备可以是多功能板,最多可有八种功能 * 每种功能都可以在硬件级由一个16位的地址来标识 * 为Linux编写的设备驱动程序可以使用一种特殊的数据结构(pci_dev)来访问设备 * 当前的工作站一般配置有至少两个PCI总线,在单个系统中插入多个总线,可通过桥(bridge)来完成,它是用来连接两个总线的特殊PCI外设 * PCI系统的整体布局组织为树型,其中每个总线连接到上一线总线,直到树根的0号总线 * lspci * proc/pci * /proc/bus/pci/ * 查看PCI设备清单和设备的配置寄存器 * /sys/bus/pci/devices * 每个外设板的硬件电路对如下三种地址空间的查询进行应答 * 内存位置 * I/O端口 * 配置寄存器 * 前两种地址空间由同一PCI总线上的所有设备共享 * 配置空间利用了地理寻址 * 配置查询每次只对一个槽寻址 *

read more »
June 27, 2012 / Data Types, Driver, Kernel, Linux, Reading Note

简介 由于Linux的多平台特性,任何一个重要的驱动程序都应该是可移植的 与内核代码相关的核心问题是应该能够同时访问已知长度的数据项,并充分利用不同处理器的能力 内核使用的数据类型主要被分成三类 类似int这样的标准C语言类型 类似u32这样的有确定大小的类型 像pid_t这样的用于特定内核对象的类型 本章将讨论在什么情况下使用这三种类型以及如何使用 使用标准C语言类型 当我们需要“两个字节的填充符”或者“用四个字节字符串表示的某个东西”时,我们不能使用标准类型,因为在不同的体系架构上,普通C语言的数据类型所占空间在大小并不相同 内核中的普通内存地址通常是unsigned long,在当前Linux支持的所有平台上,指针和long整形的大小总是相同的 C99标准定义了intptr_t和uintptr_t类型,它们是能够保存指针值的整形变量 为数据项分配确定的空间大小 <asm/types.h> <linux/types.h> u8, s8 u16, s16 u32, s32 u64, s64 如果一个用户空间程序需要使用这些类型,它可以在名字前加上两个下划线作为前缀 __u8 __u32 使用新编译器的系统将支持C99标准类型,如uint8_t和uint32_t 接口特定的类型 内核中最常用的数据类型由它们自己的typedef声明,这样可以防止出现任何移植性问题 “接口特定(interface-specific)”是指由某个库定义的一种数据类型,以便为某个特定的数据结构提供接口 完整的_t类型在<linux/types.h>中定义 _t数据项的主要问题是在我们需要打印它们的时候,不太容易选择正确的printk或者printf的输出格式 其他有关移植的问题 一个通用的原则是要避免使用显式的常量值 时间间隔 使用jiffies计算时间间隔的时候,应该用HZ来衡量 页大小 内存页的大小是PAGE_SIZE字节 PAGE_SHIFT <asm/page.h> getpagesize库函数 get_order函数 字节序 <asm/byteorder.h> __BIG_ENDIAN __LITTLE_ENDIAN u32 cpu_to_le32 (u32); u32 le32_to_cpu(u32); be64_to_cpu le16_to_cpus cpu_to_le32p 数据对齐 <asm/unaligned.h> get_unaligned(ptr); put_unaligned(val,

read more »
June 27, 2012 / Driver, Interrupt, Linux, Reading Note

简介 通常,很多设备的处理速度要比处理器慢得多,为了不让处理器一直等待外部事件,可以采用中断来让设备在产生事件时通知处理器 中断仅仅是一个信号,由硬件发出 准备并口 在没有节设定产生中断之前,并口是不会产生中断的 并口的标准规定设置端口2(px37a、0x27a或者其它端口)的第4位将启用中断报告,0x10 当处于启用中断状态,每当引脚10的电平发生从低到高改变时,并口就会产生一个中断 引脚9是并口数据字节中的最高位 安装中断处理例程 中断信号线是非常珍贵且有限的资源 内核维护了一个中断信号线的注册表,该注册表类似于I/O端口的注册表 模块在使用中断前要先请求一个中断通道,然后在使用后释放该通道 <linux/sched.h> int request_irq(unsigned int irq, irqreturn_t (handler) (int, void , struct pt_regs ), unsigned long flags, const char dev_name, void *dev_id); 返回0表示申请成功 flags SA_INTERRUPT 表明这是一个“快速”的中断处理例程 SA_SHIRQ 表示中断可以在设备之间共享 SA_SAMPLE_RANDOM 指出产生的中断能对/dev/random设备和/dev/urandom设备使用的熵池(entropy pool)有贡献 void free_irq(unsigned int irq, void *dev_id); int can_request_irq(unsigned int irq, unsigned long flags); 使用request_irq的正确位置应该是在设备第一次打开、硬件被告知产生中断之前 调用free_irq的位置是最后一次关闭设备、硬件被告知不用再中断处理器之后 /proc接口 /proc/interrupts 不依赖体系结构

read more »

« Newer Entries  
Older Entries »