Làm thế nào để hệ điều hành có thể chạy được mà không có hệ điều hành để chạy?


167

Tôi thực sự tò mò ngay bây giờ. Tôi là một lập trình viên Python và câu hỏi này chỉ khiến tôi băn khoăn: Bạn viết một HĐH. Làm thế nào để bạn chạy nó? Nó phải được chạy bằng cách nào đó, và cách đó nằm trong một hệ điều hành khác?

Làm thế nào một ứng dụng có thể chạy mà không cần trong hệ điều hành? Làm thế nào để bạn bảo máy tính chạy, nói, C và thực thi các lệnh này với màn hình, nếu nó không có HĐH để chạy?

Nó có phải làm với kernel UNIX không? Nếu vậy, hạt nhân Unix hay hạt nhân nói chung là gì?

Tôi chắc chắn rằng các hệ điều hành phức tạp hơn thế, nhưng nó hoạt động như thế nào?


14
Tôi khá chắc chắn đó là những gì BIOS dành cho - đó là một hệ điều hành thực sự nhỏ có khả năng khởi động hệ điều hành lớn hơn.
Sevenseacat

64
Một hệ điều hành thuận tiện , nhưng bạn không cần một hệ điều hành để chạy các chương trình trên máy tính.
Andres F.

10
Thậm chí hoàn toàn có thể viết phần mềm không phải HĐH mà không cần HĐH. Nhiều phiên dịch viên Forth theo truyền thống chạy mà không có HĐH (hoặc bạn có thể nói họ là HĐH). Nó thậm chí không khó. Nếu bạn biết C, bạn có thể thích viết một chương trình như vậy (một trò chơi nhỏ, có lẽ) như một bài tập học tập.
Tối đa

44
Sự nhầm lẫn này là một trong những chi phí của các hệ thống máy tính tuyệt vời, an toàn, trừu tượng cao mà chúng ta sử dụng ngày nay: mọi người có thể là những lập trình viên rất giỏi và có năng lực và thậm chí không biết các nguyên tắc cơ bản về cách thức hoạt động của máy tính. Bạn muốn đi thấp đến mức nào? Đối với rất thấp, nhưng vẫn ở trên vật lý, xem các bộ vi xử lý đầu tiên được lập trình như thế nào? trên Điện tử.SE.
dmckee

2
Lập trình đã được thực hiện trước khi phát minh ra khái niệm hệ điều hành hiện tại. Rõ ràng một cái gì đó ở cấp độ đó là những gì khởi động hệ điều hành. Hệ điều hành được bootstrapping. Điều này thường ít nhất được đề cập trong chương trình 4 năm của CS tại một số điểm vì hầu hết yêu cầu một lý thuyết máy tính về khóa học hệ điều hành.
Giàn

Câu trả lời:


263

Có rất nhiều trang web trải qua quá trình khởi động (chẳng hạn như Cách máy tính khởi động ). Tóm lại, một quy trình gồm nhiều giai đoạn giúp xây dựng hệ thống từng chút một cho đến khi cuối cùng nó có thể bắt đầu các quy trình HĐH.

Nó bắt đầu với phần sụn trên bo mạch chủ cố gắng khởi động CPU. Sau đó, nó tải lên BIOS giống như một hệ điều hành mini có thể chạy và chạy phần cứng khác. Khi đã xong, nó tìm kiếm một thiết bị khởi động (đĩa, CD, v.v.) và sau khi tìm thấy, nó định vị MBR (bản ghi khởi động chính) và tải nó vào bộ nhớ và thực thi nó. Đó là đoạn mã nhỏ này sau đó biết cách khởi tạo và khởi động hệ điều hành (hoặc các bộ tải khởi động khác vì mọi thứ trở nên phức tạp hơn). Tại thời điểm này, những thứ như kernel sẽ được tải và bắt đầu chạy.

Thật đáng kinh ngạc khi nó hoạt động cả!


108
+1 cho câu cuối cùng.
một CVn

39
Có một lý do nó được gọi là "khởi động"; thuật ngữ này là viết tắt của "bootstrapping", như trong, "tự kéo mình lên bằng bootstraps".
KeithS

5
Đã có lúc ai đó phải nhập hoặc chuyển đổi trong mã bootstrap. Đôi khi nó là một bước nhảy đơn giản đến hướng dẫn đầu tiên của chương trình trong ROM. Lần khác, nó là mã để đọc từ một thiết bị và chuyển đến hướng dẫn chương trình đầu tiên trong dữ liệu được đọc. Mọi thứ bây giờ đơn giản hơn nhiều.
BillThor


15
@BillThor: Tất nhiên, nơi mà đơn giản hơn rất nhiều, bạn có nghĩa là bạn rất phức tạp. Chúng chỉ đơn giản hơn để sử dụng .
Raphael Schweikert

173

Một hệ điều hành "kim loại trần" không chạy trong bất cứ thứ gì. Nó chạy bộ hướng dẫn đầy đủ trên máy vật lý và có quyền truy cập vào tất cả bộ nhớ vật lý, tất cả các thanh ghi thiết bị và tất cả các hướng dẫn đặc quyền, bao gồm cả các lệnh điều khiển phần cứng hỗ trợ bộ nhớ ảo.

(Nếu hệ điều hành đang chạy trên một máy ảo, nó có thể nghĩ . Nó đang ở trong tình trạng tương tự như trên Sự khác biệt là điều nhất định được mô phỏng hoặc bằng cách khác xử lý bởi các hypervisor; tức là mức độ mà chạy các máy ảo .)

Dù sao, trong khi HĐH có thể được triển khai trong (ví dụ) C, thì nó sẽ không có tất cả các thư viện C bình thường có sẵn cho nó. Đặc biệt, nó sẽ không có các thư viện 'stdio' bình thường. Thay vào đó, nó sẽ thực hiện (ví dụ) trình điều khiển thiết bị đĩa cho phép nó đọc và ghi các khối đĩa. Nó sẽ triển khai một hệ thống tệp ở trên cùng của lớp khối đĩa và trên hết nó sẽ thực hiện các cuộc gọi hệ thống mà các thư viện thời gian chạy của ứng dụng người dùng thực hiện để (ví dụ) tạo, đọc và ghi tệp ... vv.

Làm thế nào một ứng dụng có thể chạy mà không cần trong hệ điều hành?

Nó cần phải là một loại ứng dụng đặc biệt (ví dụ: hệ điều hành) biết cách tương tác trực tiếp với phần cứng I / O, v.v.

Làm thế nào để bạn bảo máy tính chạy, nói, C và thực thi các lệnh này với màn hình, nếu nó không có HĐH để chạy?

Bạn không.

Ứng dụng (vì mục đích của đối số được viết bằng C) được biên dịch và liên kết trên một số máy khác để đưa ra một hình ảnh mã gốc. Sau đó, hình ảnh được ghi vào ổ cứng ở nơi mà BIOS có thể tìm thấy nó. BIOS tải hình ảnh vào bộ nhớ và thực hiện một lệnh để chuyển đến điểm vào của ứng dụng.

Ở đó (thường) không có bất kỳ "chạy C và thực thi lệnh" nào trong ứng dụng trừ khi nó là một hệ điều hành toàn diện. Và trong trường hợp đó, trách nhiệm của hệ điều hành là phải thực hiện tất cả các cơ sở hạ tầng cần thiết để thực hiện. Không có phép thuật. Chỉ cần rất nhiều mã.

