Linux内核学习小结

网上学习总结的一些资料,加上个人的一些总结。

  1. Linux内核可以分成基础层和应用层。
    基础层包括数据结构,内核同步机制,内存管理,任务调度。
    应用层包括文件系统,设备和驱动,网络,虚拟化等。文件系统是应用层的基础。

  2. 内核基础层-数据结构
    双向链表
    hash链表
    红黑树: 用在IO调度算法中。参考deadline调度算法 deadline-iosched.c 用到了红黑树。
    radix树:以空间冗余换时间消耗,代码见 /lib/radix-tree.c。Page cache的管理用到了radix tree。

  3. 内核基础层-内存管理
    主要有伙伴系统内存管理和Slab内存管理两种。
    3.1 伙伴系统内存管理

    • 基于页面的内存管理 (1页=4096字节,每个页面地址上连续)。分配内存以页为单位。
    • 自动完成页面的分割与合并
    • alloc_pages(): 从伙伴系统申请内存。申请得到的是页面结构的指针,如果要得到内存地址,需要调用page_address()。_get_free_pages()可以直接得到内存地址。
      3.2 Slab系统内存管理
    • 基于对象的内存管理 (建立在伙伴系统之上)。分配内存不以页为单位。
    • 提供内存的构造函数和析构函数
    • kmem_cache_create: 创建Slab对象
    • kmem_cache_alloc: 申请内存
    • kmem_cache_free: 释放内存
      3.3 kmalloc()实质是Slab。内核事先已经申请了不同尺寸对象,封装成kmalloc()。
  4. 内核基础层-同步机制
    主要有自旋锁和信号量两种。
    4.1自旋锁 spin-lock
    我的理解是spin-lock主要是给多核同步用。如果是单核非抢占式,那么spin-lock没有什么作用。如果是单核抢占式,那么spin-lock起的作用是禁止抢占?。
    spin-lock如果没有抢到就busy-waiting,不会sleep。
    spin-lock可以用在interrupt-handler中,因为interrupt-handler不允许在里面sleep,spin-lock没抢到也不会sleep。
    4.2 信号量semaphore and mutex
    Semaphore计数可以多个,mutex计数只有一个。
    up: 释放信号量
    down: 获取信号量。如果没获取到就sleep
    down_trylock: 获取信号量。如果没获取到就立刻返回,不会进入sleep
    semaphore不可以用在interrupt-handler和tasklet等不能sleep的场景,因为Semaphore没抢到的话会sleep。
    对于可以sleep的场景,那就semaphore和spin-lock都可以用。spin-lock多用在轻量级场景。
    4.3 原子变量 - 读写不会被打断。用在简单的数据的读写上,比如多个进程读写一个公共int变量。
    atmoic_add - 加一个整数到原子变量
    atomic_sub - 减一个整数到原子变量
    atomic_set - 设置原子变量的数值
    atomic_read - 读取原子变量的数值
    4.4 completion - 提供一种等待完成的机制。类似信号量。
    wait_for_completion - 等待操作完成
    complete - 完成的信号
    4.5 CPU变量 - 每个CPU都有一个变量的备份
    DEFINE_PER_CPU
    4.6 RCU锁 - 免锁机制
    call_rcu
    rcu_read_lock
    4.7 顺序锁 - 适用于读多写少的场合
    read_seqbegin
    write_seqlock

5. 内核基础层 - 任务调度
5.1 被动调度
- 执行完中断后返回时执行调度,进程可能被切换
- 执行完系统调用后返回用户空间时执行调度(老式方法也是通过中断进行调度)
5.2 主动调度
- 进程在内核运行,如果需要可以主动调度。
- might_sleep: 代码执行时间较长,主动调度一下
- wait_event: 进程需要满足某些条件,没有满足的时候就阻塞自己(睡眠态)。

