IO 多路复用之 epoll 问题记录

IO 多路复用之 epoll 问题记录

记录使用 epoll 时的一些疑惑和问题

一、epoll 可以监听普通文件吗

先以一段代码来验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <sys/epoll.h>
#include <fcntl.h>

int main()
{
int epfd, fd;
struct epoll_event ev, events[2];
int result;

epfd = epoll_create(10);
if (epfd < 0)
{
perror("epoll_create()");
return -1;
}

fd = open("./test.txt", O_RDONLY | O_CREAT);
if (fd < 0)
{
perror("open()");
return -1;
}

ev.events = EPOLLIN;

result = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
if (result < 0)
{
perror("epoll_ctl()");
return -1;
}

epoll_wait(epfd, events, 2, -1);

return 0;
}

运行后结果如下:

查看更多

实现一个最简单的内核

实现一个最简单的内核

我的环境,物理机是 Inter x86_86 机器,装的是 window 10 操作系统。使用软件 VMware 虚拟化出来一个 ubuntu 18.04 的虚拟机。然后在此虚拟机上进行实验。

一、理论

我们一个操作系统需要在硬件跑起来,也就是进行开机操作,需要进行如下的步骤:

PC 硬件主机加电 –> PC 机 BIOS 固件 –> 加载可引导设备中的 GRUB –> GRUB 引导 –> 加载硬件分区中的 OS 文件 –> OS

PC 机 BIOS 固件是固化在 PC 机主板上的 ROM 芯片中的,掉电也能保存,PC 机上电的第一条指令就是 BIOS 固件中的,他负责检测和初始化 CPU、内存以及主板平台,然后加载引导设备(大概率是硬件)中的第一个扇区数据,到 0x7c00 地址开始的内存空间,再跳转到 0x7c00 处执行指令,也就是 GRUB 引导程序。

GRUB 引导程序我们暂不深究,如果下载了 ubuntu linux 操作系统,GRUB 就已经存在了。我们先来直接使用。本次我们直接从 OS 这一步入手。

查看更多

硬件

计算机的硬件

一、CPU 的工作模式

CPU 的工作模式有实模式、保护模式、长模式

1. 实模式

实模式是实际地址模式,也就是说:

  • 他会运行真实的指令,对指令的动作不作区分,直接执行指令的真实功能;
  • 还有发往内存的地址是真实的,对任何地址不加限制的发往内存。
查看更多

内核设计

内核设计

内核在开发之前需要进行设计,也就是设计架构。

从抽象角度来说,内核是计算机资源的管理者,管理资源是为了让应用使用资源。那么先从计算机的资源看起

一、计算机资源

计算机中资源分为硬件资源、软件资源。

  • 硬件资源分为:总线、CPU、内存、硬盘、网卡、显卡、各种 IO 设备
查看更多

eBPF概念

一、eBPF 介绍

eBPF 全称是“扩展的伯克利数据包过滤器”(Extended Berkeley Packet Filter),是一种数据包过滤技术,是从 BPF 技术扩展而来。BPF 提供了一种在内核事件和用户程序事件发生时安全注入代码的机制,这就让非内核开发人员也可以对内核进行控制。随着内核的发展,BPF 逐步从最初的数据包过滤扩展到了网络、内核、安全、跟踪等,而且它的功能特性还在快速发展中,这种扩展后的 BPF 被简称为 eBPF。

在 eBPF 之前,内核模块是注入内核的最主要机制。由于缺乏对内核模块的安全控制,内核的基本功能很容易被一个有缺陷的内核模块破坏。而 eBPF 则借助即时编译器(JIT),在内核中运行了一个虚拟机,保证只有被验证安全的 eBPF 指令才会被内核执行。同时,因为 eBPF 指令依然运行在内核中,无需向用户态复制数据,这就大大提高了事件处理的效率。

正是由于这些突出的特性,eBPF 现如今已经在故障诊断、网络优化、安全控制、性能监控等领域获得大量应用。比如,Facebook 开源的高性能网络负载均衡器 Katran、Isovalent 开源的容器网络方案 Cilium ,以及著名的内核跟踪排错工具 BCC 和 bpftrace 等,都是基于 eBPF 技术实现的。

我们在开发新的 eBPF 程序时,内核社区提供的 libbpf 库(https://github.com/libbpf/libbpf)不仅可以帮我们避免直接调用内核函数,而且还提供了跨内核版本的兼容性(即一次编译到处执行,简称 CO-RE

二、eBPF 的工作原理

eBPF 需要事件触发后才会执行,这些事件包括系统调用、内核跟踪点、内核函数和用户态函数的调用退出、网络事件等。借助于强大的内核态插桩和用户态插桩,eBPF 程序几乎可以在内核和应用的任意位置进行插桩。

查看更多

跟踪内存泄漏

bbc 使用 ebpf 提供了一个叫做memleak的工具,是专门用来检测内存泄露的。它可以跟踪系统或者指定进程的内存分配、释放请求,然后定期输出一个未释放内存和相应调⽤栈的汇总情况(默认5 秒)。

代码地址:https://github.com/iovisor/bcc/blob/master/tools/memleak.py

注意,这个工具会定期输出未释放的内存以及相应调用栈的汇总情况。所以如下情况也会被检测出来。

1
2
3
4
5
6
7
void test3() {
for (;;) {
void* p = malloc(16);
sleep(10);
free(p);
}
}

查看更多

环境搭建

一次编译到处运行(简称 CO-RE),解决了内核数据结构在不同版本差异导致的兼容性问题。不过在使用 CO-RE 之前,内核需要开启 CONFIG_DEBUG_INFO_BTF=yCONFIG_DEBUG_INFO=y 这两个编译选项。

系统 ubuntu 20.10 以上版本已经默认开启了这些编译选项,我安装的是 ubuntu 22.04

1
sudo apt-get install -y make clang llvm libelf-dev libbpf-dev bpfcc-tools libbpfcc-dev

二、开发 eBPF 的过程

这个过程分为 5 步

查看更多