Câu trả lời của Bill bao gồm bootstrapping , đó là quá trình bạn đi từ một máy bị tắt nguồn sang một máy mà hệ điều hành bình thường đang hoạt động. Tuy nhiên, điều đáng chú ý là khi BIOS hoàn thành các nhiệm vụ của mình, nó (thường) trao quyền kiểm soát hoàn toàn phần cứng cho hệ điều hành chính và không đóng vai trò nào nữa - cho đến khi hệ thống tiếp theo khởi động lại. HĐH chính chắc chắn không chạy "bên trong" BIOS theo nghĩa thông thường.

Nó có phải làm với kernel UNIX không? Nếu vậy, một hạt nhân unix, hoặc một hạt nhân nói chung là gì?

Có nó làm.

Nhân UNIX là cốt lõi của hệ điều hành UNIX. Đây là một phần của UNIX thực hiện tất cả các công cụ "kim loại trần" được mô tả ở trên.

Ý tưởng của "hạt nhân" là bạn cố gắng tách phần mềm hệ thống thành các phần lõi (yêu cầu truy cập thiết bị vật lý, tất cả bộ nhớ, v.v.) và các phần mềm không cốt lõi. Nhân bao gồm các công cụ cốt lõi.

Trong thực tế, sự phân biệt giữa kernel / core và non-kernel / non-core phức tạp hơn thế. Và đã có rất nhiều tranh luận về những gì thực sự thuộc về kernel, và những gì không. (Tra cứu vi nhân chẳng hạn.)


6
Câu trả lời phi thường. Tôi sẽ cung cấp cho bạn thêm một số upvote nếu có thể.
weberc2

7
Có rất nhiều câu trả lời hay ở đây, vì vậy tôi sẽ thêm nhận xét này dưới dạng nhận xét, vì cho đến nay chưa có ai đề cập đến điều này: Một trong những tính năng chính trong HĐH là cho phép nhiều ứng dụng thực hiện "đồng thời" từ quan điểm của người dùng. Khả năng lên lịch các quy trình và bảo vệ các quy trình khỏi thay đổi hành vi của nhau, thường là các tính năng chỉ có trong HĐH chứ không phải phần sụn hoặc BIOS.
Sean Barbeau

2
The idea of a "kernel" is that you try to separate the system software into core stuffDễ nhớ bằng cách lưu ý rằng thuật ngữ kernelnày là từ tiếng Đức Kern, có nghĩa là cốt lõi / hạt nhân.
deed02392

1
Thích câu trả lời này ở đây vì nó đề cập rằng nó được biên dịch và liên kết mã nhị phân không chạy C.
Travis Pessetto

3
"Tránh xa điều đó khiến người dùng PC kém thông minh hơn." - Không kém thông minh ... ít biết chữ. Bạn cũng có thể nói rằng nó đã tăng số lượng người dùng PC.
Stephen C

62

Ban đầu, không có nguồn điện trong CPU.

Và người đàn ông nói "hãy có sức mạnh" và CPU bắt đầu đọc từ một địa chỉ nhất định trong bộ nhớ và thực hiện lệnh được trình bày ở đó. Sau đó, tiếp theo và như vậy cho đến khi kết thúc sức mạnh.

Đây là khởi động lên. Nhiệm vụ của nó là tải một phần mềm khác để có quyền truy cập vào môi trường, nơi có phần mềm chính và tải nó.

Cuối cùng, một màn hình thân thiện mời bạn đăng nhập.


58
Câu trả lời này nên được chuyển đến christianity.stackexchange.com
Coomie

6
"một địa chỉ nhất định" nó đến từ đâu Xin lỗi vì đã chơi Charles Darwin ở đây.
Giữa

7
@Unhat - Địa chỉ đầu tiên được CPU tìm nạp được gắn vào bên trong nó. Thông thường là 0.
mouviciel

17
... Và vào ngày thứ 7, Người đàn ông nghỉ ngơi với các trò chơi của mình
Luke Luke

9
@mouviciel Các địa chỉ trong bộ nhớ là 0x7C00cho bất kỳ x86kiến trúc tương thích và đầu tiên phải được lấp đầy bởi BIOS mà thường tải các sector đầu tiên của bất cứ điều gì có thể khởi động thiết bị nó thích ... Nice câu trả lời mặc dù: -7
Tobias KIENZLER

29

Xin lỗi vì đến trễ, nhưng tôi sẽ mô tả nó như vậy:

  • Các bo mạch chủ được cấp điện.

  • Mạch thời gian bắt đầu và ổn định nếu cần thiết, chỉ dựa trên các đặc tính điện của chúng. Một số thiết bị mới hơn thực sự có thể sử dụng bộ vi xử lý hoặc trình sắp xếp rất hạn chế.

    Cần lưu ý, rất nhiều [sic] của những thứ như "mạch thời gian bắt đầu và ổn định nếu cần thiết" không thực sự xảy ra trong phần cứng nữa. Một lượng lớn công việc đó thực sự là phần mềm cực kỳ chuyên dụng chạy trên các bộ xử lý / trình tự tiếp theo rất hạn chế.

    - jkerian lúc 5:20 ngày 25 tháng 10

  • Nguồn được cấp cho CPU và RAM.

  • CPU tải dữ liệu (dựa trên hệ thống dây bên trong của nó) từ BIOS. Trên một số máy, BIOS có thể được nhân đôi vào RAM và sau đó được thực thi từ đó nhưng đó là IIRC hiếm.

    Khi được bật, CPU tương thích x86 bắt đầu tại địa chỉ 0xFFFFFFF0 trong không gian địa chỉ ...

    -Micheal Steil, 17 sai lầm của Microsoft trong Hệ thống bảo mật Xbox ( lưu trữ )

  • BIOS thực hiện các cuộc gọi đến các cổng và địa chỉ phần cứng được sử dụng bởi bo mạch chủ cho đĩa và IO phần cứng khác và quay đĩa, làm cho phần còn lại của RAM hoạt động, trong số những thứ khác.

  • Mã BIOS (bằng cách cài đặt CMOS, được lưu trữ trong phần cứng) sử dụng các lệnh IDE hoặc SATA mức thấp để đọc khu vực khởi động của mỗi đĩa, theo thứ tự được chỉ định bởi CMOS hoặc ghi đè người dùng bằng menu.

  • Đĩa đầu tiên với một khu vực khởi động được thực hiện khu vực khởi động của nó. Khu vực khởi động này là hội có các hướng dẫn để tải thêm dữ liệu từ đĩa, tải các NTLDRgiai đoạn sau lớn hơn GRUB, v.v.

  • Cuối cùng, mã máy hệ điều hành được bộ tải khởi động thực thi, trực tiếp hoặc gián tiếp thông qua việc tải chuỗi tải một khu vực khởi động từ một vị trí thay thế hoặc bù.

Sau đó, bạn nhận được một hoảng loạn hạt nhân thân thiện, một con chim cánh cụt nghẹt thở hoặc đĩa của bạn bị đình trệ do một vụ tai nạn đầu. =) Trong kịch bản thay thế, hạt nhân của bạn thiết lập các bảng quy trình, cấu trúc trong bộ nhớ và gắn đĩa, tải trình điều khiển, mô-đun và GUI hoặc bộ dịch vụ (nếu trên máy chủ). Sau đó, các chương trình được thực thi khi các tiêu đề của chúng được đọc và tập hợp của chúng được đưa vào bộ nhớ và ánh xạ tương ứng.