6. 内核基础层 - 中断和软中断
中断执行代码分为中断上下文和软中断上下文(bottom half?)。中断上下文里执行的通常是跟硬件关系比较紧密的代码,跟硬件关系不紧密的可以放在软中断上下文中执行。
6.1 内核定义的缺省软中断:共6种。
BLOCK_SOFTIRQ: 块设备软中断
NET_TX_SOFTIRQ: 网络发送处理软中断
NET_RX_SOFTIRQ: 网络接收处理软中断
TASKLET_SOFTIRQ: tasklet软中断
6.2 块设备软中断的初始化
open_softirq(BLOCK_SOFTIRQ, blk_done_softirq, NULL);
6.3 块设备软中断的启动:在中断处理函数中启动。
void blk_complete_request(struct request *req)
{
struct list_head *cpu_list;
unsigned_long flags;
local_irq_save(flags);
cpu_list = &__get_cpu_var(blk_cpu_done);
list_add_tail(&req->donelist, cpu_list);
raise_softirq_irqoff(BLOCK_SOFTIRQ);
local_irq_restore(flags);
}
6.4 tasklet是特殊的软中断。同一个时刻一个tasklet只能有一个CPU执行(tasklet 维护一个链表,一个一个序列化执行),不同的tasklet可以在不同的CPU上执行。
tasklet不需要考虑重入问题。别的软中断需要考虑重入问题,因为一个软中断可以在多个CPU上执行。
软中断和tasklet都是不可sleep的,因为一旦被中断,它们的上下文没有被记录下来,所以它们不能被调度。记住进程和线程是调度的基本单位,可以sleep,因为一旦被中断,其上下文会被记录下来。
所以软中断和tasklet不能用semaphore,因为semaphore可能sleep,被调度了就回不来了。

  • tasklet定义
    DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);
  • 启动tasklet
    tasklet_enable(&hil_mlcs_tasklet);
  • tasklet调度
    tasklet_schedule(&hil_mics_tasklet);

7.内核基础层 - 工作队列
工作队列类似tasklet。它有自己的进程上下文,可sleep,可以被调度,而软中断和tasklet不可以sleep。
7.1 工作队列初始化
INIT_WORK(&ioc->sas_persist_task, mptsas_persist_clear_table, (void *)ioc);
7.2 工作队列启动:把用户定义的work_struct加入系统的队列中,并唤醒系统线程去执行。
schedule_work(&ioc->sas_persist_task);

8. 内核应用层 - 文件系统
8.1 VFS - 把硬盘中的文件抽象到内存中,通过dentry, inode, superblock等结构来实现。
8.2 superblock - 文件系统自身的控制结构,给出了文件系统的全局信息
s_blocksize: 文件系统的块大小
s_maxbytes: 文件系统中最大文件的尺寸
s_type: 指向file_sytem_type结构的指针
s_magitc: 每个文件系统的魔术数
s_root: 指向文件系统根dentry的指针。
8.3 一些链表头用来连接文件系统内的重要成员
s_inode: 指向文件系统内的所有inode,可以用来遍历inode对象。每个文件有一个inode对象。
s_dirty: 指向所有dirty的inode对象
s_bdev: 指向文件系统存在的块设备指针
8.4 dentry反映了文件系统的树状关系
inode描述了文件本身的一些信息,dentry描述了这个文件在哪个目录下面。
d_inode: 指向一个inode结构。这个inode和dentry共同描述了一个普通文件或者目录文件。
d_subdirs: 子项链表头,所有的子项都要链接到这个链表。与下面的d_child合起来用。
d_child: dentry自身的链表头,需要链接到父dentry(即所在的目录)的d_subdirs成员
d_parent: 指向父dentry结构。
d_hash: 链接到dentry cache的hash 链表
d_name: 文件或者目录的名字
d_mounted: 表示dentry是否一个挂载点

8.5 inode代表一个文件

  • 一个文件可以有多个dentry,但只能有一个inode。
  • 保存了文件的大小,创建时间,文件的块大小等参数
  • 保存了文件的读写函数,文件的读写缓存等信息。
  • 成员解释:
    • i_list, i_sb_list, i_dentry是3个链表头。i_list用来链接描述inode当前状态的链表,i_sb_list用来链接到superblock中的inode链表。
    • 一个文件可以对应dentry,这些dentry都链接到i_dentry链表。
    • i_ino是inode的号码
    • i_count是inode的引用计数。
    • i_size是以字节为衡量单位的文件长度。
    • i_blkbits是文件块的位数。
    • i_fop是一个struct file_operation类型的指针。该结构提供文件的读写函数和异步io函数。每个具体的文件系统基本都要提供各自的文件操作函数。
    • i_mapping结构目的是缓存文件内容。读的时候如果已经有缓存,就直接读缓存。写的时候也可以先写进缓存,以后再写进硬盘。

