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实现

Udev 和 Sysfs啥关系?
Udev: Monitors sysfs for device changes, and creates device nodes for hardware. Udev relies on sysfs to export device information.
devfs和udev分别是Linux 2.4和Linux 2.6以后的内核生成设备文件节点的方法,前者运行于内核空间,后者运行于用户空间。udev已经淘汰了devfs。
Sysfs: Exports hardware information to user space, and is managed by the kernel. Sysfs is used by utilities like udev to access hardware information and driver details.
Udev can use sysfs information to create device nodes that match the hardware. For example, udev rules can read or write files in the sysfs directory to change settings on new devices.

Linux应用层与内核通信方式
原文链接:https://blog.csdn.net/zichuanning520/article/details/125074205
Linux系统中,应用层位于用户空间,内核位于内核空间。在ARM 体系的32位处理器中,地址统一编址,内核位于低地址空间(0–1G),剩下的是用户空间。应用和内核分属不同的地址空间,处理器处于不同的权限状态。因此,数据不能随意在两者之间传输。
应用与内核通信方式有以下几种:

  1. 系统调用
    应用与内核通信的方式中,最常用的就是系统调用。如常见的open、write、read、ioctl等系统调用。当系统调用时,触发一个软中断,处理器进入内核模式。参数传递到内核空间,完成通信过程。内核完成服务之后,将数据返回给应用,并设置处理器为用户模式。
    2.虚拟文件系统
    Linux中,一切皆文件,处理器的各种信息也是以文件的形式供应用程序访问。proc和sys,这两个目录是内核在启动时自动生成的,并不位于磁盘中。
    /proc:以文件系统的方式为用户提供访问系统内核数据的操作接口。proc文件系统是一种内核和内核模块用来向进程(process)发送信息的机制,因此被称为 proc。
    /sysfs:这是由内核在运行时导出的,目的就是通过文件系统展示出设备、驱动和总线等层次关系。
    二者区别:proc只读,sys可读可写,写入即是对系统进行设置。
  2. netlink
    本质上是socket,可以用于内核与多种用户进程之间的消息传递系统。和常用的TCP、UDP一样使用socket,socket配置不同。
  3. 内存映像
    mmap共享内存。Linux通过mmap的把内核中特定部分的内存空间映射到用户级程序的内存空间去,从而提供了用户程序对内存直接访问的能力。该方式尤其适合在那些内核和用户空间需要快速大量交互数据的情况下。
  4. 信号
    内核在运行中,可以向应用层发出各种信号。

关于select/poll/epoll:
https://copyconstruct.medium.com/the-method-to-epolls-madness-d9d2d6378642

关于bottom-half
Linux实现底半部的机制主要有tasklet、工作队列、软中断softirq和线程化irq。其中tasklet是基于softirq实现。

关于标准输入
如果命令能够让终端阻塞,说明该命令接收标准输入,反之就是不接受,比如你只运行cat命令不加任何参数,终端就会阻塞,等待你输入字符串并回显相同的字符串。
$ echo ‘hello world’ | cat
hello world
而rm命令就不能让终端阻塞,所以这样操作是错误的
$ where connect.sh | rm
正确的做法应该是这样的:
$ rm $(where connect.sh)

关于uboot
https://zhuanlan.zhihu.com/p/129129087
U-boot的启动流程
1、汇编阶段
A、初始化关键硬件:关闭看门狗、中断、MMU和Cache(缓存)等,开启时钟、串口、Flash和内存等。
目的:为了U-boot稳定性,关掉不必要或影响稳定性的硬件,打开运行U-boot必须的硬件。即通过使U-boot运行单纯化,从而保证U-boot的稳定性。
B、U-boot自搬移:U-boot自己将自己从Flash搬移到内存(RAM)运行。
目的:提高U-boot的运行速度。因为内存要比Flash速度快。
2、C语言阶段:
A、初始化大部分硬件;
B、将Linux内核(Kernel)从Flash中“搬移”到内存中运行;
C、运行内核(Kernel)。

另外一个回答:
这一过程可以分为两个阶段,各个阶段的功能如下:
第一阶段的功能:

硬件设备初始化;
加载u-boot第二阶段的代码到RAM空间;
设置好栈;
跳转到u-boot第二阶段代码的入口处;
第二阶段的功能:

初始化本阶段使用的硬件设备;
检测系统内存映射;
将Linux内核从Flash读取到RAM中;
为Linux内核设置启动参数;
调用Linux内核。

Acronyms:
LTS: Long Term Support version
LVM: Logical Volume Management

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值