2
Cần lưu ý, rất nhiều thứ như "mạch thời gian bắt đầu và ổn định nếu cần" không thực sự xảy ra trong phần cứng nữa. Một lượng lớn công việc đó thực sự là phần mềm cực kỳ chuyên dụng chạy trên các bộ xử lý / trình tự tiếp theo rất hạn chế. - Một kỹ sư phần mềm thân thiện với khu phố
jkerian

@jkerian Bạn có phiền rằng tôi đã trích dẫn nhận xét của bạn vào bài viết của mình không?
ζ--

heh, hoàn toàn không
jkerian

BIOS không phải là một hệ điều hành. BIOS là viết tắt của Hệ thống đầu vào / đầu ra cơ bản, và đó những gì BIOS làm. Nó cho phép các lập trình viên sử dụng các tài nguyên cấp thấp với trình điều khiển do nhà sản xuất cung cấp. Khi HĐH chuyển sang chế độ Được bảo vệ (32 bit) hoặc Dài (64 bit), BIOS không còn khả dụng và HĐH sử dụng trình điều khiển riêng của nó về cơ bản thay thế chức năng mà BIOS cung cấp ở mức "thấp hơn". Các hệ điều hành hiện đại, ví dụ Linux và Windows, chỉ sử dụng BIOS để phát hiện các phần RAM có thể sử dụng và tải trình tải nâng cao hơn của riêng chúng có thể tải trình điều khiển cần thiết.
Hannes Karppila

1
@HannesKarppila Cập nhật; bây giờ đã khoảng bốn tuổi và tôi không còn hoạt động trên trang này nữa.
ζ--

15

Có nhiều câu trả lời hay nhưng tôi muốn thêm điều này: Bạn đã đề cập bạn đến từ nền Python. Python là ngôn ngữ được chia sẻ (hoặc "xen kẽ" hoặc bất cứ điều gì, ít nhất là trong các trường hợp sử dụng CPython điển hình). Điều này có nghĩa là bạn có một số phần mềm khác (trình thông dịch Python) đang xem nguồn và thực thi nó theo một cách nào đó. Đây là một mô hình tốt và cho phép các ngôn ngữ cấp cao khá đẹp được trừu tượng hóa từ phần cứng thực tế. Nhược điểm là bạn luôn cần phần mềm phiên dịch này đầu tiên.

Phần mềm thông dịch như vậy, thông thường, được viết bằng ngôn ngữ biên dịch thành mã máy, ví dụ C hoặc C ++. Mã máy là những gì CPU có thể xử lý. Những gì CPU có thể làm là đọc một số byte từ bộ nhớ và tùy thuộc vào các giá trị byte bắt đầu một hoạt động cụ thể. Vì vậy, một chuỗi byte là một lệnh để tải một số dữ liệu từ bộ nhớ vào một thanh ghi, một chuỗi khác để thêm hai giá trị, một chuỗi khác để lưu giá trị từ một thanh ghi trở lại bộ nhớ chính và ngay sau đó (một thanh ghi là vùng nhớ đặc biệt là một phần của cpu nơi nó có thể hoạt động tốt nhất), hầu hết các lệnh này khá thấp ở mức đó. Con người có thể đọc được các hướng dẫn mã máy này là mã trình biên dịch mã. Mã máy này, về cơ bản, là những gì được lưu trữ trong các tệp .exe hoặc.com trên windows hoặc bên trong các tệp nhị phân Linux / Unix.

Bây giờ nếu một máy tính được khởi động thì nó bị câm, nó có một số hệ thống dây mặc dù nó sẽ đọc các hướng dẫn mã máy như vậy. Trên PC, thông thường (hiện tại) là chip EEPROM trên bo mạch chính chứa BIOS (hệ thống ouptput cơ bản), hệ thống này không thể làm được gì nhiều, nó có thể dễ dàng truy cập vào một số phần cứng, v.v. khởi động và sao chép một vài byte đầu tiên (còn gọi là bản ghi khởi động chính, MBR) vào bộ nhớ và sau đó báo cho CPU "ở đây, có chương trình của bạn", CPU sẽ xử lý các byte đó ở đó dưới dạng mã máy và thực thi nó. Thông thường, đây là một số trình tải hệ điều hành sẽ tải kernel với một số tham số và sau đó bàn giao quyền điều khiển cho kernel đó, sau đó sẽ tải tất cả quyền truy cập vào phần cứng, tải một số chương trình máy tính để bàn hoặc shell hoặc bất cứ điều gì và cho phép người dùng đăng nhập và sử dụng hệ thống.


6
"xen kẽ"? Tôi chưa bao giờ nghe thuật ngữ đó trước đây.
Bryan Oakley

3
Thuật ngữ đó đã được sử dụng rất nhiều cách đây khoảng 5 năm, để mô tả các trình thông dịch "hiện đại" có giai đoạn biên dịch riêng biệt tách biệt với thực thi. Không biết liệu thuật ngữ này có tồn tại ở bất cứ đâu không ;-)
johannes

1
"ninterpret"? Tôi chưa bao giờ nghe thuật ngữ đó trước đây.
Cole Johnson

12

Bạn hỏi "Làm thế nào một ứng dụng có thể chạy mà không ở trong HĐH". Câu trả lời dễ dàng là "HĐH không phải là ứng dụng". Mặc dù một hệ điều hành có thể được tạo ra với cùng các công cụ như một ứng dụng và được tạo ra từ cùng một nguyên liệu thô, nhưng chúng không giống nhau. Một hệ điều hành không phải chơi theo cùng một quy tắc như một ứng dụng.

OTOH, bạn có thể nghĩ phần cứng và phần sụn thực tế là "HĐH" trong đó "ứng dụng" HĐH chạy. Phần cứng là một hệ điều hành rất đơn giản - nó biết cách chạy các lệnh được viết bằng mã máy và nó biết rằng khi khởi động, nó nên xem một địa chỉ bộ nhớ rất cụ thể cho hướng dẫn đầu tiên. Vì vậy, nó khởi động và sau đó ngay lập tức chạy lệnh đó đầu tiên, tiếp theo là thứ hai, v.v.

Vì vậy, HĐH chỉ đơn giản là mã máy tồn tại ở một vị trí đã biết và có thể tương tác trực tiếp với phần cứng.


1
+1 Tôi nghĩ rằng đây là câu trả lời tốt nhất. Về mặt trừu tượng, tôi nghĩ rằng bạn đang đóng đinh nó ở mức phù hợp.
Preet Sangha

6

Trả lời câu hỏi của bạn đòi hỏi kiến ​​thức về cách mã gốc (đối với CPU) trông như thế nào và nó được CPU diễn giải như thế nào.

Thông thường toàn bộ quá trình biên dịch dựa trên việc dịch những thứ bạn viết bằng C, Pascal hoặc thậm chí Python (sử dụng pypy) và C # thành những thứ mà CPU hiểu, nghĩa là các hướng dẫn đơn giản như "lưu trữ một cái gì đó dưới [địa chỉ bộ nhớ]", "thêm số được lưu trữ dưới thanh ghi và ebx "," chức năng gọi foo "," so sánh eax với 10 ". Các hướng dẫn đó, được thực hiện từng cái một, làm những việc bạn muốn làm với mã của mình.

