Skip to content

Commit 3888907

Browse files
committed
2023/4/7
1 parent 45dd564 commit 3888907

19 files changed

+1341
-0
lines changed

给我康康/01 (copy).md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
3+
![image-20230113192049236](./../picture/image-20230113192049236.png)
4+
5+
![image-20230113193000701](./../picture/image-20230113193000701.png)
6+
7+
![image-20230113193153540](./../picture/image-20230113193153540.png)
8+
9+
done!
10+
11+
## LKD_Chapter_3
12+
13+
计算thread_info的偏移
14+
15+
```asm
16+
# current_thread_info() assuming 8KB stack size
17+
movl $-8192, %eax
18+
andl %esp, %eax
19+
```
20+
21+
### movx
22+
23+
``` asm
24+
movx source, destination
25+
```
26+
27+
movx其中 x 可以是下面的字符:
28+
29+
* l用于32位的长字值
30+
31+
* w用于16位的字值
32+
33+
* b用于8位的字节值
34+
35+
实例:
36+
37+
```asm
38+
movl %eax, %ebx #把32位的EAX寄存器值传送给32位的EBX寄存器值
39+
movw %ax, %bx #把16位的EAX寄存器值传送给16位的EBX寄存器值
40+
movb %al, %lx #把8位的EAX寄存器值传送给8位的EBX寄存器值
41+
```
42+
43+
44+
45+
### andl
46+
47+
假定以下值存储在指示的存储器地址和寄存器中:
48+
49+
![enter image description here](./../picture/WVGoy.png)
50+
51+
现在,我们有一条指令:
52+
53+
``` asm
54+
addl %ecx, (%eax)
55+
```
56+
57+
EAX包含0x100; 0x100中的值为0xFF; ECX包含0x1。
58+
59+
0x1 + 0xFF = 0x100。
60+
61+
然后将最终结果放入EAX指向的地址。 因此,(0X100) == 0x100
62+
63+
### 解析
64+
65+
ESP point to the bottom
66+
67+
value of EAX == -8192
68+
69+
EAX == -8192 + value of ESP

