Linux không sử dụng phân đoạn mà chỉ phân trang?


24

Giao diện lập trình Linux hiển thị bố cục của không gian địa chỉ ảo của một quy trình. Là mỗi khu vực trong sơ đồ một phân khúc?

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

Từ việc hiểu hạt nhân Linux ,

Có đúng không khi điều đó có nghĩa là đơn vị phân đoạn trong MMU ánh xạ các phân đoạn và độ lệch trong các phân đoạn vào địa chỉ bộ nhớ ảo và đơn vị phân trang sau đó ánh xạ địa chỉ bộ nhớ ảo sang địa chỉ bộ nhớ vật lý?

Đơn vị quản lý bộ nhớ (MMU) biến đổi một địa chỉ logic thành một địa chỉ tuyến tính bằng một mạch phần cứng gọi là đơn vị phân đoạn; sau đó, một mạch phần cứng thứ hai được gọi là đơn vị phân trang biến đổi địa chỉ tuyến tính thành địa chỉ vật lý (xem Hình 2-1).

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

Vậy thì tại sao nó nói rằng Linux không sử dụng phân đoạn mà chỉ phân trang?

Phân đoạn đã được bao gồm trong các bộ vi xử lý 80x86 để khuyến khích các lập trình viên phân chia các ứng dụng của họ thành các thực thể liên quan đến logic, như chương trình con hoặc các vùng dữ liệu toàn cầu và cục bộ. Tuy nhiên, Linux sử dụng phân khúc theo một cách rất hạn chế. Trong thực tế, phân đoạn và phân trang có phần dư thừa, bởi vì cả hai có thể được sử dụng để phân tách các không gian địa chỉ vật lý của các quy trình: phân đoạn có thể gán một không gian địa chỉ tuyến tính khác nhau cho mỗi quy trình, trong khi phân trang có thể ánh xạ cùng một không gian địa chỉ tuyến tính vào các không gian địa chỉ vật lý khác nhau . Linux thích phân trang để phân đoạn vì những lý do sau:

• Quản lý bộ nhớ đơn giản hơn khi tất cả các quy trình sử dụng cùng một giá trị thanh ghi phân đoạn, nghĩa là khi chúng chia sẻ cùng một bộ địa chỉ tuyến tính.

• Một trong những mục tiêu thiết kế của Linux là tính di động đối với một loạt các kiến ​​trúc; Các kiến ​​trúc RISC, đặc biệt, có hỗ trợ hạn chế cho phân khúc.

Phiên bản 2.6 của Linux chỉ sử dụng phân đoạn khi kiến ​​trúc 80x86 yêu cầu.


Bạn có thể chỉ định các phiên bản xin vui lòng. Nó cũng có thể hữu ích để xác định tên tác giả. Tôi biết ít nhất đầu tiên là từ một nhân vật nổi bật. Tuy nhiên, cả hai tên tiêu đề đều hơi chung chung, ban đầu tôi không rõ rằng bạn đang nói về sách :-).
sourcejedi

2
Re "Phân đoạn đã được bao gồm trong bộ vi xử lý 80x86 ...": Điều đó không đúng. Đây là một di sản của bộ xử lý 808x, có con trỏ dữ liệu 16 bit và phân đoạn bộ nhớ 64 Kb. Con trỏ phân đoạn cho phép bạn chuyển các phân đoạn để giải quyết nhiều bộ nhớ hơn. Kiến trúc đó được chuyển sang 80x86 (với kích thước con trỏ tăng lên 33 bit). Ngày nay, trong mô hình x86_64, bạn có con trỏ 64 bit có thể (về mặt lý thuyết - tôi nghĩ chỉ có 48 bit thực sự được sử dụng) giải quyết 16 exabyte, vì vậy các phân đoạn không cần thiết.
jamesqf

2
@jamesqf, tốt, chế độ được bảo vệ 32 bit trong 386 hỗ trợ các phân đoạn khá khác so với các con trỏ tỷ lệ 16 byte mà chúng có trong 8086, vì vậy nó không chỉ là di sản đơn thuần. Tất nhiên, điều đó không nói lên gì về tính hữu dụng của chúng.
ilkkachu

@jamesqf 80186 có cùng mô hình bộ nhớ với 8086, không có "33 bit"
Jasen

