1.sysctl 参数防篡改 - 基于 ebpf 的详解析实现 [一]
2.Linux内核源码解析---cgroup实现之整体架构与初始化
3.Linux 中断( IRQ / softirq )基础:原理及内核实现
4.eBPF 程序编写 - libbpf
5.df- h命令代表什么意思?
sysctl 参数防篡改 - 基于 ebpf 的实现 [一]
系统调用参数防篡改 - 基于ebpf的实现
本文基于内核代码版本5..0进行讨论。
ebpf能够修改某些函数的和源返回值,但仅限于允许错误注入的码c码分函数,这限制了其应用范围。详解析系统tap能够作用于几乎任何函数,和源但由于内核API的码c码分reentrantlock源码不稳定,它在不同内核上可能无法运行。详解析
ebpf的和源优势在于其与内核交互的API保持稳定,特别是码c码分用于“系统调用参数防篡改”的一组helper函数。在阅读代码实现时,详解析发现中段插入了一个“BPF_CGROUP_RUN_PROG_SYSCTL”。和源
这一功能源于v5.2-rc1的码c码分commit,旨在限制容器对sysctl的详解析错误写入,要求内核版本不低于5.2,和源且配置项包含“CONFIG_CGROUP_BPF”。码c码分
配套的4个helper函数记录在内核文档“Documentation/bpf/prog_cgroup_sysctl.rst”中,用于读取sysctl参数名称和值、在参数修改时获取写入的值以及覆盖准备写入的值。这些helper与内核原生路径中的过滤函数交互。
使用示例
通过Linux内核源码中的“tools/testing/selftests/bpf”目录下的测试用例可以学习ebpf的使用。在源码根目录下执行make命令编译。
针对sysctl部分,测试用例主体为“test_sysctl.c”,用于将ebpf程序加载至内核,并在对应的点位上附加。ebpf程序可以是直接以ebpf汇编语法写的,也可以是C文件编译成.o二进制文件的形式。
当判断为write操作时,返回0,内核源码中决定sysctl参数读写结果的商城直播平台源码点位返回“-EPERM”,使得修改不成功。
ebpf prog源文件中的“SEC”宏定义用于指示编译器将函数/变量放在特定的section中,便于用户态loader查找和解析。
为了实现加载和附加程序,使用了“sysctl_write_deny_prog.o”作为附加程序,类型为“BPF_CGROUP_SYSCTL”,方式为“BPF_F_ALLOW_OVERRIDE”。借助“fd”这样的整形数字,用户态程序可以深入内核态获取对应的结构体实例。
最终通过libbpf封装系统调用接口,用户态程序可以通过“bpf”系统调用入口与内核交互。使用strace工具可以追踪这一过程。
了解ebpf helper函数的使用,可以借助现成的工具进行学习,更多详情请参考后续文章。
Linux内核源码解析---cgroup实现之整体架构与初始化
cgroup在年由Google工程师开发,于年被融入Linux 2.6.内核。它旨在管理不同进程组,监控一组进程的行为和资源分配,是Docker和Kubernetes的基石,同时也被高版本内核中的LXC技术所使用。本文基于最早融入内核中的代码进行深入分析。
理解cgroup的核心,首先需要掌握其内部的常用术语,如子系统、层级、cgroupfs_root、cgroup、css_set、可回收商城源码cgroup_subsys_state、cg_cgroup_link等。子系统负责控制不同进程的行为,例如CPU子系统可以控制一组进程在CPU上执行的时间占比。层级在内核中表示为cgroupfs_root,一个层级控制一批进程,层级内部绑定一个或多个子系统,每个进程只能在一个层级中存在,但一个进程可以被多个层级管理。cgroup以树形结构组织,每一棵树对应一个层级,层级内部可以关联一个或多个子系统。
每个层级内部包含的节点代表一个cgroup,进程结构体内部包含一个css_set,用于找到控制该进程的所有cgroup,多个进程可以共用一个css_set。cgroup_subsys_state用于保存一系列子系统,数组中的每一个元素都是cgroup_subsys_state。cg_cgroup_link收集不同层级的cgroup和css_set,通过该结构可以找到与之关联的进程。
了解了这些概念后,可以进一步探索cgroup内部用于结构转换的函数,如task_subsys_state、find_existing_css_set等,这些函数帮助理解cgroup的内部运作。此外,cgroup_init_early和cgroup_init函数是初始化cgroup的关键步骤,它们负责初始化rootnode和子系统的数组,为cgroup的抬手亮屏源码使用做准备。
最后,需要明确Linux内一切皆文件,cgroup基于VFS实现。内核启动时进行初始化,以确保系统能够正确管理进程资源。cgroup的初始化过程分为早期初始化和常规初始化,其中早期初始化用于准备cpuset和CPU子系统,确保它们在系统运行时能够正常工作。通过这些步骤,我们可以深入理解cgroup如何在Linux内核中实现资源管理和进程控制。
Linux 中断( IRQ / softirq )基础:原理及内核实现
中断(IRQ),尤其是软中断(softirq)的广泛用途之一是网络数据包的接收与发送,但其应用场景并非单一。本文将全面整理中断(IRQ)与软中断(softirq)的基础知识,这些内容与网络数据包处理虽无直接联系,但整理本文旨在更深入地理解网络数据包处理机制。
什么是中断?
CPU 通过时分复用处理多任务,其中包括硬件任务,如磁盘读写、键盘输入,以及软件任务,如网络数据包处理。CPU 在任何时刻只能执行一个任务。当某个硬件或软件任务当前未被执行,但希望CPU立即处理时,会向CPU发送中断请求——希望CPU暂停手头工作,优先服务“我”。中断以事件形式通知CPU,因此常看到“在XX条件下会触发XX中断事件”的烟台和大连源码表述。
中断分为两类:
管理中断的设备:Advanced Programmable Interrupt Controller(APIC)。
硬中断的中断处理流程
中断随时发生,处理流程如下:
Maskable and non-maskable
Maskable interrupts 在x_上可以通过sti/cli指令来屏蔽(关闭)和恢复:
在屏蔽期间,这种类型的中断不会触发新的中断事件。大部分IRQ都属于这种类型。例如,网卡的收发包硬件中断。
Non-maskable interrupts 不可屏蔽,因此属于更高优先级的类型。
问题:执行速度与逻辑复杂性之间的矛盾
IRQ处理器的两个特点如下:
存在内在矛盾。
解决方式:中断的推迟处理(deferred interrupt handling)
传统解决方式是将中断处理分为两部分:
这种方式称为中断的推迟处理或延后处理。现在已是一个通用术语,涵盖各种推迟执行中断处理的方式。中断分为两部分处理:
在Linux中,有三种推迟中断(deferred interrupts):
具体细节将在后续介绍。
软中断与软中断子系统
软中断是内核子系统的一部分:
每个CPU上会初始化一个ksoftirqd内核线程,负责处理各种类型的softirq中断事件;
使用cgroup ls或ps -ef都能看到:
软中断事件的handler提前注册到softirq子系统,注册方式为open_softirq(softirq_id, handler)
例如,注册网卡收发包(RX/TX)软中断处理函数:
软中断占用了CPU的总开销:可以使用top查看,第三行倒数第二个指标是系统的软中断开销(si字段):
Linux内核源码分析学习地址:ke.qq.com/course/...
文章福利小编推荐自己的Linux内核源码分析交流群:点击加入整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!
主处理
smpboot.c类似于事件驱动的循环,会调度ksoftirqd线程执行pending的软中断。ksoftirqd内部会进一步调用到__do_softirq,
避免软中断占用过多CPU
软中断的潜在影响:推迟执行的部分(如softirq)可能会占用较长时间,在这段时间内,用户空间线程只能等待。反映在top中,si占比。
不过softirq调度循环对此有所改进,通过budget机制来避免softirq占用过多CPU时间。
硬中断-软中断调用栈
softirq是一种推迟中断处理机制,将IRQ的大部分处理逻辑推迟在这里执行。有两条路径都会执行到softirq主处理逻辑__do_softirq():
1、CPU调度到ksoftirqd线程时,会执行到__do_softirq();
2、每次IRQ handler退出时:do_IRQ()->...
do_IRQ是内核中主要的IRQ处理方式。它执行结束时,会调用exiting_irq(),这会展开成irq_exit()。后者会检查是否pending有softirq,如果有,则唤醒:
进而会使CPU执行到__do_softirq。
软中断触发执行的步骤
总结,每个软中断会经过以下阶段:
以收包软中断为例,IRQ handler并不执行NAPI,只是触发它,在内部会执行到raiseNET_RX_SOFTIRQ;真正的执行在softirq,会调用网卡的poll()方法收包。IRQ handler中会调用napi_schedule(),然后启动NAPI poll()。
需要注意的是,虽然IRQ handler所做的工作很少,但处理这个包的softirq和IRQ在同一CPU上运行。这意味着,如果大量的包都放在同一个RX队列,虽然IRQ开销可能不多,但该CPU仍然会非常繁忙,都花费在softirq上。解决方式:RPS。它不会降低延迟,只是将包重新分配:RXQ->CPU。
三种推迟执行方式(softirq/tasklet/workqueue)
提到,Linux中的三种推迟中断执行方式:
其中:
前面已经看到,Linux在每个CPU上创建了一个ksoftirqd内核线程。
softirqs是在Linux内核编译时确定的,例如网络收包对应的NET_RX_SOFTIRQ软中断。因此是一种静态机制。如果想添加一种新softirq类型,需要修改并重新编译内核。
内部组织
内部由一个数组(或称为向量)管理,每个软中断号对应一个softirq handler。数组与注册:
在5.中所有类型的softirq:
也就是在cat /proc/softirqs看到的哪些。
触发(唤醒)softirq
以收包软中断为例,IRQ handler并不执行NAPI,只是触发它,在内部会执行到raiseNET_RX_SOFTIRQ;真正的执行在softirq,会调用网卡的poll()方法收包。IRQ handler中会调用napi_schedule(),然后启动NAPI poll()。
如果对内核源码有一定了解,会发现softirq使用非常有限,原因之一是它是静态编译的,依赖内置的ksoftirqd线程来调度内置的9种softirq。如果想添加一种新功能,就得修改并重新编译内核,开发成本很高。
实际上,实现推迟执行的更常用方式是tasklet。它构建在softirq机制之上,具体来说就是使用了两种softirq:
换句话说,tasklet是在运行时(runtime)创建和初始化的softirq,
内核软中断子系统初始化了两个per-cpu变量:
tasklet再执行针对list的循环:
tasklet在内核中的使用非常广泛。不过,后面又出现了第三种方式:workqueue。
这也是一种推迟执行机制,与tasklet有些相似,但有显著不同。
使用场景
简而言之,workqueue子系统提供了一个接口,通过该接口可以创建内核线程来处理从其他地方enqueue过来的任务。这些内核线程称为worker threads,内置的per-cpu worker threads:
结构体
kworker线程调度workqueues,原理与ksoftirqd线程调度softirqs类似。然而,我们可以为workqueue创建新的线程,而softirq则不行。
参考资料引用链接
[1]
中断与中断处理:0xax.gitbooks.io/linux-...
作者:赵亚楠 原文:arthurchiao.art/blog/li...来源:云原生实验室
eBPF 程序编写 - libbpf
eBPF程序编写通过内核的bpf系统调用加载ebpf二进制,实现对MAP的增删改操作。ebpf源代码使用C语言编译生成bpf字节码。eBPF程序能够访问MAP,调用内核函数,访问内核与用户态内存,并进行计算与分支控制,但需避免死循环。
eBPF程序在加载后,与内核的特定hook点结合,被动执行,如tracepoint、kprobe、uprobe、cgroup等,提供高效的数据通信机制,如输出数据到perf_buffer或ringbuffer。
eBPF程序还能通过修改hook函数的返回值,实现函数劫持。但此功能仅适用于标有ERR_INJECT的函数。
libbpf作为辅助工具简化bpf编程,用户态与内核态逻辑分离,框架负责通信、加载与卸载bpf程序。它提供丰富的helper函数,并实现CO-RE,确保bpf程序跨内核版本运行。
libbpf通过记录relocation信息于bpf程序的.BTF section,配合clang编译bpf后端增加的builtin函数,实现程序跨内核版本运行。编译后,bpf程序以用户态可执行二进制形式存在,内核态程序作为ro数据段嵌入其中,形成独立可部署的文件。
libbpf的CORE方式相较于其他工具集有显著优势,例如bcc-tools工具集合采用libbpf重写。编写eBPF程序时,参照libbpf-bootstrap/examples/c模板,通常需要两个文件:BTF记录数据结构信息,确保程序在不同内核版本上运行的兼容性。
df- h命令代表什么意思?
df -h命令代表什么意思呢?我们一起了解一下吧!
df -h是df命令加上参数h,日常普遍用该命令可以查看磁盘被占用了多少空间、还剩多少空间等信息。
Linux 是一种自由和开放源码的类 UNIX 操作系统。df命令是Linux命令之一,df命令的英文全称即“Disk Free”,顾名思义功能是用于显示系统上可使用的磁盘空间,默认显示单位为KB。“df -h”命令的参数组合,是可以根据磁盘容量自动变换合适的容量单位,更利于阅读和查看。
参考实例:
[root@linux ~]# df -h
文件系统 容量 已用 可用 已用% 挂载点
devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs 2.0G 0 2.0G 0% /dev/shm
tmpfs 2.0G 1.1M 2.0G 1% /run
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
/dev/mapper/fedora_linuxhell-root G 2.0G G % /
tmpfs 2.0G 4.0K 2.0G 1% /tmp
/dev/sda1 M M M % /boot
tmpfs M 0 M 0% /run/user/0
显示的单词分别代表的具体含义如下。
Filesystem:表示该文件系统位于哪个分区,因此该列显示的是设备名称;
Used:表示用掉的磁盘空间大小;
Available:表示剩余的磁盘空间大小;
Use%:磁盘空间使用率;
Mounted on:文件系统的挂载点,也就是磁盘挂载的目录位置
Filesystem:表示该文件系统位于哪个分区,因此该列显示的是设备名称;
Used:表示用掉的磁盘空间大小;
Available:表示剩余的磁盘空间大小;
Use%:磁盘空间使用率;
Mounted on:文件系统的挂载点,也就是磁盘挂载的目录位置。
关于df -h命令,我们就了解到这啦!