Bây giờ hãy nghĩ về điều này: bạn không thực sự cần một hệ điều hành để thực thi mã gốc này! Tất cả những gì bạn cần là tải mã này vào bộ nhớ và báo cho CPU biết nó đang ở đó và bạn muốn nó được thực thi. Đừng lo lắng quá nhiều về điều đó, mặc dù. Đó là BIOS công việc nên lo lắng - nó tải mã của bạn (chỉ một và một cung), ngay sau khi CPU khởi động, dưới địa chỉ vật lý 0x7C00. Sau đó, CPU bắt đầu thực thi một sector (512 B) mã của bạn. Và bạn có thể làm bất cứ điều gì bạn tưởng tượng! Tất nhiên, không có bất kỳ sự hỗ trợ nào từ HĐH. Đó là bởi vì BẠN là hệ điều hành. Thật tuyệt phải không? Không có thư viện tiêu chuẩn, không tăng tốc, không trăn, không chương trình, không trình điều khiển! Bạn phải tự viết mọi thứ.

Và làm thế nào để bạn giao tiếp với phần cứng? Vâng, bạn có hai lựa chọn:

  1. Bạn ở trong "Chế độ thực" - Chế độ thực thi CPU chỉ với 1 MB bộ nhớ (và thậm chí ít hơn), không có các tính năng CPU nâng cao như mở rộng CPU, bảo vệ bộ nhớ, đa nhiệm; Mã thực thi 16 bit, chế độ địa chỉ cổ ... Nhưng với một số thói quen được cung cấp bởi BIOS, bao gồm đầu ra màn hình đơn giản, hỗ trợ bàn phím, I / O đĩa và quản lý nguồn. Nói một cách dễ hiểu, bạn sẽ quay lại thời gian của MS-DOS và CPU 16 bit.
  2. Bạn vào "Chế độ bảo vệ" với tất cả các tính năng mà CPU của bạn có, tất cả bộ nhớ bạn đã cài đặt, v.v. Nhưng trong Chế độ được bảo vệ, bạn hoàn toàn đơn độc và bạn phải tự mình thực hiện MỌI THỨ (và bạn giao tiếp với phần cứng bằng cách sử dụng các hướng dẫn "vào" và "ra" để nhập / xuất dữ liệu sang cổng I / O và sử dụng ngắt. / O). Tôi có phải nói mọi HĐH kể từ Windows 95 và Linux đầu tiên chọn tùy chọn này không?

Bây giờ bạn đang hỏi kernel là gì. Một thời gian ngắn, kernel là mọi thứ bạn không nhìn thấy và trải nghiệm trực tiếp. Nó quản lý, cùng với trình điều khiển, mọi thứ, bắt đầu từ bàn phím của bạn đến hầu hết mọi phần cứng bên trong PC. Bạn giao tiếp với nó bằng vỏ đồ họa hoặc thiết bị đầu cuối. Hoặc bằng các chức năng bên trong mã của bạn, giờ đây được thực thi, với sự hỗ trợ của HĐH.

Để hiểu rõ hơn tôi có thể cho bạn một lời khuyên: hãy thử viết hệ điều hành của riêng bạn. Ngay cả khi nó sẽ viết "Hello world" trên màn hình.


3

Có một số khác biệt về cách một hệ điều hành hoạt động phụ thuộc rất nhiều vào hệ thống. Để có ích, một hệ thống cần có một số hành vi có thể dự đoán được khi khởi động, chẳng hạn như "bắt đầu thực thi tại địa chỉ X". Đối với các hệ thống có bộ lưu trữ không bay hơi (như bộ nhớ Flash) được ánh xạ vào không gian chương trình của chúng, điều này khá dễ dàng vì bạn chỉ cần đảm bảo rằng bạn đặt mã khởi động ở đúng vị trí trong không gian chương trình của bộ xử lý. Điều này là cực kỳ phổ biến đối với vi điều khiển. Một số hệ thống phải truy xuất các chương trình khởi động của chúng từ một số vị trí khác trước khi thực hiện nó. Các hệ thống này sẽ có một số hoạt động được gắn kết (hoặc gần như cứng) vào chúng. Có một số bộ xử lý lấy mã khởi động của họ thông qua i2c từ một chip khác,

Các hệ thống sử dụng họ bộ xử lý x86 thường sử dụng quy trình khởi động nhiều giai đoạn khá phức tạp do sự phát triển và các vấn đề tương thích ngược của nó. Hệ thống thực thi một số phần sụn (được gọi là BIOS - Hệ thống đầu vào / đầu ra cơ bản, hoặc tương tự) có trong một số bộ nhớ không bay hơi trên bo mạch chủ. Đôi khi một số hoặc tất cả phần sụn này được sao chép (di dời) vào RAM để làm cho nó hoạt động nhanh hơn. Mã này được viết với kiến ​​thức về phần cứng nào sẽ có mặt và có thể sử dụng để khởi động.

Phần mềm khởi động thường được viết với các giả định về phần cứng nào sẽ có mặt trên hệ thống. Cách đây nhiều năm trên máy 286 có thể sẽ có một giả định rằng sẽ có bộ điều khiển ổ đĩa mềm tại địa chỉ I / O X và sẽ tải sector 0 đến một vị trí bộ nhớ nhất định nếu được cung cấp một bộ lệnh nhất định (và mã tại sector 0 biết cách sử dụng các chức năng riêng của BIOS để tải thêm mã và cuối cùng, đủ mã để được tải hệ điều hành). Trên một vi điều khiển, có thể có một giả định rằng có một cổng nối tiếp hoạt động với một số cài đặt nhất định cần đợi các lệnh (để cập nhật phần sụn phức tạp hơn) từ X trong khoảng thời gian trước khi tiếp tục quá trình khởi động.

Quá trình khởi động chính xác của một hệ thống nhất định không quan trọng đối với bạn vì biết rằng nó khác với các hệ thống khác nhau, nhưng tất cả chúng đều có những điểm chung. Thường trong mã khởi động (bootstrapping) khi I / O cần được thực hiện, các thiết bị I / O được thăm dò thay vì dựa vào các ngắt. Điều này là do các ngắt rất phức tạp, hãy sử dụng ngăn xếp RAM (có thể chưa được thiết lập đầy đủ) và bạn không cần phải lo lắng về việc chặn các hoạt động khác khi bạn là hoạt động duy nhất.

Khi lần đầu tiên được tải kernel hệ điều hành (kernel là phần chính của hầu hết các HĐH) ban đầu sẽ hoạt động rất giống với firmware. Nó sẽ cần được lập trình với kiến ​​thức hoặc khám phá phần cứng hiện tại, thiết lập một số RAM dưới dạng không gian ngăn xếp, thực hiện các thử nghiệm khác nhau, thiết lập các cấu trúc dữ liệu khác nhau, có thể khám phá và gắn kết một hệ thống tệp và sau đó có thể khởi động một số chương trình giống như các chương trình bạn đã quen viết (một chương trình dựa trên hệ điều hành có mặt).