Không có câu trả lời nào, do đó chỉ là một nhận xét: Phân đoạn và Trang chỉ có thể so sánh trong bối cảnh hoán đổi (ví dụ: hoán đổi trang so với hoán đổi phân đoạn) và trong bối cảnh đó Trang tráo đổi chỉ thổi phồng phân đoạn ra khỏi nước. Nếu bạn trao đổi phân khúc vào / ra, bạn cần trao đổi toàn bộ phân khúc, có thể là 2-4 GB. Điều này chưa bao giờ là một điều thực sự được sử dụng trên x86. Với các trang bạn luôn có thể làm việc trên các đơn vị 4KB. Khi nói đến việc truy cập bộ nhớ thì các phân đoạn và trang có liên quan thông qua các bảng trang và so sánh sẽ là táo với cam.
vhu

Câu trả lời:


20

Kiến trúc x86-64 không sử dụng phân đoạn trong chế độ dài (chế độ 64 bit).

Bốn trong số các thanh ghi phân đoạn: CS, SS, DS và ES bị buộc về 0 và giới hạn là 2 ^ 64.

https://en.wikipedia.org/wiki/X86_memory_seributionation#Later_developments

HĐH không còn có thể giới hạn phạm vi "địa chỉ tuyến tính" nào khả dụng. Do đó, nó không thể sử dụng phân đoạn để bảo vệ bộ nhớ; nó phải hoàn toàn dựa vào phân trang.

Đừng lo lắng về các chi tiết của CPU x86 sẽ chỉ áp dụng khi chạy ở chế độ 32 bit cũ. Linux cho các chế độ 32 bit không được sử dụng nhiều. Nó thậm chí có thể được coi là "trong tình trạng bỏ bê lành tính trong vài năm". Xem hỗ trợ 32-bit x86 trong Fedora [LWN.net, 2017].

(Điều này xảy ra là Linux 32 bit cũng không sử dụng phân đoạn. Nhưng bạn không cần tin tưởng vào điều đó, bạn có thể bỏ qua nó :-).


Đó là một chút quá lời. cơ sở / giới hạn được cố định ở mức 0 / -1 ở chế độ dài cho các phân đoạn gốc-8086 cũ (CS / DS / ES / SS), nhưng FS và GS vẫn có cơ sở phân đoạn tùy ý. Và bộ mô tả phân đoạn được tải vào CS xác định xem CPU thực thi ở chế độ 32 hay 64 bit. Và Không gian người dùng trên x86-64 Linux sử dụng FS cho lưu trữ luồng cục bộ ( mov eax, [fs:rdi + 16]). Nhân sử dụng GS (sau swapgs) để tìm ngăn xếp hạt nhân của quy trình hiện tại trong syscallđiểm vào. Nhưng có, phân đoạn không được sử dụng như một phần của cơ chế quản lý bộ nhớ / bảo vệ bộ nhớ chính của hệ điều hành.
Peter Cordes

Về cơ bản, đây là câu trích dẫn trong câu hỏi có nghĩa là "Phiên bản 2.6 của Linux chỉ sử dụng phân đoạn khi kiến ​​trúc 80x86 yêu cầu". Nhưng đoạn 2 của bạn về cơ bản là sai. Linux sử dụng phân đoạn cơ bản giống hệt nhau ở chế độ 32 và 64 bit. tức là cơ sở = 0 / giới hạn = 2 ^ 32 hoặc 2 ^ 64 cho các phân đoạn cổ điển (CS / DS / ES / SS) được sử dụng ngầm theo hướng dẫn thông thường. Không có gì "thêm" để lo lắng về mã Linux 32 bit; chức năng CTNH có nhưng không được sử dụng.
Peter Cordes

@PeterCordes về cơ bản bạn đang hiểu câu trả lời sai :-). Vì vậy, tôi đã chỉnh sửa nó để thử và làm cho lập luận của tôi bớt mơ hồ.
sourcejedi

Cải thiện tốt, bây giờ nó không gây hiểu lầm. Tôi hoàn toàn đồng ý với quan điểm thực tế của bạn, đó là bạn có thể và hoàn toàn bỏ qua phân đoạn x86, bởi vì nó chỉ được sử dụng cho những thứ quản lý hệ thống osdev và cho TLS. Nếu cuối cùng bạn muốn tìm hiểu về nó, sẽ dễ hiểu hơn rất nhiều sau khi bạn đã hiểu x86 asm với một mô hình bộ nhớ phẳng.
Peter Cordes

8

Vì x86 có các phân đoạn, không thể không sử dụng chúng. Nhưng cả hai địa chỉ cơ sở cs(phân đoạn mã) và ds(phân đoạn dữ liệu) đều được đặt thành không, vì vậy phân đoạn này không thực sự được sử dụng. Một ngoại lệ là dữ liệu cục bộ của luồng, một trong những thanh ghi phân đoạn thường không được sử dụng trỏ đến dữ liệu cục bộ của luồng. Nhưng đó là chủ yếu để tránh đặt một trong những đăng ký mục đích chung cho nhiệm vụ này.

