Sự khác biệt giữa không gian người dùng và không gian hạt nhân là gì?


72

Không gian Kernel có được sử dụng khi Kernel đang thực hiện thay mặt cho chương trình người dùng tức là Gọi hệ thống không? Hoặc nó là không gian địa chỉ cho tất cả các luồng Kernel (ví dụ như trình lập lịch biểu)?

Nếu là lần đầu tiên, điều đó có nghĩa là chương trình người dùng bình thường không thể có nhiều hơn 3 GB bộ nhớ (nếu phân chia là 3 GB + 1 GB)? Ngoài ra, trong trường hợp đó, làm thế nào kernel có thể sử dụng High Memory, bởi vì địa chỉ bộ nhớ ảo nào sẽ được ánh xạ tới, vì 1GB không gian kernel sẽ được ánh xạ logic?

Câu trả lời:


93

Không gian Kernel có được sử dụng khi Kernel đang thực hiện thay mặt cho chương trình người dùng tức là Gọi hệ thống không? Hoặc nó là không gian địa chỉ cho tất cả các luồng Kernel (ví dụ như trình lập lịch biểu)?

Vâng và vâng.

Trước khi chúng ta đi xa hơn, chúng ta nên nói điều này về bộ nhớ.

Bộ nhớ được chia thành hai khu vực riêng biệt:

  • Không gian người dùng , là một tập hợp các vị trí nơi các tiến trình người dùng bình thường chạy (tức là mọi thứ khác ngoài kernel). Vai trò của kernel là quản lý các ứng dụng đang chạy trong không gian này khỏi sự lộn xộn với nhau và máy.
  • Không gian kernel , là vị trí lưu trữ mã của kernel và thực thi theo.

Các tiến trình chạy trong không gian người dùng chỉ có quyền truy cập vào một phần bộ nhớ hạn chế, trong khi đó kernel có quyền truy cập vào tất cả bộ nhớ. Các quy trình đang chạy trong không gian người dùng cũng không có quyền truy cập vào không gian kernel. Các tiến trình không gian người dùng chỉ có thể truy cập một phần nhỏ của kernel thông qua một giao diện được hiển thị bởi kernel - các cuộc gọi hệ thống . Nếu một quá trình thực hiện một cuộc gọi hệ thống, một ngắt phần mềm được gửi đến kernel, sau đó gửi trình xử lý ngắt thích hợp và tiếp tục công việc của nó sau khi trình xử lý kết thúc.

Mã không gian hạt nhân có thuộc tính để chạy trong "chế độ nhân", mà (trong máy tính để bàn điển hình của bạn -x86-) là những gì bạn gọi mã thực thi trong vòng 0 . Điển hình trong kiến ​​trúc x86, có 4 vòng bảo vệ . Ring 0 (chế độ kernel), Ring 1 (có thể được sử dụng bởi các trình ảo hóa hoặc trình điều khiển máy ảo), Ring 2 (có thể được sử dụng bởi trình điều khiển, mặc dù vậy tôi không chắc về điều đó). Vòng 3 là những gì các ứng dụng điển hình chạy theo. Đây là vòng đặc quyền ít nhất và các ứng dụng chạy trên nó có quyền truy cập vào một tập hợp con các hướng dẫn của bộ xử lý. Ring 0 (không gian kernel) là vòng đặc quyền nhất và có quyền truy cập vào tất cả các hướng dẫn của máy. Ví dụ, ứng dụng "đơn giản" (như trình duyệt) không thể sử dụng hướng dẫn lắp ráp x86lgdtđể tải bảng mô tả toàn cầu hoặc hlttạm dừng bộ xử lý.

Nếu là lần đầu tiên, điều đó có nghĩa là chương trình người dùng bình thường không thể có nhiều hơn 3 GB bộ nhớ (nếu phân chia là 3 GB + 1 GB)? Ngoài ra, trong trường hợp đó, làm thế nào kernel có thể sử dụng High Memory, bởi vì địa chỉ bộ nhớ ảo nào sẽ được ánh xạ tới, vì 1GB không gian kernel sẽ được ánh xạ logic?

Để có câu trả lời cho điều này, xin vui lòng tham khảo câu trả lời xuất sắc của wag tại đây