Mã hệ điều hành thường được viết bằng hỗn hợp C và lắp ráp. Mã đầu tiên cho nhân hệ điều hành có lẽ luôn được lắp ráp và thực hiện những việc như thiết lập ngăn xếp, mã C dựa vào, sau đó gọi hàm C. Việc lắp ráp bằng tay khác cũng sẽ có trong đó bởi vì một số thao tác mà HĐH cần thực hiện thường không thể hiện được trong C (như chuyển đổi ngữ cảnh / ngăn xếp hoán đổi). Thông thường các cờ đặc biệt phải được chuyển đến trình biên dịch C để bảo nó không dựa vào các thư viện chuẩn mà hầu hết các chương trình C sử dụng và không mong đợi rằng có mộtint main(int argc, char *argv[])trong chương trình. Ngoài ra các tùy chọn liên kết đặc biệt mà hầu hết các lập trình viên ứng dụng không bao giờ sử dụng phải được sử dụng. Những điều này có thể làm cho chương trình kernel dự kiến ​​được tải tại một địa chỉ nhất định hoặc thiết lập mọi thứ trông giống như có các biến bên ngoài tại các vị trí nhất định mặc dù các biến đó không bao giờ được khai báo trong bất kỳ mã C nào (điều này hữu ích cho I / O được ánh xạ bộ nhớ vị trí bộ nhớ đặc biệt khác).

Toàn bộ hoạt động có vẻ giống như ma thuật lúc đầu, nhưng sau khi bạn nhìn vào nó và hiểu các phần của nó, ma thuật trở thành một tập hợp các chương trình đòi hỏi nhiều kiến ​​thức hệ thống và lập kế hoạch hơn để thực hiện. Gỡ lỗi chúng, mặc dù, có phép thuật.


3

Để hiểu cách các hệ điều hành hoạt động, có thể hữu ích khi chia chúng thành hai loại: những loại chỉ cung cấp dịch vụ cho các ứng dụng theo yêu cầu và những loại sử dụng các tính năng phần cứng trong CPU để ngăn các ứng dụng làm những việc chúng không nên làm. MS-DOS là kiểu cũ; tất cả các phiên bản Windows kể từ 3.0 đều là kiểu thứ hai (ít nhất là khi chạy bất cứ thứ gì mạnh hơn 8086).

Máy tính IBM ban đầu chạy PC-DOS hoặc MS-DOS sẽ là một ví dụ về kiểu "HĐH" trước đây. Nếu một ứng dụng muốn hiển thị một ký tự trên màn hình, sẽ có một vài cách để làm điều đó. Nó có thể gọi thủ tục sẽ yêu cầu MS-DOS gửi nó đến "đầu ra tiêu chuẩn". Nếu làm như vậy, MS-DOS sẽ kiểm tra xem đầu ra có được chuyển hướng hay không và nếu không, nó sẽ gọi một thường trình được lưu trong ROM (trong một tập hợp các thói quen mà IBM gọi là Hệ thống đầu vào / đầu ra cơ bản) sẽ hiển thị một ký tự tại vị trí con trỏ và di chuyển con trỏ ("ghi teletype"). Thói quen BIOS đó sau đó sẽ lưu trữ một cặp byte ở đâu đó trong phạm vi 0xB800: 0 đến 0xB800: 3999; phần cứng trên Bộ điều hợp đồ họa màu sẽ liên tục tìm nạp các cặp byte trong phạm vi đó, sử dụng byte đầu tiên của mỗi cặp để chọn hình dạng ký tự và thứ hai để chọn màu nền trước và màu nền. Các byte được tìm nạp và xử lý thành các tín hiệu màu đỏ, xanh lục và xanh lam, theo trình tự mang lại một màn hình văn bản rõ ràng.

Các chương trình trên PC IBM có thể hiển thị văn bản bằng cách sử dụng thường trình "đầu ra tiêu chuẩn" của DOS hoặc bằng cách sử dụng thói quen "ghi teletype" của BIOS hoặc bằng cách lưu trữ trực tiếp để hiển thị bộ nhớ. Nhiều chương trình cần hiển thị nhiều văn bản đã nhanh chóng chọn cách tiếp cận sau, vì nó có thể nhanh gấp hàng trăm lần so với việc sử dụng các thói quen của DOS. Điều này không phải là do các thói quen của DOS và BIOS đặc biệt kém hiệu quả; trừ khi màn hình hiển thị trống, nó chỉ có thể được viết vào một số thời điểm nhất định. Thường trình BIOS để xuất một ký tự được thiết kế để có thể gọi nó bất cứ lúc nào; do đó, mỗi yêu cầu phải bắt đầu trước khi chờ đợi thời điểm thích hợp để thực hiện thao tác ghi. Ngược lại, mã ứng dụng biết những gì nó cần làm có thể tự tổ chức xung quanh các cơ hội có sẵn để viết màn hình.

Một điểm quan trọng ở đây là trong khi DOS và BIOS cung cấp một phương tiện xuất văn bản ra màn hình, thì không có gì đặc biệt "kỳ diệu" về những khả năng như vậy. Một ứng dụng muốn ghi văn bản vào màn hình có thể hoạt động hiệu quả không kém, ít nhất là nếu phần cứng màn hình hoạt động theo cách mà ứng dụng mong đợi (nếu ai đó đã cài đặt Bộ điều hợp hiển thị đơn sắc, tương tự như CGA nhưng có bộ nhớ ký tự nằm ở 0xB000: 0000-0xB000: 3999), BIOS sẽ tự động xuất các ký tự ở đó; một ứng dụng được lập trình để hoạt động với MDA hoặc CGA cũng có thể làm như vậy, nhưng một ứng dụng được lập trình chỉ dành cho CGA sẽ hoàn toàn vô dụng trên MDA).

Trên các hệ thống mới hơn, mọi thứ có một chút khác biệt. Bộ xử lý có nhiều chế độ "đặc quyền". Họ bắt đầu ở chế độ đặc quyền nhất, nơi mã được phép làm bất cứ điều gì nó muốn. Sau đó, họ có thể chuyển sang chế độ hạn chế, trong đó chỉ có các phạm vi bộ nhớ hoặc phương tiện I / O được chọn. Mã không thể chuyển trực tiếp từ chế độ hạn chế trở lại chế độ đặc quyền, nhưng bộ xử lý đã xác định các điểm nhập chế độ đặc quyền và mã chế độ hạn chế có thể yêu cầu bộ xử lý bắt đầu chạy mã tại một trong những điểm nhập đó ở chế độ đặc quyền. Ngoài ra, có các điểm nhập chế độ đặc quyền được liên kết với một số hoạt động sẽ bị cấm trong chế độ hạn chế. Ví dụ, giả sử rằng ai đó muốn chạy nhiều ứng dụng MS-DOS, với mỗi ứng dụng có màn hình riêng. Nếu các ứng dụng có thể ghi trực tiếp vào bộ điều khiển hiển thị ở 0xB800: 0, sẽ không có cách nào để ngăn một ứng dụng ghi đè lên màn hình của ứng dụng khác. Mặt khác, một hệ điều hành có thể chạy ứng dụng ở chế độ hạn chế và bẫy mọi truy cập vào bộ nhớ hiển thị; nếu nó phát hiện ra rằng một ứng dụng được cho là ở "nền" đang cố ghi 0xB800: 160, thì nó có thể lưu trữ dữ liệu vào một số bộ nhớ mà nó được đặt làm bộ đệm màn hình ứng dụng nền. Nếu ứng dụng đó sau đó được chuyển sang nền trước, thì bộ đệm có thể được sao chép sang màn hình thực. một hệ điều hành có thể chạy ứng dụng ở chế độ hạn chế và bẫy mọi truy cập vào bộ nhớ hiển thị; nếu nó phát hiện ra rằng một ứng dụng được cho là ở "nền" đang cố ghi 0xB800: 160, thì nó có thể lưu trữ dữ liệu vào một số bộ nhớ mà nó được đặt làm bộ đệm màn hình ứng dụng nền. Nếu ứng dụng đó sau đó được chuyển sang nền trước, thì bộ đệm có thể được sao chép sang màn hình thực. một hệ điều hành có thể chạy ứng dụng ở chế độ hạn chế và bẫy mọi truy cập vào bộ nhớ hiển thị; nếu nó phát hiện ra rằng một ứng dụng được cho là ở "nền" đang cố ghi 0xB800: 160, thì nó có thể lưu trữ dữ liệu vào một số bộ nhớ mà nó được đặt làm bộ đệm màn hình ứng dụng nền. Nếu ứng dụng đó sau đó được chuyển sang nền trước, thì bộ đệm có thể được sao chép sang màn hình thực.