Điều đó không nói rằng Linux không sử dụng phân đoạn trên x86, vì điều đó là không thể. Bạn đã nhấn mạnh một phần, Linux sử dụng phân đoạn theo một cách rất hạn chế . Phần thứ hai là Linux chỉ sử dụng phân đoạn khi kiến ​​trúc 80x86 yêu cầu

Bạn đã trích dẫn lý do, phân trang dễ dàng hơn và dễ mang theo hơn.


7

Là mỗi khu vực trong sơ đồ một phân khúc?

Không.

Mặc dù hệ thống phân đoạn (ở chế độ được bảo vệ 32 bit trên x86) được thiết kế để hỗ trợ các phân đoạn mã, dữ liệu và ngăn xếp riêng biệt, trong thực tế, tất cả các phân đoạn được đặt trong cùng một vùng nhớ. Nghĩa là, chúng bắt đầu từ 0 và kết thúc ở cuối bộ nhớ (*) . Điều đó làm cho các địa chỉ logic và địa chỉ tuyến tính bằng nhau.

Đây được gọi là mô hình bộ nhớ "phẳng" và có phần đơn giản hơn mô hình mà bạn có các phân đoạn riêng biệt và sau đó con trỏ bên trong chúng. Cụ thể, một mô hình được phân đoạn yêu cầu các con trỏ dài hơn, vì bộ chọn phân đoạn phải được thêm vào cùng với con trỏ bù. (Bộ chọn phân đoạn 16 bit + offset 32 ​​bit cho tổng số con trỏ 48 bit; so với chỉ con trỏ phẳng 32 bit.)

Chế độ dài 64 bit thậm chí không thực sự hỗ trợ phân đoạn ngoài mô hình bộ nhớ phẳng.

Nếu bạn đã lập trình ở chế độ được bảo vệ 16 bit trên 286, bạn sẽ cần nhiều phân đoạn hơn, vì không gian địa chỉ là 24 bit nhưng con trỏ chỉ có 16 bit.

(* Lưu ý rằng tôi không thể nhớ cách Linux 32 bit xử lý phân tách kernel / không gian người dùng. Phân đoạn sẽ cho phép thông qua việc đặt giới hạn phân đoạn không gian người dùng để chúng không bao gồm không gian kernel. Paging cho phép vì nó cung cấp mức bảo vệ trên mỗi trang.)

Vậy thì tại sao nó nói rằng Linux không sử dụng phân đoạn mà chỉ phân trang?

X86 vẫn có các phân đoạn và bạn không thể vô hiệu hóa chúng. Chúng chỉ được sử dụng ít nhất có thể. Trong chế độ được bảo vệ 32 bit, các phân đoạn cần được thiết lập cho mô hình phẳng và ngay cả ở chế độ 64 bit, chúng vẫn tồn tại.


Huh, tôi đoán rằng hạt nhân 32 bit có thể giảm thiểu Meltdown rẻ hơn so với việc thay đổi bảng trang bằng cách đặt giới hạn phân đoạn trên CS / DS / ES / SS để ngăn không gian người dùng truy cập trên 2G hoặc 3G. (Bản thô Meltdown là một cách giải quyết cho bit kernel / user trong các mục trong bảng trang, cho phép không gian người dùng đọc từ các trang chỉ được ánh xạ kernel). Các trang VDSO có thể được ánh xạ ở đầu 4G, mặc dù: / wrfsbaselà bất hợp pháp ở chế độ được bảo vệ / tương thích, chỉ ở chế độ dài, do đó, trên không gian người dùng 32 bit không thể đặt mức cao của cơ sở FS.
Peter Cordes

Trên kernel 64 bit, không gian người dùng 32 bit có khả năng nhảy xa tới phân đoạn mã 64 bit, do đó bạn không thể phụ thuộc vào giới hạn phân đoạn để bảo vệ Meltdown, chỉ có thể trong kernel 32 bit thuần túy. (Điều này có nhược điểm lớn trên các máy có nhiều RAM vật lý, ví dụ như hết mem thấp cho ngăn xếp luồng.) Dù sao, vâng, Linux bảo vệ bộ nhớ kernel bằng phân trang, để lại cơ sở / giới hạn = 0 / -1 trong không gian người dùng cho bình thường các phân đoạn (không phải là FS / GS được sử dụng cho lưu trữ luồng cục bộ).
Peter Cordes

