Trong linux/arch/x86/include/asm/switch_to.h
, có định nghĩa về vĩ mô switch_to
, các đường chính mà làm thread thực switch phép lạ đọc như thế này (cho đến Linux 4.7 khi nó thay đổi):Tại sao switch_to sử dụng push + jmp + ret để thay đổi EIP, thay vì jmp trực tiếp?
asm volatile("pushfl\n\t" /* save flags */ \
pushl %%ebp\n\t" /* save EBP */ \
"movl %%esp,%[prev_sp]\n\t" /* save ESP */ \
"movl %[next_sp],%%esp\n\t" /* restore ESP */ \
"movl $1f,%[prev_ip]\n\t" /* save EIP */ \
"pushl %[next_ip]\n\t" /* restore EIP */ \
__switch_canary \
"jmp __switch_to\n" /* regparm call */ \
"1:\t" \
"popl %%ebp\n\t" /* restore EBP */ \
"popfl\n" /* restore flags */ \
Các toán hạng được đặt tên có bộ nhớ hạn chế như [prev_sp] "=m" (prev->thread.sp)
. __switch_canary
được xác định là không có gì trừ khi CONFIG_CC_STACKPROTECTOR
được xác định (sau đó là tải và lưu trữ sử dụng %ebx
).
Tôi hiểu cách thức hoạt động, giống như con trỏ backup kernel stack/phục hồi, và làm thế nào push next->eip
và jmp __switch_to
với một hướng dẫn ret
ở phần cuối của các chức năng, mà thực sự là một "giả" lệnh gọi phù hợp với thực ret
hướng dẫn và tạo hiệu quả cho next->eip
điểm trả về của chuỗi tiếp theo.
Điều tôi không hiểu là, tại sao lại là hack? Tại sao không chỉ call __switch_to
, sau đó là ret
, jmp
đến next->eip
, sạch hơn và thân thiện với người đọc hơn.
Nhưng sẽ không hiệu quả khi giết ngăn xếp dự đoán ngược? – harold
có - nhưng 'jmp' để đăng ký mục tiêu thực hiện điều đó là tốt, vì bạn _never_ muốn trở về' switch_to() ', thực sự (cho đến khi chuyển đổi ngữ cảnh tiếp theo). Không có sự khác biệt giữa cả hai như xa như vậy. –
@harold: Điểm tốt; các nội dung hiện tại của ngăn xếp dự báo địa chỉ trả về có giá trị sau khi chuyển đổi ngữ cảnh cho trả về từ 'context_switch()' ([trong 'kernel/sched/core.c'] (http://elixir.free-electrons.com /linux/v4.6/source/kernel/sched/core.c#L2752)), và có thể một vài cấp sao lưu ngăn xếp cuộc gọi vào bộ lập lịch biểu (cho đến khi backtraces của họ phân kỳ).Nhưng điều đó chỉ đúng nếu '% [next_ip]' luôn luôn là/thường bên trong switch_to; nó đặt 'prev_ip' theo cách đó, nhưng có lẽ đó không phải là giá trị phổ biến nhất (sự tiền hạt nhân có thể để nó ở một nơi khác?) –