Những điều quan trọng cần lưu ý là (1) mặc dù thường có một bộ thói quen tiêu chuẩn để thực hiện các dịch vụ tiêu chuẩn khác nhau như hiển thị văn bản, họ không làm bất cứ điều gì mà một ứng dụng đang chạy trong "chế độ đặc quyền" không thể làm nếu nó được lập trình đúng để xử lý phần cứng đã được cài đặt; (2) mặc dù hầu hết các ứng dụng đang chạy ngày nay sẽ bị hệ điều hành của chúng ngăn không cho thực hiện I / O như vậy, một chương trình khởi động ở chế độ đặc quyền sẽ làm bất cứ điều gì nó muốn và có thể thiết lập bất kỳ quy tắc nào nó muốn cho chế độ hạn chế Chương trình.


2

Như Stephen C. đã nói, nó không chỉ là về việc khởi động Hệ điều hành, mà còn là cách nó chạy, tương tác với phần cứng và với phần mềm bên trên nó.

Tôi sẽ chỉ thêm vào câu trả lời của anh ấy, rằng bạn có thể muốn xem "Các yếu tố của hệ thống máy tính" . Đó là một cuốn sách và một số công cụ, giải thích cách máy tính, hệ điều hành và trình biên dịch tương tác. Điều độc đáo ở đây là nó cung cấp cho bạn các công cụ để phát triển rất nhanh hệ điều hành của riêng bạn trong một môi trường giả lập, bỏ qua nhiều chi tiết cần thiết cho một thực tế, để bạn có thể nắm bắt các khái niệm . Nó làm một công việc tuyệt vời là cho phép bạn nhìn thấy khu rừng thay vì những cái cây.

Nếu bạn muốn đi sâu vào chi tiết hơn về cách Hệ điều hành tương tác với phần cứng, thì hãy xem Minix .


1

Bạn viết một hệ điều hành. Nó phải được chạy bằng cách nào đó, và cách đó nằm trong một hệ điều hành khác?

Ứng dụng của bạn đang chạy trong một hệ điều hành. Hệ điều hành này cung cấp các dịch vụ cho ứng dụng của bạn, những việc như mở tệp và ghi byte vào nó. Các dịch vụ này thường được cung cấp thông qua các cuộc gọi hệ thống.

Hệ điều hành đang chạy trong phần cứng. Phần cứng cung cấp dịch vụ cho Hệ điều hành, những việc như đặt tốc độ truyền của cổng nối tiếp và ghi byte vào nó. Các dịch vụ này thường được cung cấp thông qua các thanh ghi ánh xạ bộ nhớ hoặc cổng I / O.


Để đưa ra một ví dụ rất đơn giản về cách thức hoạt động của nó:

Ứng dụng của bạn yêu cầu hệ điều hành ghi một cái gì đó vào một tệp. Đối với ứng dụng của bạn, hệ điều hành cung cấp các khái niệm như tệp và thư mục.

Trên phần cứng, những khái niệm này không tồn tại. Phần cứng cung cấp các khái niệm như các đĩa được chia thành các khối cố định 512 byte. Hệ điều hành quyết định khối nào sẽ sử dụng cho tệp của bạn và một số khối khác cho siêu dữ liệu như tên tệp, kích thước và vị trí trên đĩa. Sau đó, nó báo cho phần cứng: ghi 512 byte này vào sector có số này trên đĩa với số đó; ghi 512 byte khác vào cung có số khác nhau này trên đĩa có cùng số đó; vân vân

Cách hệ điều hành bảo phần cứng thực hiện thay đổi rất nhiều. Một trong những chức năng của một hệ điều hành là bảo vệ các ứng dụng khỏi những khác biệt này. Đối với ví dụ về đĩa, trên một loại phần cứng, hệ điều hành sẽ phải ghi đĩa và số khu vực vào cổng I / O, sau đó ghi từng byte một vào một cổng I / O riêng biệt. Trên một loại phần cứng khác, hệ điều hành sẽ phải sao chép toàn bộ 512 byte của một khu vực vào một vùng bộ nhớ, ghi vị trí của vùng bộ nhớ đó vào một vị trí bộ nhớ đặc biệt và ghi số đĩa và số khu vực vào một khu vực khác vị trí bộ nhớ đặc biệt.


Phần cứng cao cấp ngày nay là vô cùng phức tạp. Các hướng dẫn cung cấp cho tất cả các chi tiết lập trình của họ là bộ chặn cửa với hàng ngàn trang; chẳng hạn, hướng dẫn sử dụng CPU Intel mới nhất có bảy tập, với tổng số hơn 4000 trang, và chỉ dành cho CPU. Hầu hết các thành phần khác phơi bày các khối bộ nhớ hoặc cổng I / O, mà hệ điều hành có thể yêu cầu CPU ánh xạ tới các địa chỉ trong không gian địa chỉ của nó. Một vài trong số các thành phần này cho thấy nhiều thứ hơn đằng sau một vài cổng I / O hoặc địa chỉ bộ nhớ; như một ví dụ, RTC (Đồng hồ thời gian thực, thành phần giữ thời gian của máy tính khi tắt nguồn) để lộ vài trăm byte bộ nhớ phía sau một cặp cổng I / O và đó là một thành phần rất đơn giản có từ PC / AT gốc. Những thứ như đĩa cứng có bộ xử lý hoàn toàn riêng biệt, mà hệ điều hành nói chuyện thông qua các lệnh được tiêu chuẩn hóa. GPU thậm chí còn phức tạp hơn.

Một số người trong các ý kiến ​​trên đề nghị Arduino. Tôi đồng ý với họ, đơn giản hơn rất nhiều khi hiểu về ATMega328, nó thực hiện mọi thứ trên Arduino Uno ngoại trừ việc kết nối USB như một cổng nối tiếp, có một hướng dẫn chỉ với vài trăm trang. Trên Arduino, bạn chạy trực tiếp trên phần cứng, không có hệ điều hành nào ở giữa; chỉ một vài thói quen thư viện nhỏ mà bạn không phải sử dụng nếu bạn không muốn.


1

Ví dụ runnable

Về mặt kỹ thuật, một chương trình chạy mà không có HĐH, là HĐH. Vì vậy, hãy xem cách tạo và chạy một số hệ điều hành rất nhỏ xin chào thế giới.

Mã của tất cả các ví dụ dưới đây có mặt trên repo GitHub này .

Giày cao cổ