4
Đừng ngần ngại cho tôi biết nếu tôi đã phạm sai lầm ở đâu đó. Tôi chưa quen với lập trình kernel và tôi đã bỏ qua những gì tôi đã học được cho đến nay, cùng với một số thông tin khác tôi tìm thấy trên web. Điều đó có nghĩa là có thể có những thiếu sót trong cách hiểu của tôi về các khái niệm có thể được thể hiện trong văn bản.
NlightNFotis

Cảm ơn! Tôi nghĩ bây giờ tôi hiểu nó tốt hơn. Chỉ để chắc chắn rằng tôi hiểu đúng, tôi có thêm một câu hỏi nữa. Một lần nữa xem xét rằng 3 GB đầu tiên được sử dụng cho không gian người dùng và 128 MB dung lượng nhân được sử dụng cho Bộ nhớ cao, 896 MB (Bộ nhớ thấp) còn lại có được ánh xạ tĩnh khi khởi động không?
Poojan

1
@NlightNFotis Tôi nói rằng gần 15 người tin rằng bất cứ điều gì bạn nói, đều đúng (hoặc do đó bạn khiến chúng tôi phải suy nghĩ;))
Braiam

Tôi nghĩ rằng vòng x86 -1là dành cho các siêu giám sát? vi.wikipedia.org/wiki/Protection_ring
Dori

1
Lưu ý sự khác biệt giữa bộ nhớ ảo và bộ nhớ vật lý. Hầu hết những gì bạn hỏi là về bộ nhớ ảo. Điều này được ánh xạ tới bộ nhớ vật lý, điều này trở nên phức tạp khi bộ nhớ vật lý đạt tới 3GB và PAE được sử dụng. Sau đó, nó trở nên đơn giản trở lại khi sử dụng kernel 64 bit, trong trường hợp này, địa chỉ âm được dành riêng cho kernel và địa chỉ dương cho không gian người dùng. Các quy trình 32 bit hiện có thể sử dụng 4GB không gian ảo. Các quy trình 64 bit có thể sử dụng nhiều hơn nữa - thường có giá trị 48 bit (tại thời điểm này trên x86-64).
ctrl-alt-delor

16

Các vòng CPU là sự phân biệt rõ ràng nhất

Trong chế độ được bảo vệ x86, CPU luôn ở một trong 4 vòng. Nhân Linux chỉ sử dụng 0 và 3:

  • 0 cho nhân
  • 3 cho người dùng

Đây là định nghĩa khó nhất và nhanh nhất của kernel vs userland.

Tại sao Linux không sử dụng vòng 1 và 2: https://stackoverflow.com/questions/6710040/cpu-privilege-rings-why-rings-1-and-2-arent- used

Làm thế nào là vòng hiện tại được xác định?

Vòng hiện tại được chọn bởi sự kết hợp của:

  • bảng mô tả toàn cầu: một bảng trong bộ nhớ của các mục GDT và mỗi mục có một trường Privlmã hóa vòng.

    Lệnh LGDT đặt địa chỉ cho bảng mô tả hiện tại.

    Xem thêm: http://wiki.osdev.org/Global_Descriptor_Table

  • phân đoạn đăng ký CS, DS, v.v., trỏ đến chỉ mục của một mục trong GDT.

    Ví dụ: CS = 0có nghĩa là mục nhập đầu tiên của GDT hiện đang hoạt động cho mã thực thi.

Mỗi chiếc nhẫn có thể làm gì?

Chip CPU được chế tạo vật lý sao cho:

  • vòng 0 có thể làm bất cứ điều gì

  • vòng 3 không thể chạy một số hướng dẫn và ghi vào một số thanh ghi, đáng chú ý nhất là:

    • không thể thay đổi chiếc nhẫn của riêng mình! Nếu không, nó có thể tự đặt thành số 0 và nhẫn sẽ vô dụng.

      Nói cách khác, không thể sửa đổi bộ mô tả phân đoạn hiện tại , xác định vòng hiện tại.

    • không thể sửa đổi các bảng trang: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

      Nói cách khác, không thể sửa đổi thanh ghi CR3 và chính việc phân trang ngăn không cho sửa đổi các bảng trang.

      Điều này ngăn một tiến trình nhìn thấy bộ nhớ của các tiến trình khác vì lý do bảo mật / dễ dàng cho các lý do lập trình.

    • không thể đăng ký xử lý ngắt. Chúng được cấu hình bằng cách ghi vào các vị trí bộ nhớ, điều này cũng được ngăn chặn bằng cách phân trang.

      Trình xử lý chạy trong vòng 0 và sẽ phá vỡ mô hình bảo mật.

      Nói cách khác, không thể sử dụng các hướng dẫn LGDT và LIDT.

    • không thể thực hiện các hướng dẫn IO như inout, do đó có quyền truy cập phần cứng tùy ý.

      Mặt khác, ví dụ, quyền truy cập tệp sẽ vô dụng nếu bất kỳ chương trình nào có thể đọc trực tiếp từ đĩa.

      Chính xác hơn là nhờ Michael Petch : hệ điều hành thực sự có thể cho phép các hướng dẫn IO trên vòng 3, điều này thực sự được kiểm soát bởi phân đoạn trạng thái Nhiệm vụ .

      Điều không thể xảy ra là vòng 3 sẽ tự cho phép làm như vậy nếu nó không có nó ở vị trí đầu tiên.

      Linux luôn không cho phép nó. Xem thêm: https://stackoverflow.com/questions/2711044/why-doesnt-linux-use-the-hardware-context-switch-via-the-tss