Trước khi bit NX được hỗ trợ trong các bảng trang phần cứng (PAE), một số bản vá bảo mật ban đầu đã sử dụng phân đoạn để tạo các ngăn xếp không thể thực thi cho mã không gian người dùng. ví dụ linux.com/news/exec-shield-new-linux-security-feature (bài Ingo Molnar của đề cập đến "xuất sắc năng lượng mặt trời thiết kế của 'phi exec chồng vá'".)
Peter Cordes

3

Linux x86 / 32 không sử dụng phân đoạn theo nghĩa là nó khởi tạo tất cả các phân đoạn theo cùng một địa chỉ và giới hạn tuyến tính. Kiến trúc x86 yêu cầu chương trình phải có các phân đoạn: mã chỉ có thể được thực thi từ phân đoạn mã, ngăn xếp chỉ có thể được đặt trong phân đoạn ngăn xếp, dữ liệu chỉ có thể được thao tác trong một trong các phân đoạn dữ liệu. Linux bỏ qua cơ chế này bằng cách đặt tất cả các phân đoạn theo cùng một cách (với các ngoại lệ mà sách của bạn không đề cập đến dù sao), để cùng một địa chỉ logic có hiệu lực trong bất kỳ phân khúc nào. Điều này thực tế tương đương với việc không có phân khúc nào cả.


2

Là mỗi khu vực trong sơ đồ một phân khúc?

Đây là 2 cách sử dụng gần như hoàn toàn khác nhau của từ "phân khúc"

  • Các thanh ghi phân đoạn / phân đoạn x86: Các hệ điều hành x86 hiện đại sử dụng mô hình bộ nhớ phẳng trong đó tất cả các phân đoạn có cùng cơ sở = 0 và giới hạn = tối đa trong chế độ 32 bit, giống như phần cứng thực thi ở chế độ 64 bit , tạo ra loại phân đoạn . (Ngoại trừ FS hoặc GS, được sử dụng cho lưu trữ cục bộ luồng ngay cả ở chế độ 64 bit.)
  • Phần / phân đoạn trình liên kết / chương trình nạp. ( Sự khác biệt của phần và phân đoạn trong định dạng tệp ELF )

Các cách sử dụng có nguồn gốc chung: nếu bạn đang sử dụng mô hình bộ nhớ được phân đoạn (đặc biệt là không có bộ nhớ ảo phân trang), bạn có thể có dữ liệu và địa chỉ BSS có liên quan đến cơ sở phân đoạn DS, xếp chồng so với cơ sở SS và mã liên quan đến Địa chỉ cơ sở CS.

Vì vậy, nhiều chương trình khác nhau có thể được tải đến các địa chỉ tuyến tính khác nhau hoặc thậm chí được di chuyển sau khi bắt đầu mà không thay đổi độ lệch 16 hoặc 32 bit so với cơ sở phân đoạn.

Nhưng sau đó bạn phải biết một con trỏ có liên quan đến phân khúc nào, vì vậy bạn có "con trỏ xa", v.v. (Các chương trình x86 16 bit thực tế thường không cần truy cập vào mã của họ dưới dạng dữ liệu, do đó, có thể sử dụng phân đoạn mã 64k ở đâu đó và có thể là một khối 64k khác với DS = SS, với ngăn xếp tăng dần từ độ lệch cao và dữ liệu tại phía dưới. Hoặc một mô hình mã nhỏ với tất cả các cơ sở phân khúc bằng nhau).


Cách phân đoạn x86 tương tác với phân trang

Ánh xạ địa chỉ ở chế độ 32/64-bit là:

  1. phân đoạn: offset (cơ sở phân đoạn ngụ ý bởi thanh ghi giữ phần bù hoặc được ghi đè bằng tiền tố lệnh)
  2. Địa chỉ ảo tuyến tính 32 hoặc 64 bit = cơ sở + bù. (Trong mô hình bộ nhớ phẳng như Linux sử dụng, con trỏ / offset = địa chỉ tuyến tính cũng vậy. Ngoại trừ khi truy cập TLS liên quan đến FS hoặc GS.)
  3. bảng trang (được lưu trữ bởi TLB) ánh xạ tuyến tính thành 32 (chế độ kế thừa), 36 (PAE kế thừa) hoặc địa chỉ vật lý 52 bit (x86-64). ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the ).

    Bước này là tùy chọn: phân trang phải được bật trong khi khởi động bằng cách đặt một chút trong thanh ghi điều khiển. Không có phân trang, địa chỉ tuyến tính là địa chỉ vật lý.