给我康康/2022_10_1 (copy).md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# 内核接收网络包的大致过程
2+
3+
4+
5+
![接收网络包](./../picture/%E6%8E%A5%E6%94%B6%E7%BD%91%E7%BB%9C%E5%8C%85.png)!](./../2022_10/2022_10_1/%E6%8E%A5%E6%94%B6%E7%BD%91%E7%BB%9C%E5%8C%85.png)
6+
7+
1. 网络中的数据帧被网卡(网络适配器,这里使用简称)接收。
8+
2. 网卡判断目标地址是否为本机;不是,则抛弃接收的数据帧。
9+
3. 是,则通过PCIe总线(可以理解为硬件层面上的高速公路)以DMA的方式将数据帧复制到内存`RingBuffer`数据结构。
10+
4. 在复制后,网卡发送硬中断通知CPU。
11+
5. CPU在接收到硬中断后发送请求通知网络设备驱动(后简称驱动)。
12+
6. 驱动响应请求,向`ksoftirqd`线程发送软中断请求,并释放CPU硬中断。
13+
7. `ksoftirqd`线程调用poll函数进行收包。
14+
8. `RingBuffer`的数据帧被处理为`SocketBuffer`(后简称`skb`)的形式。
15+
9. `skb`向上贯穿网络层和运输层。
16+
10. 通过`socket库`接口将数据送至应用层。
17+
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# 2022_8_9 Linux网络
2+
3+
## Socket Buffer 穿越 TCP/IP 协议栈
4+
5+
### 发送
6+
7+
`Socket Buffer`**由上向下**穿越 TCP/IP 协议栈的各层期间会发生
8+
9+
* 数据包中不断加入穿越的层级的协议的头信息。
10+
* sk_buff管理结构~~强调下以防混淆~~中描述协议头的地址指针被赋值。
11+
12+
### 接收
13+
14+
`dev_alloc_skb`申请`Socket Buffer`,将接收到的网络数据帧从设备硬件的缓冲区复制到`Socket Buffer `的数据包缓冲区;填写 `sk_buff`管理结构中的地址、接收时间和协议等信息~~填快递单~~`Socket Buffer`到达内核地址空间~~(Kernel address space?)~~
15+
16+
`Socket Buffer`**由下向上**穿越 TCP/IP 协议栈的各层时将发生
17+
18+
* 数据包中不断丢弃穿越的层级的协议的头信息。
19+
* sk_buff管理结构中描述协议头的地址指针被复位~~置零~~,并调整`sk_buff`结构中
20+
指向有效数据的 sk_buff->data 指针。
21+
22+
![Screenshot 2022-08-09 223032](./../picture/Screenshot%202022-08-09%20223032-1678028278780-3.png)
23+
24+
### 优点
25+
26+
Socket Buffer 这样组织的优点是避免了重复复制数据,要传送的数据只需复制两次:
27+
28+
1. 从应用程序的用户地址空间复制到内核地址空间;
29+
2. 从内核地址空间复制到网络适配器的硬件缓冲区中。
30+
31+
32+
33+
---
34+
35+
36+
37+
## 需要衔接的知识点
38+
39+
1. 套接字层
40+
41+
2. TCP/IP各层
42+
43+
3. 产生中断通知内核???过程???
44+
45+
4. 网络数据帧
46+
47+
5. function `dev_alloc_skb`(我的理解是类似于C中的molloc)
48+
49+
> 向系统申请`Socket Buffer`
50+
51+
6. sk_buff->data 指针
52+
53+
* 数据包MAC协议头???(数据包以dataref: 1结尾)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
## Linux内核树源码编译
2+
3+
安装依赖
4+
5+
```bash
6+
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison binutils-dev libcap-dev libreadline-dev pahole -y
7+
```
8+
9+
查看可用的源码版本
10+
11+
```bash
12+
apt-cache search linux-source
13+
```
14+
15+
安装
16+
17+
```bash
18+
# 复制配置文件
19+
sudo cp /boot/config-$(uname -r) /usr/src/linux-source-5.15.0/linux-source-5.15.0/.config
20+
cd linux-source-5.15.0/
21+
22+
# 如果需要修改配置
23+
make menuconfig
24+
25+
# 复制签名
26+
sudo cp -R ../debian .
27+
sudo cp -R ../debian.master .
28+
# 多线程编译
29+
sudo make -j 8 modules
30+
sudo make -C samples/bpf
31+
sudo make modules_install
32+
sudo make -j 8
33+
sudo make install
34+
35+
# 安装启动项
36+
sudo update-initramfs -c -k 5.15.60
37+
sudo update-grub
38+
39+
# 卸载启动项:
40+
sudo update-initramfs -d -k 5.15.60
41+
sudo rm -rf /boot/*5.15.60*
42+
sudo update-grub
43+
44+
dpkg --get-selections|grep linux
45+
sudo apt-get remove linux-headers-5.15.0-43-generic linux-image-5.15.0-43-generic
46+
sudo apt autoremove
47+
```
48+
49+
50+
51+
## Linux内核tools子模块编译
52+
53+
进入linux-source目录下的tools文件夹直接make
54+
55+
56+
57+
## Linux跟踪文件系统
58+
59+
### Linux跟踪技术
60+
61+
* TRACEFS
62+
* Ftrace手动Uprobe Hook
63+
* Ftrace手动Kprobe Hook
64+
* Hook技术
65+
66+
67+
68+
```bash
69+
# 查看tracefs的挂载
70+
mount | grep tracefs
71+
72+
# 查看bugfs的挂载
73+
mount | grep bugfs
74+
```
75+
76+
77+
78+
之前总结的ebpf的uprobe/kprobe工作机制与是传统的hook机制相似
79+
80+
实际使用Hook时,需要知道目标函数的偏移量