Làm thế nào để các chương trình và hệ điều hành chuyển tiếp giữa các vòng?

  • khi CPU được bật, nó bắt đầu chạy chương trình ban đầu ở vòng 0 (cũng khá tốt, nhưng đó là một xấp xỉ tốt). Bạn có thể nghĩ chương trình ban đầu này là kernel (nhưng thông thường nó là bootloader sau đó gọi kernel vẫn ở vòng 0).

  • khi một quá trình userland muốn kernel thực hiện một cái gì đó cho nó như ghi vào một tệp, nó sử dụng một lệnh tạo ra một ngắt như int 0x80hoặcsyscall để báo hiệu kernel. x86-64 Linux tòa nhà xin chào ví dụ thế giới:

    .data
    hello_world:
        .ascii "hello world\n"
        hello_world_len = . - hello_world
    .text
    .global _start
    _start:
        /* write */
        mov $1, %rax
        mov $1, %rdi
        mov $hello_world, %rsi
        mov $hello_world_len, %rdx
        syscall
    
        /* exit */
        mov $60, %rax
        mov $0, %rdi
        syscall
    

    biên dịch và chạy:

    as -o hello_world.o hello_world.S
    ld -o hello_world.out hello_world.o
    ./hello_world.out
    

    GitHub ngược dòng .

    Khi điều này xảy ra, CPU gọi một trình xử lý gọi lại ngắt mà kernel đã đăng ký khi khởi động. Dưới đây là một ví dụ cụ thể về thanh ghi đăng ký một trình xử lý và sử dụng nó .

    Trình xử lý này chạy trong vòng 0, quyết định xem kernel có cho phép hành động này không, thực hiện hành động đó và khởi động lại chương trình userland trong vòng 3. x86_64

  • khi execcuộc gọi hệ thống được sử dụng (hoặc khi kernel sẽ bắt đầu/init ), kernel sẽ chuẩn bị các thanh ghi và bộ nhớ của tiến trình người dùng mới, sau đó nó nhảy đến điểm vào và chuyển CPU sang vòng 3

  • Nếu chương trình cố gắng làm một cái gì đó nghịch ngợm như ghi vào một thanh ghi bị cấm hoặc địa chỉ bộ nhớ (vì phân trang), CPU cũng gọi một số trình xử lý gọi lại kernel trong vòng 0.

    Nhưng vì vùng người dùng nghịch ngợm, hạt nhân có thể giết quá trình lần này hoặc đưa ra cảnh báo bằng tín hiệu.

  • Khi kernel khởi động, nó sẽ thiết lập đồng hồ phần cứng với một số tần số cố định, tạo ra các ngắt theo định kỳ.

    Đồng hồ phần cứng này tạo ra các ngắt chạy vòng 0 và cho phép nó lập lịch trình quá trình người dùng nào thức dậy.

    Bằng cách này, lập lịch có thể xảy ra ngay cả khi các quy trình không thực hiện bất kỳ cuộc gọi hệ thống nào.

Điểm có nhiều vòng là gì?

Có hai ưu điểm chính của việc tách kernel và userland:

  • việc tạo ra các chương trình sẽ dễ dàng hơn vì bạn chắc chắn sẽ không can thiệp vào chương trình kia. Ví dụ, một quy trình người dùng không phải lo lắng về việc ghi đè bộ nhớ của chương trình khác vì phân trang, cũng như không đặt phần cứng ở trạng thái không hợp lệ cho quy trình khác.
  • nó an toàn hơn Ví dụ: quyền truy cập tệp và phân tách bộ nhớ có thể ngăn ứng dụng hack đọc dữ liệu ngân hàng của bạn. Tất nhiên, điều này cho rằng bạn tin tưởng hạt nhân.

