2013-04-08 10 views
9

Tôi đang chạy trên hệ thống CentOS 6.3 x86 (kernel v2.6.32) x86.Tại sao tôi có thể thực hiện các thao tác dấu chấm động bên trong một mô-đun hạt nhân Linux?

Tôi đã biên dịch hàm sau thành một mô-đun trình điều khiển ký tự xương trần làm thử nghiệm để xem hạt nhân Linux phản ứng như thế nào với các hoạt động điểm động.

static unsigned floatstuff(void){ 
    float x = 3.14; 
    x *= 2.5; 
    return x; 
} 

... 

printk(KERN_INFO "x: %u", x); 

Mã được biên dịch (không mong đợi) vì vậy tôi đã chèn mô-đun và kiểm tra nhật ký với dmesg. Nhật ký hiển thị: x: 7.

Điều này có vẻ lạ; Tôi nghĩ bạn không thể thực hiện các thao tác dấu chấm động trong hạt nhân Linux - lưu một số ngoại lệ như kernel_fpu_begin(). Module đã thực hiện thao tác dấu chấm động như thế nào?

Đây có phải là vì tôi đang sử dụng bộ xử lý x86 không?

+2

Tại sao hạt nhân không thể thực hiện thao tác dấu phẩy động? – Mysticial

+0

Tại sao bạn lại ngạc nhiên? Một mô-đun hạt nhân, sau khi tất cả, chỉ là một đoạn mã được thực thi bởi CPU. Miễn là nó có thể thực thi các opcodes bạn ném vào nó, bạn ổn. –

+10

Ngoài ra, nó hoàn toàn có thể là số học được thực hiện trong quá trình biên dịch và tất cả những gì còn lại là một 'trả về 7;'. –

Trả lời

4

tôi nghĩ bạn không thể thực hiện các thao dấu chấm động trong hạt nhân Linux

Bạn không thể an toàn: thất bại trong việc sử dụng kernel_fpu_begin()/kernel_fpu_end() không có nghĩa là hướng dẫn FPU sẽ lỗi (không trên x86 ít nhất).

Thay vào đó nó sẽ âm thầm làm hỏng trạng thái FPU của không gian người dùng. Thật tệ; đừng làm thế.

Trình biên dịch không biết kernel_fpu_begin() có nghĩa là gì, do đó, nó không thể kiểm tra/cảnh báo về mã biên dịch theo hướng dẫn FPU bên ngoài vùng bắt đầu FPU.

Có thể có chế độ gỡ lỗi nơi hạt nhân tắt hướng dẫn SSE, x87 và MMX bên ngoài các khu vực kernel_fpu_begin/end, nhưng điều đó sẽ chậm hơn và không được thực hiện theo mặc định.

Có thể, mặc dù: thiết lập CR0::TS = 1 làm cho các lỗi chỉ dẫn x87, vì vậy việc chuyển ngữ cảnh FPU lười biếng có thể thực hiện được và có các bit khác cho SSE và AVX.


nhiều cách cho mã kernel lỗi gây ra vấn đề nghiêm trọng. Đây chỉ là một trong nhiều. Trong C, bạn khá nhiều luôn biết khi bạn đang sử dụng dấu chấm động (trừ khi một lỗi đánh máy kết quả trong một hằng số 1. hoặc một cái gì đó trong một ngữ cảnh mà thực sự biên dịch).


Tại sao trạng thái kiến ​​trúc FP khác với số nguyên?

Linux phải lưu/khôi phục trạng thái nguyên bất kỳ khi nào nó vào/thoát hạt nhân. Tất cả các mã cần phải sử dụng bản ghi số nguyên (ngoại trừ một khối đường thẳng khổng lồ của FPU tính toán kết thúc bằng một jmp thay vì một ret (ret đổi rsp).)

Nhưng mã hạt nhân tránh FPU thường, vì vậy Linux rời Trạng thái FPU chưa được lưu trên mục nhập từ cuộc gọi hệ thống, chỉ lưu trước khi chuyển ngữ cảnh thực tế sang một quy trình không gian người dùng khác hoặc trên kernel_fpu_begin. Nếu không, nó thường trở lại cùng một quá trình không gian người dùng trên cùng một lõi, do đó, trạng thái FPU không cần phải được khôi phục vì hạt nhân không chạm vào nó. (Và đây là nơi tham nhũng sẽ xảy ra nếu một nhiệm vụ hạt nhân thực sự đã sửa đổi trạng thái FPU. Tôi nghĩ rằng điều này đi theo cả hai cách: không gian người dùng cũng có thể bị hỏng trạng thái FPU FPU của bạn).

Trạng thái nguyên là khá nhỏ, chỉ có thanh ghi 16x 64 bit + RFLAGS và phân đoạn regs. Trạng thái FPU lớn hơn gấp đôi thậm chí không có đăng ký AVX: 8x 80 bit x87 và thanh ghi 16x XMM hoặc YMM hoặc 32x ZMM (+ MXCSR và x87 trạng thái + điều khiển). Ngoài ra thanh ghi MPX bnd0-4 được gộp lại với "FPU". Tại thời điểm này "trạng thái FPU" chỉ có nghĩa là tất cả các thanh ghi không phải là số nguyên. Trên Skylake của tôi, dmesg nói x86/fpu: Enabled xstate features 0x1f, context size is 960 bytes, using 'compacted' format.