Trên x86, điều đơn giản nhất và ở mức thấp nhất bạn có thể làm là tạo Master Boot sector (MBR) , đây là một loại sector khởi động , sau đó cài đặt nó vào đĩa.

Ở đây chúng tôi tạo một printfcuộc gọi với một cuộc gọi duy nhất :

printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img

Kết quả:

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

Đã thử nghiệm trên Ubuntu 18.04, QEMU 2.11.1.

main.img chứa những điều sau đây:

  • \364in octal == 0xf4in hex: mã hóa cho một hltlệnh, thông báo cho CPU ngừng hoạt động.

    Do đó, chương trình của chúng tôi sẽ không làm gì cả: chỉ bắt đầu và dừng lại.

    Chúng tôi sử dụng số bát phân vì \xsố hex không được chỉ định bởi POSIX.

    Chúng ta có thể có được mã hóa này một cách dễ dàng với:

    echo hlt > a.asm
    nasm -f bin a.asm
    hd a
    

    nhưng 0xf4mã hóa cũng được ghi lại trong hướng dẫn sử dụng Intel.

  • %509ssản xuất 509 không gian. Cần phải điền vào tệp cho đến byte 510.

  • \125\252trong bát phân == 0x55theo sau 0xaa: byte ma thuật được yêu cầu bởi phần cứng. Chúng phải là byte 511 và 512.

    Nếu không có, phần cứng sẽ không coi đây là đĩa khởi động.

Lưu ý rằng ngay cả khi không làm gì, một vài ký tự đã được in trên màn hình. Chúng được in bởi phần sụn và phục vụ để xác định hệ thống.

Chạy trên phần cứng thực

Trình giả lập là niềm vui, nhưng phần cứng là vấn đề thực sự.

Tuy nhiên, xin lưu ý rằng điều này rất nguy hiểm và bạn có thể xóa nhầm đĩa của mình: chỉ thực hiện việc này trên các máy cũ không chứa dữ liệu quan trọng! Hoặc thậm chí tốt hơn, các bảng điều khiển như Raspberry Pi, xem ví dụ ARM bên dưới.

Đối với một máy tính xách tay thông thường, bạn phải làm một cái gì đó như:

  • Ghi hình ảnh vào thanh USB (sẽ hủy dữ liệu của bạn!):

    sudo dd if=main.img of=/dev/sdX
    
  • cắm USB trên máy tính

  • bật nó lên

  • bảo nó khởi động từ USB.

    Điều này có nghĩa là làm cho phần sụn chọn USB trước đĩa cứng.

    Nếu đó không phải là hành vi mặc định của máy của bạn, hãy tiếp tục nhấn Enter, F12, ESC hoặc các phím lạ khác sau khi bật nguồn cho đến khi bạn nhận được menu khởi động nơi bạn có thể chọn khởi động từ USB.

    Thường có thể định cấu hình thứ tự tìm kiếm trong các menu đó.

Ví dụ: trên Lenovo Thinkpad T430 cũ của tôi, UEFI BIOS 1.16, tôi có thể thấy:

Chào thế giới

Bây giờ chúng tôi đã thực hiện một chương trình tối thiểu, hãy chuyển sang một thế giới xin chào.

Câu hỏi rõ ràng là: làm thế nào để làm IO? Một vài lựa chọn:

  • yêu cầu phần sụn, ví dụ BIOS hoặc UEFI, làm gì cho chúng tôi
  • VGA: vùng nhớ đặc biệt được in ra màn hình nếu được ghi vào. Có thể được sử dụng trên chế độ Bảo vệ.
  • viết một trình điều khiển và nói chuyện trực tiếp với phần cứng màn hình. Đây là cách "phù hợp" để làm điều đó: mạnh mẽ hơn, nhưng phức tạp hơn.
  • cổng nối tiếp . Đây là một giao thức được tiêu chuẩn hóa rất đơn giản để gửi và lấy các ký tự từ một thiết bị đầu cuối máy chủ.

    Nguồn .

    Thật không may, nó không được tiếp xúc trên hầu hết các máy tính xách tay hiện đại, nhưng là cách phổ biến để đi đến các bảng phát triển, xem các ví dụ ARM dưới đây.

    Đây thực sự là một sự xấu hổ, vì các giao diện như vậy thực sự hữu ích để gỡ lỗi kernel Linux chẳng hạn .

  • sử dụng các tính năng gỡ lỗi của chip. ARM gọi semihosting của họ chẳng hạn. Trên phần cứng thực, nó đòi hỏi một số hỗ trợ phần cứng và phần mềm bổ sung, nhưng trên trình giả lập, nó có thể là một tùy chọn tiện lợi miễn phí. Ví dụ .

Ở đây chúng tôi sẽ làm một ví dụ BIOS vì nó đơn giản hơn trên x86. Nhưng lưu ý rằng nó không phải là phương pháp mạnh mẽ nhất.

chính.S

.code16
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"

link.ld

SECTIONS
{
    . = 0x7c00;
    .text :
    {
        __start = .;
        *(.text)
        . = 0x1FE;
        SHORT(0xAA55)
    }
}

Lắp ráp và liên kết với:

gcc -c -g -o main.o main.S
ld --oformat binary -o main.img -T linker.ld main.o

Kết quả:

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

Đã thử nghiệm trên: Lenovo Thinkpad T430, UEFI BIOS 1.16. Đĩa được tạo trên máy chủ Ubuntu 18.04.

Bên cạnh các hướng dẫn lắp ráp người dùng tiêu chuẩn, chúng tôi có:

  • .code16: báo cho GAS xuất mã 16 bit

  • cli: vô hiệu hóa các ngắt phần mềm. Chúng có thể khiến bộ xử lý bắt đầu chạy lại sauhlt

  • int $0x10: thực hiện cuộc gọi BIOS. Đây là những gì in từng ký tự một.

Các cờ liên kết quan trọng là:

  • --oformat binary: xuất mã lắp ráp nhị phân thô, không làm cong nó trong tệp ELF như trường hợp đối với các tệp thực thi người dùng thông thường.

Sử dụng C thay vì lắp ráp

Vì C biên dịch để lắp ráp, sử dụng C mà không có thư viện chuẩn khá đơn giản, về cơ bản bạn chỉ cần:

  • một tập lệnh liên kết để đặt mọi thứ vào bộ nhớ ở đúng nơi
  • các cờ báo cho GCC không sử dụng thư viện chuẩn
  • một điểm nhập lắp ráp nhỏ đặt trạng thái C yêu cầu main, đáng chú ý là:
    • chồng
    • không có BSS

TODO: liên kết để một số ví dụ x86 trên GitHub. Đây là một ARM tôi đã tạo .

Tuy nhiên, mọi thứ sẽ trở nên thú vị hơn nếu bạn muốn sử dụng thư viện tiêu chuẩn, vì chúng tôi không có nhân Linux, nơi thực hiện nhiều chức năng thư viện tiêu chuẩn C thông qua POSIX .