Làm thế nào để chơi xung quanh nó?

Tôi đã tạo một thiết lập kim loại trần nên là cách tốt để thao tác trực tiếp với nhẫn: https://github.com/cirosantilli/x86-bare-metal-examples

Thật không may, tôi đã không đủ kiên nhẫn để tạo một ví dụ về vùng người dùng, nhưng tôi đã đi xa như thiết lập phân trang, vì vậy vùng sử dụng nên khả thi. Tôi muốn thấy một yêu cầu kéo.

Ngoài ra, các mô-đun hạt nhân Linux chạy trong vòng 0, vì vậy bạn có thể sử dụng chúng để thử các hoạt động đặc quyền, ví dụ: đọc các thanh ghi điều khiển: https://stackoverflow.com/questions/7415515/how-to-access-the-control-registers -cr0-cr2-cr3-from-a-chương trình-nhận-phân đoạn / 7419306 # 7419306

Dưới đây là một thiết lập QEMU + Buildroot thuận tiện để dùng thử mà không làm chết máy chủ của bạn.

Nhược điểm của các mô-đun hạt nhân là các kthread khác đang chạy và có thể can thiệp vào các thử nghiệm của bạn. Nhưng trên lý thuyết, bạn có thể tiếp quản tất cả các trình xử lý ngắt với mô-đun hạt nhân của mình và sở hữu hệ thống, đó thực sự là một dự án thú vị.

Vòng âm

Mặc dù các vòng âm không thực sự được tham chiếu trong hướng dẫn sử dụng Intel, nhưng thực tế có các chế độ CPU có khả năng hơn cả vòng 0 và do đó rất phù hợp với tên "vòng âm".

Một ví dụ là chế độ hypanneror được sử dụng trong ảo hóa.

Để biết thêm chi tiết, hãy xem: https://security.stackexchange.com/questions/129098/what-is-protection-ring-1

CÁNH TAY

Trong ARM, các vòng được gọi là Cấp độ ngoại lệ thay vào đó, nhưng các ý tưởng chính vẫn giữ nguyên.

Có 4 cấp độ ngoại lệ trong ARMv8, thường được sử dụng là:

  • EL0: người dùng

  • EL1: kernel ("người giám sát" theo thuật ngữ ARM).

    Được nhập với svchướng dẫn (SuperVisor Call), trước đây được biết đến như swi trước khi hợp nhất , là hướng dẫn được sử dụng để thực hiện các cuộc gọi hệ thống Linux. Xin chào thế giới ví dụ ARMv8:

    .text
    .global _start
    _start:
        /* write */
        mov x0, 1
        ldr x1, =msg
        ldr x2, =len
        mov x8, 64
        svc 0
    
        /* exit */
        mov x0, 0
        mov x8, 93
        svc 0
    msg:
        .ascii "hello syscall v8\n"
    len = . - msg
    

    GitHub ngược dòng .

    Kiểm tra nó với QEMU trên Ubuntu 16.04:

    sudo apt-get install qemu-user gcc-arm-linux-gnueabihf
    arm-linux-gnueabihf-as -o hello.o hello.S
    arm-linux-gnueabihf-ld -o hello hello.o
    qemu-arm hello
    

    Dưới đây là một ví dụ cụ thể về thanh ghi đăng ký trình xử lý SVC và thực hiện cuộc gọi SVC .

  • EL2: hypannerors , ví dụ Xen .

    Được nhập với hvchướng dẫn (Cuộc gọi HyperVisor).

    Một trình ảo hóa là cho một hệ điều hành, một hệ điều hành là gì đối với người dùng.

    Ví dụ, Xen cho phép bạn chạy nhiều HĐH như Linux hoặc Windows trên cùng một hệ thống cùng một lúc và nó cách ly các HĐH với nhau để bảo mật và dễ gỡ lỗi, giống như Linux làm cho các chương trình người dùng.

    Hypervisor là một phần quan trọng của cơ sở hạ tầng đám mây ngày nay: chúng cho phép nhiều máy chủ chạy trên một phần cứng duy nhất, giữ mức sử dụng phần cứng luôn ở mức gần 100% và tiết kiệm rất nhiều tiền.

    AWS đã sử dụng Xen cho đến năm 2017 khi việc chuyển sang KVM đưa ra tin tức .

  • EL3: một cấp độ khác. Ví dụ TODO.

    Được nhập với smchướng dẫn (Cuộc gọi Chế độ Bảo mật)