Xem Understanding FPU usage in linux kernel; Linux hiện đại không làm công tắc ngữ cảnh FPU lười biếng theo mặc định cho các công tắc ngữ cảnh (chỉ dành cho chuyển đổi kernel/người dùng). (Nhưng bài viết đó giải thích Lazy là gì.)

Hầu hết các quy trình đều sử dụng SSE để sao chép/zeroing các khối bộ nhớ nhỏ trong mã do trình biên dịch tạo ra và hầu hết các triển khai chuỗi thư viện/memcpy/memset đều sử dụng SSE/SSE2. Ngoài ra, phần cứng hỗ trợ tối ưu hóa lưu/khôi phục là một điều bây giờ (xsaveopt/xrstor), do đó, "háo hức" FPU lưu/khôi phục có thể thực sự làm ít công việc hơn nếu một số/tất cả các thanh ghi FP chưa thực sự được sử dụng. ví dụ. tiết kiệm chỉ 128b thấp của thanh ghi YMM nếu chúng được zeroed với vzeroupper để CPU biết chúng sạch sẽ. (Và đánh dấu sự thật đó chỉ với một chút trong định dạng lưu.)

Chuyển ngữ cảnh "háo hức", các lệnh FPU luôn được kích hoạt, vì vậy mã hạt nhân xấu có thể làm hỏng chúng bất cứ lúc nào.

2

Không chắc chắn nơi nhận thức này đến từ đâu. Nhưng hạt nhân thực hiện trên cùng một bộ xử lý như mã chế độ người dùng và do đó có quyền truy cập vào cùng một bộ lệnh. Nếu bộ xử lý có thể làm nổi điểm (trực tiếp hoặc bằng đồng bộ xử lý), hạt nhân cũng có thể.

Có thể bạn đang nghĩ đến trường hợp số học dấu chấm động được mô phỏng trong phần mềm. Nhưng ngay cả như vậy, nó sẽ có sẵn trong hạt nhân (tốt, trừ khi bị vô hiệu hóa bằng cách nào đó).

Tôi tò mò, nhận thức này đến từ đâu? Có lẽ tôi đang thiếu cái gì đó.

Found this. Có vẻ là một lời giải thích tốt.

+0

Có lẽ câu hỏi của tôi hơi gây hiểu lầm. Tôi hiểu rằng FPU có thể thực thi các lệnh dấu phẩy động này (tức là bản thân mã máy là hệ thống bất khả tri), nhưng tôi bị nhầm lẫn về cách lấy mã C của tôi để biên dịch mà không có lỗi GCC về các ký hiệu không xác định như '__fixunssfsi' khi tôi đang biên dịch mô-đun hạt nhân. Tôi nghi ngờ đây chỉ là GCC tùy thuộc vào thói quen trợ giúp trong một thư viện hạt nhân không bao gồm, vì vậy làm thế nào để tôi nhận được xung quanh này để mã máy chính xác được tạo ra - bộ vi xử lý hỗ trợ dấu chấm động sau khi tất cả. –

+0

Hãy để tôi thêm rằng tôi biết các thanh ghi dấu chấm động không được lưu; Tôi không đặc biệt quan tâm đến việc tấn công một chương trình người dùng vì tôi hoàn toàn thử nghiệm mã để hiểu rõ hơn về hành vi. –

+2

Tôi đã tìm ra; Tôi cần chuyển cờ trình biên dịch này tới GCC: '-mhard-float'. –

1

Hạt nhân hệ điều hành có thể chỉ cần tắt FPU ở chế độ hạt nhân.

Trong khi hoạt động của FPU, trong khi hạt nhân hoạt động điểm động sẽ bật FPU và sau đó tắt FPU.

Nhưng bạn không thể in.

+0

Câu trả lời hay: Xem [câu trả lời SO này] (http://stackoverflow.com/a/6133286/1305969). Điều này giải thích rằng FPU có thể bị vô hiệu hóa vì lý do hiệu suất. – zx485

3

Đừng làm vậy!

Trong hạt nhân-không gian chế độ FPU bị vô hiệu hóa do một số lý do:

  • Nó cho phép Linux để chạy trong kiến ​​trúc mà không có FPU
  • Nó tránh để lưu và khôi phục lại toàn bộ các thanh ghi tất cả các hạt nhân/không gian người dùng chuyển đổi (nó có thể tăng gấp đôi thời gian chuyển đổi ngữ cảnh)
  • Về cơ bản tất cả các chức năng hạt nhân sử dụng số nguyên cũng đại diện cho số thập phân -> bạn có thể không cần điểm động
  • Trong Linux, preemption Là vô hiệu hóa khi kernel-không gian đang chạy trong chế độ FPU
  • số Floating point là ác và may generate very bad unexpected behaviour

Nếu bạn thực sự muốn sử dụng số FP (và bạn không nên), bạn phải sử dụng kernel_fpu_beginkernel_fpu_end nguyên thủy để tránh để phá vỡ sổ đăng ký không gian người dùng và bạn nên tính đến tất cả các vấn đề có thể xảy ra (bảo mật được bao gồm) trong việc xử lý các số FP.

Các vấn đề liên quan