IPI
概念
IPI(Inter-Processor Interrupt,处理器间中断)。
处理器间中断允许一个 CPU 向其它 CPU 发送中断信号,处理器间中断不是通过 IRQ 线传输的,而是作为信号直接放在连接所有 CPU 本地 APIC 的总线上。
IPI 需要 GIC 支持吗
IPI(Inter-Processor Interrupt,处理器间中断)是用于处理器内部通讯的中断。在ARM架构中,IPI通常需要由GIC(Generic Interrupt Controller,通用中断控制器)来管理和实现。GIC可以在处理器间转发IPI信号,以便在多个处理器之间进行协作和通讯。通过GIC,处理器可以指定IPI优先级和处理方式,从而灵地控制IPI的处理此,GIC是对IPI信号进行处理和转发的必要组件,也是支持多处理器通讯和协作的关键组件。
IPI 需要使用 IRQ 线吗?
IPI 通常不 线进行传输,因为 IPI 是一种处理器间的中断机制,主要用于同一个处理器中的多个核之间的通讯。在 ARM 架构中,IPI 可以通过 LPI。LPI 是一种与 IRQ 相关的本地中断机制,但与 IRQ 不同,LPI 不需要通过中断控制器进行路由,可以直接在处理器内部传递和处理。在 ARMv8 架构中,LPI 被广泛用于处理器间通讯和协作,同时可以通过 GIC 自动映射到硬件中断,提高了处理器的并行度和性能。
LPI 是 GIC 内部设施吗?还是 SOC 内部的设施?
LPI(Local Processor Interrupt,本地处理器中断)是 ARMv8 架构中的一种中断机制,用于处理器内部的中断处理。 LPI 是由 GIC(Generic Interrupt Controller,通用中断控制器)内部提供的中断机制,主要用于处理器内部的通讯和协作。因此,LPI 是 GIC 内部设施。但是,LPI 这种中断机制也是 SOC(System on a Chip,片上系统)内部的设施,因为它被嵌入在 SOC 架构中,用于处理 SOC 内部的中断需求。
pic 和 gic 区别
PIC(Programmable Interrupt Controller,可编程中断控制器)和GIC(Generic Interrupt Controller,通用中断控制器)都是用于管理中断的硬件组件。它们的主要区别在于: 1. 架构不同:PIC是x86架构中的中断控制器,而GIC是ARM架构中的中断控制器。 2. 可编程性:PIC不支持动态配置和优先级控制,而GIC支持动态配置和优先级控制。这意味着GIC可以灵活地适应不同的硬件平台和应用场景。 3. 中断处理方式:PIC是经典的中断处理方式,即通过向CPU发送中断请求来引发中断处理程序。而GIC支持多种中断处理方式,包括虚拟中断和消息传递中断。 总之,PIC和GIC都是用于管理中断的硬件组件,但在架构、可编程性和中断处理方式等面有所不同。
一个实例说明
参考:IPI中断机制
XLR 732 (x86架构)多核多线程处理器的中断由 PIC(Programmable Interrupt Controller)统一控制。PIC 允许一个硬件线程中断其他的硬件线程,这种方式被称为核间中断(Inter-Processor Interrupts,IPI)。
PIC 拥有一个宽度为 32 位的核间中断寄存器 IPI Base,该寄存器包含目的线程的编号、中断向量及中断类型(是否中断多个硬件线程)等内容。核间中断可以通过向这个寄存器写入需要的值来产生。
若硬件线程 A 想要发送一个核间中断给硬件线程 B,它只需要向寄存器 IPI Base 中写入 B 的 Thread ID、中断向量、中断类型等值就可以了,PIC 会通知 B 所在的核挂起它当前的执行序列,并根据中断向量跳转到中断服务例程 ISR 的入口。
使用 IPI 进行核间通信的关键在于要利用中断服务例程 ISR 去读取一个事先约好的共享内存区域。发起方首先将消息写到一块共享内存中,然后发起核间中断。被中断的硬件线程在中断服务例程中读取该内存,以获得发起方通知的消息。为防止多核间的竞争导致消息被改写,使用这种方式必须利用锁机制来确保消息的完整性。
例程1
arch/arm/kernel/smp.c
void smp_send_reschedule(int cpu)
{
smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
}
EXPORT_SYMBOL(smp_send_reschedule); // 导出符号,不然 ko 无法编译通过
include/linux/sched.h
static __always_inline void scheduler_ipi(void)
{
printk("CPU%d: scheduler_ipi()\n", smp_processor_id()); // 添加打印
/*
* Fold TIF_NEED_RESCHED into the preempt_count; anybody setting
* TIF_NEED_RESCHED remotely (for the first time) will also send
* this IPI.
*/
preempt_fold_need_resched();
}
重新编译内核
liyongjun@Box:~/project/board/buildroot$ rm OrangePiPC/build/linux-custom/.stamp_built
liyongjun@Box:~/project/board/buildroot$ make O=OrangePiPC/ linux -j4 V=s
liyongjun@Box:~/project/board/buildroot$ cp OrangePiPC/images/zImage ~/tftp/
测试 ko
hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <asm/io.h>
static int hello_init(void)
{
printk("hello_init\n");
smp_send_reschedule(3);
return 0;
}
static void hello_exit(void)
{
printk("hello_exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
Makefile
obj-m = hello.o
KDIR = /home/liyongjun/project/board/buildroot/OrangePiPC/build/linux-custom
CROSS_COMPILE = /home/liyongjun/project/board/buildroot/OrangePiPC/host/bin/arm-linux-
EXTRA_CFLAGS = -g
all:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules
clean:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) clean
硬件:OrangePi PC
更换内核
# cd /boot/
# tftp -gr zImage 192.168.31.223
# reboot
下载 ko
# tftp -gr hello.ko 192.168.31.223
设置 printk 打印等级
# echo 7 > /proc/sys/kernel/printk
运行
# insmod hello.ko
[ 399.773120] CPU1: scheduler_ipi()
[ 399.781822] hello_init
[ 399.787149] CPU3: scheduler_ipi() // 生效了
CPU1 执行 smp_send_reschedule(3);
让 CPU3 执行调度。
例程2
这个例程才是比较通用
hello.c
#include <linux/module.h>
#include <linux/smp.h>
static void print_str(void *str)
{
printk("CPU%d do task\n", smp_processor_id());
printk("%s\n", (char *)str);
}
static int hello_init(void)
{
char str[] = "hello";
printk("hello_init\n");
smp_call_function_single(2, print_str, str, 1); // 让 CPU2 执行 print_str()
return 0;
}
static void hello_exit(void)
{
printk("hello_exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
Makefile
obj-m = hello.o
KDIR = /home/liyongjun/project/board/buildroot/OrangePiPC/build/linux-custom
CROSS_COMPILE = /home/liyongjun/project/board/buildroot/OrangePiPC/host/bin/arm-linux-
EXTRA_CFLAGS = -g
all:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules
clean:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) clean
install:
cp hello.ko ~/tftp
OrangePi PC 运行
# tftp -gr hello.ko 192.168.31.223
#
# insmod hello.ko
[167594.203052] CPU0: scheduler_ipi()
[167594.211962] hello_init
[167594.214459] CPU2 do task
[167594.217086] hello
#
# rmmod hello.ko
[167597.888388] CPU0: scheduler_ipi()
[167597.897441] hello_exit
参考
全面解析Linux 内核 3.10.x - IPI核间中断的应用
IPI中断机制 PIC 是 x86 架构中的中断控制器,而 GIC 是 ARM 架构中的中断控制器。