8.6 文件系统基本概念 - file

  • file 跟进程有关。描述进程和文件交互的关系。每个进程指向一个文件描述符表。同一个文件被不同进程打开,就会有多个file?? inode代表文件的信息,跟进程无关。
  • 成员解释
    • f_dentry: 指向文件对应的dentry结构
    • f_vfsmnt: 指向文件所属的文件系统的vfsmount对象
    • f_pos: 表示进程对文件操作的位置。如果进程对文件读取了前10个字节,f_pos指向第11个字节的位置。
    • f_uid: 文件的用户id
    • f_gid: 文件的组id
    • f_ra: 文件预读的位置
    • f_mapping: 指向一个address_space结构。该结构封装了文件的读写缓存页面。

8.7 文件系统的架构 - superblock的作用

  • 文件在打开时都需要在内存中分配一个inode结构,这些inode结构都要链接到超级块。

8.8 文件系统的例子 - 模块编译和安装
模块编译和安装
- 模块安装 insmod *.ko
- 模块卸载 rmmod *.ko

8.9 最简单文件系统可以分为三部分
1. register_filesystem()把XXX文件系统登记到系统
2. kern_mount()为XXX文件系统申请必要的数据结构(超级块, inode, dentry)
3. 在XXX文件系统内建目录,每个目录下建文件

8.10 创建文件系统总结:
- 创建超级块
- 填充超级块
- 创建根inode
- 创建根dentry

8.11 创建目录和文件总结:
- 创建dentry
- 创建inode

  1. sysfs

    • 基于内存的文件系统,不涉及硬盘读写。
    • 展示设备信息
      9.1 sysfs 打开过程
      • real_lookup -> sysfs_lookup
        9.2 sysfs_lookup: 为文件创建inode结构
        9.3 sysfs_attach_attr->sysfs_create: 为dentry结构绑定属性
        9.4 sysfs_open_file->check_perm->buffer (保存私有结构)
        9.5 sysfs_read_file->fill_read_buffer->(ops->show) 读文件,在内存中进行,不涉及硬盘
        9.6 sysfs_write_file->fill_write_buffer->(ops->store) 写文件,在内存中进行,不涉及硬盘
  2. Linux 进程管理
    11.1 进程原理

    • task_struct 结构
    • 进程优先级
    • 进程系统调用
      11.2 进程调度机制
    • 调度器实现schedule()
    • 完全公平调度类CFS
    • 实时调度类原理
    • 多核模式调度SMP
      11.3 进程竞争机制
    • RCU机制原理
    • 内存优化和屏障
    • 大内核锁
    • per_CPU计时器
      11.4 进程系统调用
    • 写时复制原理
    • 进程内存布局
    • 进程堆栈管理
    • 系统调用实现流程
  3. Linux device driver学习
    13.1 设备无关软件层

  • 对设备程序的统一接口。文件和设备采用统一命名。设备名称有一个索引节点,包含主设备号和从设备号。通过主设备号可以找到相应的驱动程序,通过从设备号可以确定具体的物理设备。
  • 对设备的保护机制跟文件系统一样,都采用rwx权限
  • 提供一个独立于设备的块。数据块的大小可能对不同设备大小不一样。
  • 为了解决数据交换速度匹配问题,采用缓冲
  • 块设备的存储分配也是由文件系统处理
  • 对于独占设备,分配和释放临界资源
    13.2 Linux设备驱动程序
  • CPU并不是系统中唯一智能设备,每个硬件物理设备都拥有自己的控制器。
  • Linux管理硬件控制器的代码由内核统一管理,这些代码就是设备驱动程序。
  • 用户请求设备进行输入输出流程
    用户进程->文件系统->设备驱动程序->设备控制器->设备本身
  • 当用户进程发出IO任务时,系统把请求处理的权限放在文件系统,文件系统再通过驱动程序提供的接口将任务
    下放到驱动程序,驱动程序再根据需要对设备控制器进行操作,设备控制器再去控制这个设备本身。
    13.3 Linux设备驱动程序功能
  • 对设备进行初始化
  • 使设备投入运行和退出服务
  • 从设备接收设备并将它们送回内核
  • 将数据从内核送到设备
  • 检测和处理设备出现的错误
    13.4 Linux驱动程序具有共性
  • 驱动程序属于内核代码
  • 为内核提供一个统一的接口
  • 驱动程序的执行属于内核机制并使用内核服务(内存分配,中断发送,等待队列…)
  • 动态加载
  • 可以配置
    13.5 I/O接口及设备控制器
    - 专用I/O接口(键盘接口,图形接口,鼠标接口,网络接口,磁盘接口等)
    - 通用I/O接口(并口,串口,PCMCIA接口,SCSI接口,USB等)
    13.6 设备文件
    • 每个设备除设备名以外,还有3个属性:类型,主设备号,从设备号
    • 设备文件我们通过mknod系统调用创建
      13.7 中断在驱动程序工作过程中的作用
    • 用户发出输入/输出请求
    • 调用驱动程序的read()/request(),将完成的输入/输出指令发送给设备控制器,现在设备驱动程序等待操作的发生
    • 通过一段时间之后,硬件设备准备好完成指令的操作,并产生中断信号标志时间的发送
    • 中断信号导致调用驱动程序的中断服务子程序,将所要数据从硬件设备复制到设备驱动程序的缓冲区中,并通知正在等待read()/request()现在数据可使用。
    • 在数据可供使用时,read()/request()现在可以将数据提供给用户进程。
      13.8 驱动DMA工作
    • 所有的PC机包含一个直接内存访问的控制器或DMAC的辅助处理器,可以用来控制RAM和I/O设备之间的数据传送。
    • DMAC一旦被CPU激活,就可以自动传送数据。当数据传送完毕之后,DMAC发出一个中断请求。
    • 当CPU和DMAC同时访问同一个内存单元时,所产生的冲突由一个内存仲裁器的硬件电路来解决。
      13.9 设备子系统原理
    • 字符设备,块设备,网络适配器
    • I/O原理