Lưu ý rằng phân đoạn không cho phép bạn sử dụng hơn 32 hoặc 64 bit không gian địa chỉ ảo trong một quy trình (hoặc luồng) duy nhất , bởi vì không gian địa chỉ phẳng (tuyến tính), mọi thứ được ánh xạ vào chỉ có cùng số bit như độ lệch. (Đây không phải là trường hợp của x86 16 bit, trong đó phân đoạn thực sự hữu ích cho việc sử dụng hơn 64k bộ nhớ với hầu hết các thanh ghi và phần bù 16 bit.)


CPU lưu trữ các bộ mô tả phân đoạn được tải từ GDT (hoặc LDT), bao gồm cả cơ sở phân khúc. Khi bạn hủy đăng ký một con trỏ, tùy thuộc vào đăng ký của nó, nó sẽ mặc định là DS hoặc SS là phân đoạn. Giá trị thanh ghi (con trỏ) được coi là phần bù từ cơ sở phân đoạn.

Vì cơ sở phân khúc thường bằng không, CPU thực hiện trường hợp đặc biệt này. Hoặc từ góc độ khác, nếu bạn làm có một phân khúc cơ sở khác không, tải có thêm độ trễ vì "đặc biệt" (bình thường) trường hợp bỏ qua việc thêm địa chỉ cơ sở không được áp dụng.


Cách Linux thiết lập các thanh ghi phân đoạn x86:

Cơ sở và giới hạn của CS / DS / ES / SS đều là 0 / -1 ở chế độ 32 và 64 bit. Đây được gọi là mô hình bộ nhớ phẳng vì tất cả các con trỏ trỏ vào cùng một không gian địa chỉ.

(AMD CPU kiến trúc sư thiến Phân khúc bởi thực thi một mô hình bộ nhớ phẳng cho chế độ 64-bit vì hệ điều hành chủ đạo đã không sử dụng nó dù sao, ngoại trừ không exec bảo vệ được cung cấp trong một cách tốt hơn bằng cách phân trang với PAE hoặc x86- Định dạng bảng 64 trang.)

  • TLS (Lưu trữ cục bộ luồng): FS và GS không được cố định tại cơ sở = 0 ở chế độ dài. (Chúng mới với 386 và không được sử dụng hoàn toàn bởi bất kỳ hướng dẫn nào, thậm chí không phải là rephướng dẫn chuỗi sử dụng ES). x86-64 Linux đặt địa chỉ cơ sở FS cho mỗi luồng thành địa chỉ của khối TLS.

    ví dụ: mov eax, [fs: 16]tải giá trị 32 bit từ 16 byte vào khối TLS cho luồng này.

  • bộ mô tả phân đoạn CS chọn chế độ CPU ở chế độ nào (chế độ được bảo vệ 16/32/64-bit / chế độ dài). Linux sử dụng một mục nhập GDT duy nhất cho tất cả các quy trình không gian người dùng 64 bit và một mục nhập GDT khác cho tất cả các quy trình không gian người dùng 32 bit. (Để CPU hoạt động bình thường, DS / ES cũng phải được đặt thành các mục hợp lệ và SS cũng vậy). Nó cũng chọn mức đặc quyền (kernel (ring 0) so với user (ring 3)), vì vậy ngay cả khi trở về không gian người dùng 64 bit, kernel vẫn phải sắp xếp để CS thay đổi, sử dụng irethoặc sysretthay vì bình thường hướng dẫn nhảy hoặc ret.

  • Trong x86-64, syscallđiểm vào sử dụng swapgsđể lật GS từ GS của không gian người dùng sang kernel, mà nó sử dụng để tìm ngăn xếp kernel cho luồng này. (Một trường hợp chuyên biệt của lưu trữ luồng cục bộ). Lệnh syscallkhông thay đổi con trỏ ngăn xếp để trỏ vào ngăn xếp kernel; nó vẫn trỏ đến ngăn xếp người dùng khi kernel đạt đến điểm vào 1 .

  • DS / ES / SS cũng phải được đặt thành các bộ mô tả phân đoạn hợp lệ để CPU hoạt động ở chế độ được bảo vệ / chế độ dài, mặc dù cơ sở / giới hạn từ các bộ mô tả đó bị bỏ qua ở chế độ dài.

Vì vậy, về cơ bản phân đoạn x86 được sử dụng cho TLS và cho các công cụ osdev x86 bắt buộc mà phần cứng yêu cầu bạn phải làm.


Chú thích 1: Lịch sử thú vị: có kho lưu trữ danh sách thư giữa các nhà phát triển kernel và kiến ​​trúc sư AMD từ một vài năm trước khi silicon AMD64 được phát hành, dẫn đến việc điều chỉnh thiết kế syscallđể có thể sử dụng được. Xem các liên kết trong câu trả lời này để biết chi tiế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.