给我康康/LKD (copy).md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
## list_entry()函数 ——从获取目标进程到linux内核双链表的思想与实现
2+
3+
``` c
4+
list_entry(task -> task.next, struct task_struct, tasks) //LKD_Chapter_3_page_26 对于给定进程,获取链表中的下一个进程
5+
```
6+
7+
8+
9+
``` c
10+
11+
#define list_entry(ptr, type, member) \
12+
container_of(ptr, type, member)
13+
14+
#define container_of(ptr, type, member) ({ \
15+
const typeof( ((type *)0)->member ) *__mptr = (ptr); // (1)
16+
(type *)( (char *)__mptr - offsetof(type,member) );}) // (2)
17+
18+
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
19+
```
20+
21+
用途: 利用结构体的已知__成员__(一般是__包含__在结构体中的指针)获得结构体对象的首地址,即获得结构体指针。
22+
23+
24+
25+
(1)定义一个常量指针__mptr,并将ptr赋值给它。合法性检查
26+
27+
(2)用当前节点地址ptr值剪掉member离type结构体首地址的距离,最后就得到了ptr节点指向的节点的type类型结构体的首地址。
28+
29+
示意图
30+
31+
32+
33+
![list_entry&内核链表](./../picture/list_entry&%E5%86%85%E6%A0%B8%E9%93%BE%E8%A1%A8-1675393729053-1.jpg)
34+
35+
36+
37+
验证offset计算偏移量
38+
39+
``` c
40+
#include <stdio.h>
41+
42+
/*
43+
typedef struct listhead
44+
{
45+
46+
listhead * prev;
47+
listhead * next;
48+
}listhead;
49+
*/
50+
51+
typedef struct
52+
{
53+
long long num;
54+
char name;
55+
int num_1;
56+
//listhead tasks;
57+
}node;
58+
59+
int main()
60+
{
61+
printf("offset:%u\n", \
62+
&((node *) 0) -> num_1);
63+
}
64+
```
65+
66+
<img src="./../picture/image-20230301085557782.png" alt="image-20230301085557782" style="zoom: 200%;" />
67+
68+
# 感想
69+
70+
1. 利用这个知识应该可以改进之前写的eBPF程序,返回一些有用的数据
71+
2. 发现我的读书学习过分看重次要问题了,是在阅读LKD进程部分时不知道list_entry才去查的,但是留有一个印象就好,应该把重点放在阅读进程,线程主要问题的学习和理解上。
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# 一些特性
2+
3+
* 系统几乎一切被当作文件对待(Socket除外)
4+
5+
* fork()系统调用创建进程
6+
7+
* 简单稳定的进程间通信
8+
9+
# 初理解系统调用
10+
11+
> 当一个程序执行一条系统调用时,可以说内核正在代其执行.
12+
13+
可以把系统调用理解为我们去旭日餐厅点炒菜的过程,当程序执行系统调用时,相当通过服务员告诉厨师要做什么菜,这里的服务员就是系统调用界面,厨师就是内核,当厨师完成炒菜后会通过服务员交给我们,也就是用户进程程序得到返回的结果,整个过程完毕。
14+
15+
# 处理器任意时间的活动概括
16+
17+
1. 运行于用户空间执行用户进程
18+
2. 运行在内核空间的进程上下文,执行内核进程(当CPU空转时也在运行处于其中的空进程)
19+
3. 运行在内核空间的中断上下文,处理中断
20+
21+
# 一点联想
22+
23+
Linux内核代码(包括其他系统)的设计都应该足够简洁,并被安全可靠地实现来解决现实存在的问题。《代码整洁之道》(*Clean Code*)这本书应该加入学习计划(又挖坑🤯)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# 内核开发特点
2+
3+
## 无C库或标准头文件
4+
5+
### 库和头文件的区别
6+
7+
* 头文件是文本文件,可以阅读编写更改;而库文件是二进制文件,已经预先编译完成,无法直接阅读。
8+
9+
* 头文件在编译预处理阶段被处理插入程序文本;而库文件这是在编译链接阶段由链接器与源文件合并。(对此处有疑问的同志可以查看c程序的编译过程,如*CSAPP* 1.2程序被其他程序翻译成不同的格式)
10+
11+
## 编程必须使用GNU C
12+
13+
### 我对内联函数的理解
14+
15+
在程序文件中“写”/定义一次,在需要使用的地方“标记”引用,在编译程序时系统自动将定义的函数展开加入到字段中。
16+
17+
### 内联函数与函数调用的本质区别
18+
19+
没有单独在其他物理位置创建函数体,而是被展开在文件代码中,这样也就省去了调用通信的时间消耗(优点),但同时代码会变长,占用更多内存空间或指令缓存(缺点)。
20+
21+
* 也因为上面的区别在定义内联函数时需要使用`static`为关键字,并用`inline`限定。例如:
22+
23+
``` c
24+
// include/linux/skbuff.h
25+
static inline unsigned char *skb_end_pointer(const struct sk_buff *skb)
26+
```
27+
28+
这里使用static关键字就是使系统在编译时不会为内联函数单独开辟函数体的空间。
29+
30+
### 分支声明
31+
32+
使用unlikely()对绝少发生的分支进行标记优化。例如:
33+
34+
```c
35+
/* 假设error绝大多数时间为0,即极少出错 */
36+
if(unlikely(error)){
37+
/* *** */
38+
}
39+
```
40+
41+
likely()的使用同理。
42+
43+
* 因为分支一般判断特殊情况,所以unlikely()的使用更加广泛。
44+
45+
# 读书时的疑问
46+
47+
1. 优化分支条件,需要正确判断条件是否绝少或通常发生;此处可否运用聚类算法?
48+
2. 进程和线程概率的区别。(在第三章就会有一个初步的认识)
49+
50+
51+

0 commit comments

Comments
 (0)