2008-11-07 39 views
6

Tôi đang làm việc trên thiết kế hạt nhân và tôi có một số câu hỏi liên quan đến phân trang. Ý tưởng cơ bản mà tôi có cho đến nay là: Mỗi chương trình có bộ nhớ 4G (hoặc vì nó nghĩ) riêng của nó, trừ đi một phần ở đâu đó mà tôi dự trữ cho các chức năng hạt nhân mà chương trình có thể gọi. Vì vậy, hệ điều hành cần phải tìm ra một số cách để tải các trang trong bộ nhớ mà chương trình cần sử dụng trong quá trình hoạt động của nó. Bây giờ, giả sử rằng chúng tôi có số lượng bộ nhớ và thời gian xử lý vô hạn, tôi có thể tải/phân bổ bất kỳ trang nào mà chương trình đã viết hoặc đọc từ khi xảy ra lỗi trang cho các trang không tồn tại (hoặc được hoán đổi Vì vậy, hệ điều hành có thể nhanh chóng phân bổ chúng hoặc trao đổi chúng. Mặc dù vậy, trong thế giới thực, tôi cần tối ưu hóa quá trình này để chúng tôi không có một chương trình liên tục tiêu thụ tất cả bộ nhớ mà nó từng chạm vào.Hệ điều hành thường quản lý bộ nhớ hạt nhân và xử lý trang như thế nào?

Vì vậy, tôi đoán câu hỏi của tôi là, làm thế nào để một hệ điều hành thường đi về điều này? Suy nghĩ ban đầu của tôi là tạo ra một hàm mà chương trình gọi tới các trang thiết lập/miễn phí, sau đó nó có thể tự quản lý bộ nhớ, nhưng chương trình thường làm điều này, hay trình biên dịch cho rằng nó có triều đại tự do? Ngoài ra, trình biên dịch xử lý các tình huống mà nó cần phân bổ một phân đoạn bộ nhớ khá lớn như thế nào? Tôi có cần cung cấp chức năng cố gắng cung cấp cho nó các trang X theo thứ tự không?

Điều này rõ ràng không phải là một câu hỏi cụ thể về ngôn ngữ, nhưng tôi là một phần của tiêu chuẩn C và tốt với C++, vì vậy tôi muốn bất kỳ ví dụ mã nào ở trong đó hoặc trong assembly. (Hội đồng không cần thiết, tôi hoàn toàn có ý định làm cho nó hoạt động với càng nhiều mã C càng tốt, và tối ưu hóa như một bước cuối cùng.)

Một điều khác cũng cần phải trả lời dễ dàng hơn: xử lý các hàm hạt nhân mà một chương trình cần gọi? Có OK chỉ để có một khu vực bộ nhớ (tôi đã suy nghĩ về phía cuối của không gian ảo) có chứa hầu hết các chức năng cơ bản/quá trình bộ nhớ cụ thể mà chương trình có thể gọi? Suy nghĩ của tôi từ đó sẽ có chức năng hạt nhân làm điều gì đó rất lạ mắt và hoán đổi các trang (để các chương trình không thể thấy các hàm hạt nhân nhạy cảm trong không gian riêng của chúng) khi các chương trình cần làm bất cứ điều gì lớn, nhưng tôi không thực sự tập trung vào an ninh tại thời điểm này.

Vì vậy, tôi đoán tôi lo lắng hơn về ý tưởng thiết kế chung so với các chi tiết cụ thể. Tôi muốn làm cho hạt nhân hoàn toàn tương thích với GCC (bằng cách nào đó) và tôi cần phải đảm bảo rằng nó cung cấp mọi thứ mà một chương trình bình thường sẽ cần.

Cảm ơn lời khuyên nào.

Trả lời

12

Điểm khởi đầu tốt cho tất cả các câu hỏi này là xem xét cách Unix thực hiện. Như một câu nói nổi tiếng nói, "Những người không hiểu UNIX sẽ phải chịu trách nhiệm để tái tạo lại nó, kém cỏi."

Đầu tiên, về gọi hàm hạt nhân. Nó không đủ để chỉ đơn giản có các chức năng ở đâu đó mà chương trình có thể gọi, vì chương trình có thể chạy trong "chế độ người dùng" (vòng 3 trên IA-32) và hạt nhân phải chạy trong "chế độ hạt nhân" (thường là vòng 0 trên IA-32) để thực hiện các hoạt động riêng của nó. Bạn phải bằng cách nào đó làm việc chuyển đổi giữa hai chế độ, và điều này là rất cụ thể về kiến ​​trúc.

Trên IA-32, cách truyền thống là sử dụng cổng trong IDT cùng với phần mềm gián đoạn (Linux sử dụng int 0x80). Các bộ vi xử lý mới hơn có các cách khác (nhanh hơn) để thực hiện nó, và những cái nào có sẵn tùy thuộc vào việc CPU là từ AMD hay Intel, và trên mô hình CPU cụ thể. Để chứa biến thể này, các hạt nhân Linux gần đây sử dụng một trang mã được ánh xạ bởi hạt nhân ở đầu không gian địa chỉ cho mọi tiến trình. Vì vậy, trên Linux gần đây, để làm một hệ thống gọi bạn gọi một hàm trên trang này, nó sẽ làm bất cứ điều gì cần thiết để chuyển sang chế độ hạt nhân (hạt nhân có nhiều hơn một bản sao của trang đó, và chọn bản sao nào để sử dụng khi khởi động tùy thuộc vào các tính năng của bộ xử lý).

Bây giờ, việc quản lý bộ nhớ. Đây là chủ đề lớn; bạn có thể viết một cuốn sách lớn về nó và không exaust chủ đề. Hãy nhớ rằng có ít nhất hai chế độ xem của bộ nhớ: chế độ xem thực (thứ tự thực của các trang, hiển thị với hệ thống con bộ nhớ phần cứng và thường đến thiết bị ngoại vi bên ngoài) và chế độ xem lôgic (thứ tự các trang được xem bởi các chương trình đang chạy trên CPU). Nó khá dễ nhầm lẫn cả hai. Bạn sẽ được phân bổ trang vật lý và chỉ định các địa chỉ hợp lý vào không gian địa chỉ chương trình hoặc hạt nhân. Một trang vật lý duy nhất có thể có một số địa chỉ logic và có thể được ánh xạ tới các địa chỉ logic khác nhau trong các quy trình khác nhau.

Bộ nhớ hạt nhân (dành riêng cho hạt nhân) thường được ánh xạ ở đầu không gian địa chỉ của mọi quá trình. Tuy nhiên, nó được thiết lập để nó chỉ có thể được acessed trên chế độ hạt nhân.Không cần các thủ thuật lạ mắt để che giấu phần bộ nhớ đó; phần cứng thực hiện tất cả công việc chặn truy cập (trên IA-32, nó được thực hiện thông qua cờ trang hoặc giới hạn phân đoạn).

Các chương trình cấp phát bộ nhớ trên phần còn lại của không gian địa chỉ bằng nhiều cách:

  • Một phần của bộ nhớ được cấp bởi bộ nạp chương trình của hạt nhân. Điều này bao gồm mã chương trình (hoặc "văn bản"), chương trình được khởi tạo dữ liệu ("dữ liệu"), dữ liệu chưa được khởi tạo của chương trình ("bss", không điền), ngăn xếp và một số tỷ lệ và kết thúc. Bao nhiêu để phân bổ, ở đâu, những gì nên được các nội dung ban đầu, mà cờ bảo vệ để sử dụng, và một số thứ khác, được đọc từ các tiêu đề trên tập tin thực thi được nạp.
  • Theo truyền thống trên Unix, có một vùng bộ nhớ có thể phát triển và thu hẹp (giới hạn trên của nó có thể được thay đổi thông qua cuộc gọi hệ thống brk()). Đây là truyền thống được sử dụng bởi heap (bộ cấp phát bộ nhớ trên thư viện C, trong đó malloc() là một trong các giao diện, chịu trách nhiệm về heap).
  • Bạn thường có thể yêu cầu hạt nhân ánh xạ tệp tới vùng không gian địa chỉ. Đọc và viết cho khu vực đó là (thông qua phân trang ma thuật) hướng đến tập tin sao lưu. Điều này thường được gọi là mmap(). Với một số mmap ẩn danh, bạn có thể phân bổ các vùng mới của không gian địa chỉ không được bất kỳ tệp nào hỗ trợ, nhưng nếu không thì hành động theo cùng một cách. Trình tải chương trình của hạt nhân thường sẽ sử dụng mmap để phân bổ các phần của mã chương trình (ví dụ, mã chương trình có thể được bản thân thực thi).

Các khu vực không gian địa chỉ không được phân bổ theo bất kỳ cách nào (hoặc được dành riêng cho hạt nhân) được coi là lỗi và trên Unix sẽ gửi tín hiệu đến chương trình.

Trình biên dịch phân bổ bộ nhớ tĩnh (bằng cách chỉ định nó trên các tiêu đề tệp thực thi; trình tải chương trình của hạt nhân sẽ cấp phát bộ nhớ khi tải chương trình) hoặc động (bằng cách gọi hàm trên thư viện chuẩn của ngôn ngữ, một hàm trong thư viện chuẩn ngôn ngữ C, sau đó gọi hạt nhân để cấp phát bộ nhớ và chia nhỏ nó nếu cần). Cách tốt nhất để tìm hiểu những điều cơ bản của tất cả điều này là đọc một trong nhiều cuốn sách về hệ điều hành, đặc biệt là những cuốn sách sử dụng biến thể Unix làm ví dụ. Nó sẽ đi theo cách chi tiết hơn tôi có thể trả lời trên StackOverflow.

+0

Ồ ... về cơ bản, ngôn ngữ C giả định rằng "heap" của nó là một không gian địa chỉ dài và sẽ tự quản lý bộ nhớ đó và tất cả hạt nhân phải làm là thay đổi bộ nhớ lớn như thế nào? Điều đó nghe có vẻ khá đơn giản, và dễ dàng đo lường bộ nhớ được tiêu thụ bởi một chương trình. –

+1

Không phải ngôn ngữ C, mà là việc triển khai truyền thống thư viện chuẩn của nó. Tiêu chuẩn C cũng cho phép triển khai phức tạp hơn; cách thư viện chuẩn quản lý bộ nhớ của nó chủ yếu là một chi tiết thực hiện, theo như tiêu chuẩn C có liên quan. – CesarB

6

Câu trả lời cho câu hỏi này phụ thuộc nhiều vào kiến ​​trúc. Tôi sẽ giả sử bạn đang nói về x86. Với x86, hạt nhân thường cung cấp một bộ các cuộc gọi hệ thống , là các điểm vào được xác định trước vào hạt nhân. Mã người dùng chỉ có thể nhập hạt nhân tại những điểm cụ thể đó, vì vậy hạt nhân đã kiểm soát cẩn thận cách nó tương tác với mã người dùng.

Trong x86, có hai cách để thực hiện cuộc gọi hệ thống: với ngắt và với hướng dẫn sysenter/sysexit. Với ngắt, hạt nhân thiết lập bảng mô tả ngắt gián đoạn (IDT), xác định các điểm nhập có thể vào hạt nhân. Mã người dùng sau đó có thể sử dụng lệnh int để tạo ra một ngắt mềm để gọi vào hạt nhân. Ngắt cũng có thể được tạo ra bởi phần cứng (cái gọi là ngắt cứng); những ngắt này thường phải khác biệt với các ngắt mềm, nhưng chúng không nhất thiết phải như vậy.

Hướng dẫn sysenter và sysexit là cách thực hiện cuộc gọi hệ thống nhanh hơn, vì việc xử lý ngắt là chậm; Tôi không quen với việc sử dụng chúng, vì vậy tôi không thể bình luận về việc họ có phải là lựa chọn tốt hơn cho tình huống của bạn hay không.

Cho dù bạn sử dụng, bạn sẽ phải xác định giao diện cuộc gọi hệ thống. Có thể bạn sẽ muốn truyền tham số cuộc gọi hệ thống trong thanh ghi và không phải trên ngăn xếp, vì việc tạo ra ngắt sẽ khiến bạn chuyển đổi ngăn xếp thành ngăn xếp hạt nhân. Điều này có nghĩa là bạn gần như chắc chắn phải viết một số ngôn ngữ assembly trên cả hai chế độ người dùng kết thúc để thực hiện cuộc gọi hệ thống, và một lần nữa trên đầu hạt nhân để thu thập các đối số cuộc gọi hệ thống và lưu các thanh ghi.

Khi bạn đã có tất cả những điều đó tại chỗ, bạn có thể bắt đầu nghĩ về việc xử lý lỗi trang. Lỗi trang có hiệu quả chỉ là một loại gián đoạn khác - khi mã người dùng cố truy cập địa chỉ ảo mà không có mục nhập bảng trang, nó sẽ tạo gián đoạn 14 và bạn cũng sẽ nhận được địa chỉ bị lỗi làm mã lỗi. Hạt nhân có thể lấy thông tin này và sau đó quyết định đọc trong trang bị thiếu từ đĩa, thêm ánh xạ bảng trang và nhảy lại vào mã người dùng.

Tôi khuyên bạn nên xem một số tài liệu từ lớp MIT Operating Systems. Kiểm tra phần tham khảo, nó có rất nhiều thứ tốt.

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