Một vài khả năng, mà không cần đến một hệ điều hành toàn diện như Linux, bao gồm:

  • Newlib

    Ví dụ chi tiết tại: https://electronics.stackexchange.com/questions/223929/c-stiteria-lologists-on-bare-metal/223931

    Ở Newlib, bạn phải tự thực hiện các tòa nhà chọc trời, nhưng bạn có được một hệ thống rất tối thiểu và rất dễ thực hiện chúng.

    Ví dụ, bạn có thể chuyển hướng printftới các hệ thống UART hoặc ARM, hoặc thực hiện exit()với semihosting .

  • các hệ điều hành nhúng như FreeRTOSZephyr .

    Các hệ điều hành như vậy thường cho phép bạn tắt lập lịch trước, do đó cung cấp cho bạn toàn quyền kiểm soát thời gian chạy của chương trình.

    Chúng có thể được coi là một loại Newlib được triển khai trước.

CÁNH TAY

Trong ARM, các ý tưởng chung là như nhau. Tôi đã tải lên:

Đối với Raspberry Pi, https://github.com/dwelch67/raspberrypi trông giống như hướng dẫn phổ biến nhất hiện nay.

Một số khác biệt từ x86 bao gồm:

  • IO được thực hiện bằng cách viết trực tiếp vào địa chỉ ma thuật, không có inouthướng dẫn.

    Đây được gọi là bộ nhớ ánh xạ IO .

  • đối với một số phần cứng thực sự, như Raspberry Pi, bạn có thể tự thêm phần sụn (BIOS) vào hình ảnh đĩa.

    Đó là một điều tốt, vì nó làm cho việc cập nhật firmware đó trở nên minh bạch hơn.

Chương trình cơ sở

Trên thực tế, khu vực khởi động của bạn không phải là phần mềm đầu tiên chạy trên CPU của hệ thống.

Cái thực sự chạy đầu tiên là cái gọi là phần sụn , là một phần mềm:

  • được thực hiện bởi các nhà sản xuất phần cứng
  • nguồn thường đóng nhưng có khả năng dựa trên C
  • được lưu trữ trong bộ nhớ chỉ đọc và do đó khó / không thể sửa đổi nếu không có sự đồng ý của nhà cung cấp.

Các phần mềm nổi tiếng bao gồm:

  • BIOS : firmware x86 cũ tất cả hiện tại. SeaBIOS là triển khai mã nguồn mở mặc định được QEMU sử dụng.
  • UEFI : Người kế vị BIOS, được tiêu chuẩn hóa tốt hơn, nhưng có khả năng hơn, và vô cùng nở rộ.
  • Coreboot : nỗ lực mã nguồn mở chéo cao quý

Phần sụn thực hiện những việc như:

  • lặp qua từng đĩa cứng, USB, mạng, v.v. cho đến khi bạn tìm thấy thứ gì đó có khả năng khởi động.

    Khi chúng tôi chạy QEMU, hãy -hdanói rằng đó main.imglà một đĩa cứng được kết nối với phần cứng và

    hda là cái đầu tiên được thử, và nó được sử dụng.

  • tải 512 byte đầu tiên vào địa chỉ bộ nhớ RAM 0x7c00, đặt RIP của CPU ở đó và để nó chạy

  • hiển thị những thứ như menu khởi động hoặc lệnh in BIOS trên màn hình

Phần sụn cung cấp chức năng giống như hệ điều hành mà hầu hết các hệ điều hành phụ thuộc. Ví dụ: một tập hợp con Python đã được chuyển để chạy trên BIOS / UEFI: https://www.youtube.com/watch?v=bYQ_lq5dcvM

Có thể lập luận rằng các phần mềm không thể phân biệt được với các HĐH và phần sụn đó là chương trình kim loại trần "thật" duy nhất mà người ta có thể làm.

Như nhà phát triển CoreOS này đặt nó :

Phần khó

Khi bạn cấp nguồn cho PC, các chip tạo nên chipset (cầu bắc, cầu nam và SuperIO) vẫn chưa được khởi tạo đúng cách. Mặc dù ROM ROM đã bị loại bỏ khỏi CPU hết mức có thể, nhưng CPU này có thể truy cập được, bởi vì nó phải như vậy, nếu không CPU sẽ không có hướng dẫn để thực thi. Điều này không có nghĩa là BIOS ROM được ánh xạ hoàn toàn, thường thì không. Nhưng vừa đủ được ánh xạ để quá trình khởi động diễn ra. Bất kỳ thiết bị khác, chỉ cần quên nó.

Khi bạn chạy Coreboot theo QEMU, bạn có thể thử nghiệm các lớp Coreboot cao hơn và với tải trọng, nhưng QEMU cung cấp rất ít cơ hội để thử nghiệm mã khởi động cấp thấp. Đối với một điều, RAM chỉ hoạt động ngay từ đầu.

Đăng trạng thái ban đầu của BIOS

Giống như nhiều thứ trong phần cứng, tiêu chuẩn hóa còn yếu và một trong những điều bạn không nên dựa vào là trạng thái đăng ký ban đầu khi mã của bạn bắt đầu chạy sau BIOS.

Vì vậy, hãy tạo cho mình một ưu tiên và sử dụng một số mã khởi tạo như sau: https://stackoverflow.com/a/32509555/895245

Các thanh ghi thích %ds%escó tác dụng phụ quan trọng, vì vậy bạn nên loại bỏ chúng ngay cả khi bạn không sử dụng chúng một cách rõ ràng.

Lưu ý rằng một số trình giả lập đẹp hơn phần cứng thực và cung cấp cho bạn trạng thái ban đầu tốt đẹp. Sau đó, khi bạn chạy trên phần cứng thực sự, mọi thứ sẽ phá vỡ.

GNU GRUB Multiboot

Các phần khởi động rất đơn giản, nhưng chúng không thuận tiện lắm:

  • bạn chỉ có thể có một hệ điều hành trên mỗi đĩa
  • mã tải phải thực sự nhỏ và vừa với 512 byte. Điều này có thể được giải quyết bằng cuộc gọi BIOS int 0x13 .
  • bạn phải tự khởi động rất nhiều, như chuyển sang chế độ được bảo vệ

Chính vì những lý do đó mà GNU GRUB đã tạo ra một định dạng tệp thuận tiện hơn gọi là multiboot.

Ví dụ làm việc tối thiểu: https://github.com/cirosantilli/x86-bare-metal-examples/tree/d217b180be4220a0b4a453f31275d38e697a99e0/multiboot/hello-world

Tôi cũng sử dụng nó trên repo ví dụ GitHub của mình để có thể dễ dàng chạy tất cả các ví dụ trên phần cứng thực mà không cần ghi USB một triệu lần. Trên QEMU nó trông như thế này:

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

Nếu bạn chuẩn bị HĐH của mình dưới dạng tệp multiboot, GRUB có thể tìm thấy nó trong một hệ thống tệp thông thường.

Đây là những gì hầu hết các distro làm, đặt hình ảnh hệ điều hành /boot.

Các tệp Multiboot về cơ bản là một tệp ELF với một tiêu đề đặc biệt. Chúng được GRUB chỉ định tại: https://www.gnu.org/software/grub/manual/multiboot/multiboot.html

Bạn có thể biến một tệp multiboot thành một đĩa có thể khởi động được grub-mkrescue.

El Torito

Định dạng có thể được ghi vào đĩa CD: https://en.wikipedia.org/wiki/El_Torito_%28CD-ROM_st Chuẩn% 29

Cũng có thể tạo ra một hình ảnh lai hoạt động trên cả ISO hoặc USB. Điều này có thể được thực hiện với grub-mkrescue( ví dụ ) và cũng được nhân Linux thực hiện khi make isoimagesử dụng isohybrid.

Tài nguyên

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.