13.10 字符设备驱动
- file_operations原理
- 系统调用流程
- ioctl流程
- 请求中断
13.11 块设备驱动
- 资源管理
- I/O调度
- BIO结构原理
- PCI总线原理
13.12 网卡设备驱动
- net_device/net_device_ops
- sk_buff原理
- 网卡数据中断
- 网卡适配器映射
13.13 内核模块架构
- 模块添加与删除
- 自动化与热插拔
- 主从设备号
- 版本控制
13.14 内核线程和普通进程的区别
- 内核线程执行的是内核中的函数,而普通进程只有通过系统调用才能够执行内核中的函数
- 内核线程只运行在内核态,而普通进程既可以运行在用户态,也可以运行在内核态
- 因为内核线程只运行在内核态,它只能使用大于PAGE_OFFSET(3G)的地址空间。而普通进程不管是用户态还是内核态,都可以使用4GB地址空间。
- 内核线程是有kernal_thread()函数的内核态下进行创建。系统中大部分的内核线程是在系统的启动过程中建立的。

  1. 网络协议
    14.1 网络系统架构

    • 接收缓冲区
    • 发送缓冲区
    • netfilter
    • iptables
      14.2 网络协议栈
    • TCP/UDP
    • TCP控制块
    • IP协议
    • netlink机制
      14.3 系统API
    • POSIX网络API
    • epoll实现原理
    • socket初始化
    • 网络系统参数配置
  2. 内核组件专题
    15.1 时间管理

    • 通用时间子系统
    • 高分辨率定时器
    • 动态时钟结构
    • 定时器系统调用实现
      15.2 系统缓存
    • 页缓存实现
    • 块缓存实现
      15.3 数据同步
    • 数据同步原理
    • inode同步与拥塞
    • 强制回写与完全同步
  3. 文件系统专题
    16.1 虚拟文件系统VFS

    • VFS结构
    • 文件操作系统调用
    • file/inode原理
      16.2 无存储文件系统
    • proc文件系统
      • 文件系统数据结构
      • 管理/proc数据线
      • 系统控制机制
    • sysfs文件系统
      • sysfs数据结构
      • 装载文件系统
      • 文件目录操作
      • 向sysfs增加数据
        16.3 磁盘文件系统
    • Ext2 文件系统
    • Ext3 文件系统
    • Ext4 文件系统
      16.4 用户态文件系统
    • FUSE使用场景
    • FUSE原理
    • FUSE实现

你可能感兴趣的:(Linux,linux)