Các ARMv8 Kiến trúc mô hình tham chiếu DDI 0487C.a - Chương D1 - Hệ thống AArch64 Cấp Programmer của Người mẫu - Hình D1-1 minh họa này đẹp:

nhập mô tả hình ảnh ở đây

Lưu ý cách ARM, có thể do lợi ích của nhận thức muộn, có quy ước đặt tên tốt hơn cho các cấp đặc quyền so với x86, mà không cần mức âm: 0 là thấp hơn và cao nhất 3. Mức cao hơn có xu hướng được tạo ra thường xuyên hơn so với mức thấp hơn.

EL hiện tại có thể được truy vấn theo MRShướng dẫn: https://stackoverflow.com/questions/31787617/what-is-the-civerse-execut-mode-exception-level-etc

ARM không yêu cầu tất cả các cấp ngoại lệ phải có mặt để cho phép các triển khai không cần tính năng này để tiết kiệm diện tích chip. ARMv8 "Cấp độ ngoại lệ" cho biết:

Việc triển khai có thể không bao gồm tất cả các cấp độ Ngoại lệ. Tất cả các triển khai phải bao gồm EL0 và EL1. EL2 và EL3 là tùy chọn.

Ví dụ, QEMU mặc định cho EL1, nhưng EL2 và EL3 có thể được bật bằng các tùy chọn dòng lệnh: https://stackoverflow.com/questions/42824706/qemu-system-aarch64-entering-el1-when-emulation-a53-power-up

Đoạn mã được thử nghiệm trên Ubuntu 18.10.


3

Nếu là lần đầu tiên, điều đó có nghĩa là chương trình người dùng bình thường không thể có nhiều hơn 3 GB bộ nhớ (nếu phân chia là 3 GB + 1 GB)?

Có, đây là trường hợp trên một hệ thống linux bình thường. Có một bộ các bản vá "4G / 4G" nổi xung quanh tại một thời điểm khiến cho không gian địa chỉ kernel và người dùng hoàn toàn độc lập (với chi phí hiệu năng vì nó khiến kernel khó truy cập bộ nhớ người dùng hơn) nhưng tôi không nghĩ họ đã từng được hợp nhất ngược dòng và quan tâm suy yếu với sự gia tăng của x86-64

Ngoài ra, trong trường hợp đó, làm thế nào kernel có thể sử dụng High Memory, bởi vì địa chỉ bộ nhớ ảo nào sẽ được ánh xạ tới, vì 1GB không gian kernel sẽ được ánh xạ logic?

Cách linux sử dụng để làm việc (và vẫn làm trên các hệ thống có bộ nhớ nhỏ so với không gian địa chỉ) là toàn bộ bộ nhớ vật lý được ánh xạ vĩnh viễn vào phần nhân của không gian địa chỉ. Điều này cho phép kernel truy cập tất cả bộ nhớ vật lý mà không cần ánh xạ lại nhưng rõ ràng nó không mở rộng ra các máy 32 bit có nhiều bộ nhớ vật lý.

Thế là khái niệm về trí nhớ thấp và cao đã ra đời. Bộ nhớ "thấp" được ánh xạ vĩnh viễn vào không gian địa chỉ hạt nhân. Bộ nhớ "cao" thì không.

Khi bộ xử lý đang chạy một cuộc gọi hệ thống, nó đang chạy ở chế độ kernel nhưng vẫn trong bối cảnh của quy trình hiện tại. Vì vậy, nó có thể truy cập trực tiếp cả không gian địa chỉ kernel và không gian địa chỉ người dùng của quy trình hiện tại (giả sử bạn không sử dụng các bản vá 4G / 4G đã nói ở trên). Điều này có nghĩa là không có vấn đề gì đối với bộ nhớ "cao" được phân bổ cho quy trình người dùng.

Sử dụng bộ nhớ "cao" cho mục đích kernel là một vấn đề. Để truy cập bộ nhớ cao không được ánh xạ tới quy trình hiện tại, nó phải được ánh xạ tạm thời vào không gian địa chỉ của kernel. Điều đó có nghĩa là mã bổ sung và hình phạt hiệu suất.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.