在协议栈数据报文转发过程中,数据报文可能是来自cpu的不同核处理,当然前提是系统开启了多核并且硬件支持。
这里我们主要讲解多核在数据转发中的应用
先看看数据转发基本流程:
中断指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程。即在程序运行过程中,系统出现了一个必须由CPU立即处理的情况,此时,CPU暂时中止程序的执行转而处理这个新的情况的过程就叫做中断。
硬件中断是一个异步信号, 表明需要注意, 或需要改变在执行一个同步事件.硬件中断是由与系统相连的外设(比如网卡 硬盘 键盘等)自动产生的. 每个设备或设备集都有他自己的IRQ(中断请求), 基于IRQ, CPU可以将相应的请求分发到相应的硬件驱动上(注: 硬件驱动通常是内核中的一个子程序, 而不是一个独立的进程). 比如当网卡受到一个数据包的时候, 就会发出一个中断.
root@OpenWrt:~# cat /proc/interrupts
CPU0 CPU1
0: 5 0 IO-APIC 2-edge timer
1: 677 0 IO-APIC 1-edge i8042
4: 18 0 IO-APIC 4-edge ttyS0
8: 0 1 IO-APIC 8-edge rtc0
9: 0 0 IO-APIC 9-fasteoi acpi
12: 0 5 IO-APIC 12-edge i8042
14: 0 0 IO-APIC 14-edge ata_piix
15: 0 0 IO-APIC 15-edge ata_piix
16: 72987 170 IO-APIC 16-fasteoi eth1
17: 0 1786766 IO-APIC 17-fasteoi ioc0, ehci_hcd:usb1, eth2
18: 65 0 IO-APIC 18-fasteoi uhci_hcd:usb2
56: 0 0 PCI-MSI 129024-edge vmw_vmci
虽然中断cpu固定,当然我们也可以查看和修改当前绑定的cpu
首先我们找到网络接口eth1的中断号,比如16
然后通过 /proc/irq/16/smp_affinity_list命令查看当前绑定的cpu
root@OpenWrt:~# cat /proc/irq/16/smp_affinity_list
0-1
root@OpenWrt:~#
0-1表示当前绑定的cpu为cpu0和cpu1,包括两个cpu
那现在如何让eth1只绑定到cpu0呢?
修改方式:设置/proc/irq/16/smp_affinity值
首先查看当前值,当前为3(前面的0先不用管),3的二进制表示为11,表示0位和1为置为1,
也就是绑定了cpu0和cpu1。
root@OpenWrt:~# cat /proc/irq/16/smp_affinity
00000000,00000003
root@OpenWrt:~#
那现在我们修改为cpu0的话,只需要修改为1即可,对应的字符串为00000000,00000001
root@OpenWrt:~#
root@OpenWrt:~# cat /proc/irq/16/smp_affinity
00000000,00000003
root@OpenWrt:~#
root@OpenWrt:~#
root@OpenWrt:~# echo "00000000,00000001" >/proc/irq/16/smp_affinity
root@OpenWrt:~# cat /proc/irq/16/smp_affinity
00000000,00000001
root@OpenWrt:~# cat /proc/irq/16/smp_affinity_list
0
root@OpenWrt:~#
通过以上操作,可以看到成功将eth1(中断号16)绑定到了cpu 0,也就是通过硬件中断上来的数据包都是通过cpu0处理(这里还只是数据处理的一部分,硬件中断只相当于上半部,下半部是软中断处理)。
软中断的处理类似于硬中断. 但是软中断仅仅由当前运行的进程产生.通常软中断是对一些I/O的请求.软中断仅与内核相联系, 而内核主要负责对需要运行的任何其他进程进行调度.软中断不会直接中断CPU, 也只有当前正在运行的代码(或进程)才会产生软中断. 软中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求.有一个特殊的软中断是Yield调用, 它的作用是请求内核调度器去查看是否有一些其他的进程可以运行.
协议的处理都是在软件中断环境中,从缓冲队列中获取到skb(数据报文)后开始一系列封包解包操作,比如路由、nat、过滤等,我们前面将的netfilter框架也在协议栈中,都是属于网卡的软件中断触发。
而软件中断中同一个接口也可能是不同cpu处理的,通过上面的流程图应该可以很明确看出。中断上半部缓存数据报文时,会判断放在哪个队列,判断是有算法支撑的,会尽量保证队列均衡,队列个数和cpu个数相同,不同的队列由不同的cpu处理,这样就出现了并发的情况,对于临界资源,需要加锁处理,否则可能出现数据丢失或者不完整情况,特别是全局数据的操作。
smp_processor_id 获取当前处理的cpu id
for_each_possible_cpu(i) 获取所有的cpu id
cpu_online(i) 查看当前在线的cpu列表
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL");
MODULE_AUTHOR("openwrt");
MODULE_DESCRIPTION("packet");
static u_int32_t packet_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
int cpu = smp_processor_id();
printk("packet handle cpu core = %d\n", cpu);
return NF_ACCEPT;
}
static struct nf_hook_ops packet_ops[] __read_mostly = {
{
.hook = packet_hook,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_MANGLE + 1,
},
};
static int __init smp_test_init(void)
{
int cpu = smp_processor_id();
printk("cur cpu = %d\n", cpu);
int i;
int count = 0;
for_each_possible_cpu(i) {
if (cpu_online(i)){
printk("online cpu core: %d\n", i);
count++;
}
}
printk("total cpu core: %d\n", count);
nf_register_net_hooks(&init_net, packet_ops, ARRAY_SIZE(packet_ops));
return 0;
}
static void smp_test_exit(void)
{
nf_unregister_net_hooks(&init_net, packet_ops, ARRAY_SIZE(packet_ops));
return ;
}
module_init(smp_test_init);
module_exit(smp_test_exit);
加载内核模块打印当前处理的核,并且列出当前设备所有的在线CPU
root@OpenWrt:/# insmod smp_test.ko
root@OpenWrt:/# <4>[253513.942540] cur cpu = 1
<4>[253513.943316] online cpu core: 0
<4>[253513.944169] online cpu core: 1
<4>[253513.945158] total cpu core: 2
当有数据包转发时,会打印每个数据包当前处理的cpu核,可以看到不同的数据包处理的核是不一样的。
root@OpenWrt:/#
root@OpenWrt:/# <4>[253534.694982] packet handle cpu core = 1
<4>[253534.771213] packet handle cpu core = 1
<4>[253534.772719] packet handle cpu core = 1
<4>[253534.773811] packet handle cpu core = 1
<4>[253534.774995] packet handle cpu core = 1
<4>[253534.859312] packet handle cpu core = 1
<4>[253534.861465] packet handle cpu core = 1
<4>[253534.945897] packet handle cpu core = 1
<4>[253535.151810] packet handle cpu core = 0
<4>[253535.153427] packet handle cpu core = 1
<4>[253535.154753] packet handle cpu core = 1
<4>[253535.157813] packet handle cpu core = 0
<4>[253535.314836] packet handle cpu core = 0
<4>[253535.316588] packet handle cpu core = 1
<4>[253535.317903] packet handle cpu core = 1
<4>[253535.319414] packet handle cpu core = 0
<4>[253536.448938] packet handle cpu core = 0
OpenWrt应用过滤插件作者(应用过滤用于控制app联网,可以过滤游戏、视频、聊天等几百款app)
从事嵌入式Linux开发近10年,主要负责路由器网通产品研发,精通OpenWrt系统,包括luci、消息机制、内核模块等。擅长模块:路由器上网行为管理、智能流控、上网认证、防火墙、虚拟服务器、多wan负载均衡等
开源作品地址:
https://github.com/destan19/OpenAppFilter