Hỏi về nguyên lý nạp chương trình vào RAM

CÂU HỎI: Trong C++ khi chương trình của mình được chạy, nó sẽ được tải từ file trên đĩa vào RAM như thế nào?
=>>Và các hằng số cũng là những vị trí lưu trữ dữ liệu thì nó được lưu trữ ra làm sao?
Mong mọi người giúp mình với!

1 Like

Cái này nghe rất uyên thâm và dự là có liên quan đến Nguyên lý hệ điều hành. Chờ cao nhân chỉ giáo. :smile:

Nói ra thì rất dài dòng, em chỉ nói qua thế này:

  • Bác biết rằng một mã nguồn tùy vào độ phức tạp, phương pháp lập trình sau khi biên dịch thành một chương trìnhchương trình này khi chuyển sang trạng thái chạy sẽ được chia thành các tiến trình (process) (Với những chương trình đơn giản thì có thể cả chương trình khi chạy sẽ chỉ thành một tiến trình)

  • Một process khi có một lời gọi hệ thống (system call) sẽ được HĐH cung cấp tài nguyên bộ nhớ cần thiết để hoạt động.

  • Qúa trình tương tác vật lý điển tử rất phức tạp nên để đánh lừa các chương trình rằng nó đang được cung cấp một dải địa chỉ liền mạch và cũng để cho con người tiện thao tác thì HĐH sử dụng một kĩ thuật trừu tượng hóa bộ nhớ vật lý thành các bộ nhớ ảo. Cụ thể HĐH sẽ quản lý thành các ô nhớ, độ dài 1 ô nhớ tùy vào cách HĐH định nghĩa, và cứ khi khai báokhởi tạo một biến, mảng hay hằng thì nó sẽ được HĐH lưu trên một dải ô nhớ ảo liền mạch (nhưng thực chất ở mức vật lý nó nằm rải rác đâu đó trên thanh RAM)

  • Ví dụ bác khi báo một biến int i và nó được lưu ở vị trí ô nhớ 452321 trên thanh RAM nhưng hệ điều hành sẽ không quản lý bằng hệ thập phân như vậy mà nó sẽ quản lý bằng hệ hexadecimal (hệ thập lục phân - 16), tức là vị trí của biến i ở trên sẽ được đổi thành 6E6E1 và được kí hiệu là 0x6E6E1 (trong C cũng viết như vậy) còn trong ASM thì viết là 6E6E1h
    Như vậy với HĐH Win 32 bit tức định nghĩa độ dài một ô nhớ bằng 32 bit nhị phân, tương ứng là 8 bit hex, thì chỉ có thể quản lý được số lượng địa chỉ từ địa chỉ 0x00000000 -> 0xffffffff

  • Ngoài ra thùy theo HĐH có thể sử dụng thêm kĩ thuật phân đoạn bộ nhớ ảo

  • Trong khi lập trình bác biết rằng các ngôn ngữ cũng cung cấp tương tác ở mức thấp lên các địa chỉ bộ nhớ ảo mà cụ thể là con trỏ

P/s: Tóm lại tổng quát là thằng HĐH định nghĩa các địa chỉ vật lý thành các địa chỉ ảo…một chương trình khi có yêu cầu chạy thì được phân thành các process…một process khi có một yêu cầu tới HĐH sẽ được HĐH cung cấp cho một dải địa chỉ ảo liền mạch…Cứ thế thằng process vào sau sẽ dùng các địa chỉ chưa dùng tiếp sau…thằng process nào xong yêu cầu giải phóng bộ nhớ cho thằng khác vào…Qua loa là vậy!

9 Likes

Cảm ơn bạn :nsmks94 Nguyễn Đức Minh

Đó mới chỉ một phần. Mình nói vắn tắt đầy đủ ntn. Trc hết phải hiểu về cấu trúc của tệp tin thực thi như .exe, .dll, .ocv, .sys,…etc (ở đây nói riêng là trên nền tảng Windows).
I. Phần thứ nhất là nói sơ qua về cấu trúc một tệp tin thực thi.
Một tệp tin thực thi mang tên là PE (Portable Execute). Nó gồm các thành phần chính lần lượt như sau:

  1. Header: Chứa các thông tin của tệp tin như DOS Header, PE Header, Optional Header, NT Header, Section Header,…
  2. Sections: Tuỳ thuộc vào trình biên dịch mà nó đặt tên và số lượng section khác nhau nhưng nó chủ yếu là bắt buộc có những section sau:
  • .text: Section này chứa mã lệnh thực thi của chương trình.
  • .data: Section này chứa các biến chưa đc khởi tạo.
  • .bss: Section này chứa những biến đã đc khởi tạo, hoặc hằng số.
  • .idata: Section này chứa các thông tin về các hàm của các thư viện mình sử dụng trong chương trình.
  • .rsrc: Section này chứa tài nguyên nhúng vào chương trình như hình ảnh, âm thanh,…
  • .reloc: Section này để giúp trình Windows Loader chỉnh sửa những ánh xạ lỗi khi chương trình đc chạy và ánh xạ vào RAM.
    II. Nạp chương trình vào RAM.
    Tóm tắt là: Khi mình chạy chương trình thì trình Windows Loader (đây là trình có chức năng nạp chương trình chạy vào bộ nhớ) thì nó sẽ làm các bước lần lượt như sau:
  1. Nó (Windows Loader) sẽ quét qua header của chương trình mình chạy để lấy thông tin như các thông tin về vị trí ánh xạ (ImageBase), kích thước bộ nhớ ảo của RAM dành cho chương trình (ImageSize), điểm chạy đầu tiên (gọi là OEP), thông tin về các hàm, thư viện mình sử dụng (dựa vào header nó sẽ quét đến section .idata)
  2. Sau khi có hết thông tin đó rồi thì nó ánh xạ từng thư viện (.dll, .ocx,…etc), sau đó nó là đến các sections chứa biến, mã thực thi,…
  3. Rồi đến phân chia Stack cho từng luồng thực thi (Thread).
  4. Sau khi tải hết lên RAM rồi thì Windows Loader sẽ giao cho nó, và cho phép bắt đầu chạy từ mã lệnh đầu tiên. Vị trí điểm khởi chạy đầu tiên này đc gọi là OEP, và chạy cho đến hết chương trình.

Đó là tất cả quá trình là HĐH Windows chạy một ứng dụng. Trên đó là tóm tắt nếu bạn muốn xem chi tiết hơn tất cả cái trên thì bạn có thể tìm hiểu ở đây:
https://msdn.microsoft.com/en-us/magazine/cc301808.aspx
https://msdn.microsoft.com/en-us/magazine/cc301805.aspx

Ps: Trên đây là nói riêng về chương trình Native, còn về .NET có khác và thêm một số vấn đề nữa bạn có thể tìm hiểu ở MSDN.COM

Br,
Vic

6 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?