Quản lý bộ nhớ trong Linux | Bài tập lớn học phần Hệ điều hành | Trường Đại học Phenikaa
Linux hỗ trợ bộ nhớ ảo, tức là nó sử dụng một phần của ổ đĩa như là RAM để tăng kích thước của bộ nhớ. Kernel sẽ ghi nội dung của các phần của bộ nhớ hiện không sử dụng lên đĩa cứng để có thêm không gian cho các mục đích khác. Khi cần sử dụng lại những nội dung này, chúng sẽ được đọc trở lại vào bộ nhớ. Điều này hoàn toàn trong suốt với người sử dụng, với các chương trình chạy trong Linux chỉ nhìn thấy một lượng lớn bộ nhớ có sẵn mà không cần quan tâm đến việc nó nằm trên đĩa. Tài liệu giúp bạn tham khảo, ôn tập và đạt kết quả cao. Mời bạn đón xem.
Preview text:
Mục Lục
Lời nói đầu.........................................................................................................................1
CHƯƠNG I: TỔNG QUAN VỀ BỘ NHỚ TRONG HỆ ĐIỀU HÀNH LINUX
1.Giới thiệu về hệ điều hành LINUX.................................................................................2
1.1.Hiệu suất, tính linh hoạt và cơ chế quản lý..................................................................3
CHƯƠNG II: CƠ CHẾ QUẢN LÝ BỘ NHỚ, ÁNH XẠ BỘ NHỚ, CẤP PHÁT VÀ GIẢI PHÓNG VÙNG NHỚ
2.Quản lý bộ nhớ ảo...........................................................................................................4
2.1.Quản lý bộ nhớ vật lý.................................................................................................10 2.1.1.Ánh xạ bộ nhớ (Memory
Mapping)........................................................................12
2.1.1.1.Cấp phát vùng nhớ...............................................................................................13
CHƯƠNG III: CƠ CHẾ PHÂN TRANG VÀ PHÂN ĐOẠN
3.Cơ chế phân trang.........................................................................................................15
3.1.Cơ chế phân đoạn......................................................................................................21 3.1.1.Cơ chế phân
mảnh..................................................................................................22
TÀI LIỆU THAM KHẢO...............................................................................................26 Lời nói đầu
Hệ điều hành Linux là một trong những hệ điều hành mã nguồn mở phổ biến nhất trên thế
giới. Được phát triển bởi một cộng đồng lớn các nhà phát triển trên toàn cầu, Linux đã trở
thành một trong những lựa chọn ưa thích của người dùng và các tổ chức, từ các hệ thống
nhúng đến các máy chủ và máy tính cá nhân.
Linux nổi tiếng với tính linh hoạt, độ ổn định và tính bảo mật cao. Với sự đa dạng của các
phiên bản và phân phối như Ubuntu, CentOS, Debian, và nhiều hơn nữa, người dùng có
thể tùy chỉnh và điều chỉnh hệ thống của họ theo nhu cầu cụ thể.
Một trong những điểm mạnh của Linux là môi trường dòng lệnh mạnh mẽ, cho phép người
dùng tương tác trực tiếp với hệ thống và thực hiện nhiều tác vụ từ cơ bản đến phức tạp một cách hiệu quả.
Quản lý bộ nhớ trong Linux là một khía cạnh quan trọng của hệ điều hành, đặc biệt là với
các hệ thống máy chủ và máy tính có tài nguyên hạn chế. Hệ điều hành Linux sử dụng
một số kỹ thuật quản lý bộ nhớ để tối ưu hóa việc sử dụng tài nguyên và đảm bảo hiệu suất hệ thống.
Trong Linux, quản lý bộ nhớ bao gồm các khái niệm như bộ nhớ ảo, bộ nhớ vật lý, swap
space, và các cơ chế như paging và segmentation để quản lý việc truy cập vào bộ nhớ.
Linux sử dụng cơ chế điều khiển việc sử dụng bộ nhớ, đảm bảo rằng các tiến trình hoạt
động một cách hiệu quả mà không làm giảm hiệu suất của hệ thống.
Các công cụ quản lý bộ nhớ trong Linux như top, free, và vmstat giúp người quản trị hệ
thống theo dõi và kiểm soát việc sử dụng bộ nhớ của các tiến trình và ứng dụng, từ đó tối
ưu hóa hiệu suất và sử dụng tài nguyên một cách hiệu quả nhất.
CHƯƠNG I: TỔNG QUAN VỀ BỘ NHỚ TRONG HỆ ĐIỀU HÀNH LINUX
1.Giới Thiệu Về Hệ Điều Hành Linux
Linux là một hệ điều hành dạng Unix (Unix-Like Operating System) được thiết kế để chạy
trên máy tính cá nhân, sử dụng bộ vi xử lý trung tâm Intel 80386 trở lên hoặc các bộ vi xử
lý trung tâm tương thích như AMD, Cyrix. Phiên bản Linux đầu tiên được Linus Torvalds
phát triển vào năm 1991, và phiên bản Linux 1.0 ra đời vào năm 1994, sau ba năm phát
triển. Hệ điều hành này được phát triển và phát hành dưới giấy phép GNU General Public
License, cho phép bất kỳ ai cũng có thể tải và xem mã nguồn của Linux.
Ban đầu, Linux được phát triển cho vi xử lý dòng 386, và sau đó đã mở rộng để hỗ trợ
một loạt lớn các kiến trúc vi xử lý như x86, x86-64, ARM, PowerPC, và MIPS, giúp nó
trở thành một trong những hệ điều hành phổ biến nhất và linh hoạt nhất hiện nay.
Linux không chỉ được sử dụng trên máy tính cá nhân mà còn được áp dụng trong một loạt
các thiết bị nhúng như máy điện thoại di động, máy tính bảng, máy chủ, router, và các
thiết bị IoT. Sự linh hoạt và tính đa dạng của Linux đã đưa nó trở thành một trong những
lựa chọn hàng đầu cho các ứng dụng từ nhỏ đến lớn, từ cá nhân đến doanh nghiệp.
*Hiệu suất, tính linh hoạt và cơ chế quản lý
Các thuộc tính và các phân khúc của Linux được tập trung vào hiệu suất và tính linh hoạt
của người sử dụng. Hiện nay, các máy tính cá nhân thường được trang bị ít nhất 1GB bộ
nhớ RAM, trong khi các máy chủ server có thể có hàng GB bộ nhớ. Mặc dù có sự gia tăng
về dung lượng bộ nhớ, Linux vẫn giữ được sự hiệu quả trong quản lý bộ nhớ.
Linux có một cách tiếp cận rõ ràng trong việc quản lý bộ nhớ. Các ứng dụng trên Linux
không được phép truy cập trực tiếp vào địa chỉ vật lý của bộ nhớ. Thay vào đó, Linux
cung cấp cho mỗi tiến trình - được gọi là tiến trình - một mô hình địa chỉ phẳng không
phân đoạn như DOS. Mỗi tiến trình chỉ nhìn thấy một phần của không gian địa chỉ của nó.
Hầu hết các phiên bản Unix cung cấp cơ chế bảo vệ bộ nhớ để đảm bảo rằng không có
tiến trình nào có thể ghi đè lên bộ nhớ của tiến trình khác hoặc trên bộ nhớ hệ thống.
Trong hầu hết các hệ thống Linux và Unix, con trỏ được sử dụng để trỏ đến một ô nhớ cụ
thể với số bit từ 32 trở lên, cho phép hệ thống định vị đến 4GB bộ nhớ. Mô hình bộ nhớ
phẳng này dễ dàng truy xuất và xử lý hơn so với mô hình phân đoạn.
Ngoài ra, một số hệ thống sử dụng mô hình địa chỉ 64 bit, mở ra khả năng mở rộng đến
terabyte trong không gian địa chỉ.
Các sơ đồ quản lý bộ nhớ trong Linux bao gồm: phân hoạch cố định, phân hoạch động,
swapping, phân trang, phân đoạn và kết hợp phân trang phân đoạn. Linux sử dụng
swapping, phân trang, phân đoạn và bộ nhớ ảo để quản lý bộ nhớ. Các cơ chế này sẽ được
thảo luận chi tiết trong các phần tiếp theo để hiểu rõ quá trình quản lý bộ nhớ trong hệ điều hành Linux.
CHƯƠNG II: CƠ CHẾ QUẢN LÝ BỘ NHỚ, ÁNH XẠ BỘ NHỚ, CẤP PHÁT
VÀ GIẢI PHÓNG VÙNG NHỚ
2.Quản lý bộ nhớ ảo
Linux hỗ trợ bộ nhớ ảo, tức là nó sử dụng một phần của ổ đĩa như là RAM để tăng kích
thước của bộ nhớ. Kernel sẽ ghi nội dung của các phần của bộ nhớ hiện không sử dụng
lên đĩa cứng để có thêm không gian cho các mục đích khác. Khi cần sử dụng lại những
nội dung này, chúng sẽ được đọc trở lại vào bộ nhớ. Điều này hoàn toàn trong suốt với
người sử dụng, với các chương trình chạy trong Linux chỉ nhìn thấy một lượng lớn bộ nhớ
có sẵn mà không cần quan tâm đến việc nó nằm trên đĩa. Tuy nhiên, việc đọc và ghi lên
đĩa thường chậm hơn khoảng một nghìn lần so với việc sử dụng bộ nhớ thật, vì vậy các
chương trình chạy có thể không nhanh như mong đợi. Phần của ổ đĩa cứng được sử dụng
như là bộ nhớ ảo được gọi là không gian hoán đổi.
Linux có thể sử dụng một tệp thông thường trong hệ thống tệp hoặc một phân vùng đặc
biệt để làm không gian hoán đổi. Một phân vùng swap có thể nhanh hơn nhưng lại khó
thay đổi kích thước so với việc sử dụng một tệp swap. Khi bạn biết mình cần bao nhiêu
không gian hoán đổi, bạn có thể bắt đầu tạo một phân vùng swap. Tuy nhiên, nếu bạn
không chắc chắn, bạn có thể sử dụng một tệp swap trước, sau đó sử dụng hệ thống một
thời gian để xác định cần bao nhiêu không gian hoán đổi trước khi quyết định tạo phân vùng swap.
*Mô hình bộ nhớ ảo
Trước khi tìm hiểu về các phương thức Linux sử dụng để hỗ trợ bộ nhớ ảo, hãy hiểu một
ít về mô hình trừu tượng của nó.
Khi bộ xử lý thực thi một chương trình, nó đọc một chỉ lệnh từ bộ nhớ và giải mã nó.
Trong quá trình này, nó có thể lấy hoặc lưu trữ dữ liệu từ một vị trí trong bộ nhớ. Sau đó,
bộ xử lý thực hiện chỉ lệnh và di chuyển đến chỉ lệnh tiếp theo trong chương trình. Do đó,
bộ xử lý luôn truy cập bộ nhớ để lấy hoặc lưu trữ dữ liệu.
Tất cả các địa chỉ trong bộ nhớ ảo là địa chỉ ảo, không phải là địa chỉ vật lý. Bộ xử lý
chuyển đổi các địa chỉ ảo này thành địa chỉ vật lý dựa trên thông tin trong các bảng quản lý bởi hệ điều hành.
Để làm cho việc chuyển đổi này dễ dàng hơn, bộ nhớ ảo và bộ nhớ vật lý được chia thành
nhiều phần có kích thước thích hợp gọi là "trang". Tất cả các trang này có cùng kích thước
để quản lý dễ dàng. Trên Linux, hệ thống Alpha AXP sử dụng trang có kích thước 8Kbyte,
trong khi hệ thống Intel x86 sử dụng trang có kích thước 4Kbyte. Mỗi trang được gán một
số duy nhất được gọi là "số khung trang" (PFN: Page Frame Number).
Để chuyển từ địa chỉ ảo sang địa chỉ vật lý, bộ xử lý sử dụng số khung trang ảo như là chỉ
mục vào bảng trang của tiến trình. Nếu mục tương ứng trong bảng trang là hợp lệ, bộ xử
lý sẽ lấy số khung trang vật lý từ mục này. Nếu mục không hợp lệ, tiến trình sẽ truy cập
vào một vùng không tồn tại của bộ nhớ ảo, và bộ xử lý sẽ báo lỗi trang cho hệ điều hành.
Sau đó, bộ xử lý sẽ nhân số khung trang vật lý với kích thước trang để lấy địa chỉ của
trang cơ sở trong bộ nhớ vật lý. Cuối cùng, bộ xử lý sẽ cộng địa chỉ offset để lấy địa chỉ
cuối cùng của lệnh hoặc dữ liệu cần truy cập.
Bằng cách ánh xạ địa chỉ ảo và địa chỉ vật lý như vậy, bộ nhớ ảo có thể được ánh xạ vào
bộ nhớ vật lý của hệ thống một cách linh hoạt. Các trang trong bộ nhớ ảo không được hiển
thị trong bộ nhớ vật lý theo một thứ tự nhất định, mà thực sự được xếp đặt linh hoạt.
*Sử dụng không gian hoán đổi
Trong hệ điều hành Linux, việc sử dụng không gian hoán đổi là một phương pháp quan
trọng để quản lý bộ nhớ khi bộ nhớ vật lý (RAM) không đủ để chứa tất cả các tiến trình
và dữ liệu cần thiết. Linux cung cấp hai loại không gian hoán đổi là partition hoán đổi và
tập tin hoán đổi, mỗi loại đều có những đặc điểm và ưu điểm riêng.
Một không gian hoán đổi đã khởi tạo sẽ được lấy để sử dụng nhờ lệnh swapon. Lệnh này
báo cho kernel rằng không gian hoán đổi có thể được sử dụng. Đường dẫn đến không gian
hoán đổi được cấp như là đối số, vì vậy để bắt đầu hoán đổi trên một file swap tạm thời,
bạn có thể sử dụng đoạn lệnh sau : $ swapon /extra-swap $
Không gian hoán đổi có thể được sử dụng tự động bằng cách liệt kê chúng trong file/etc/fstab /dev/hda8 none swap sw 0 0 /swapfile none swap sw 0 0
Đoạn mã khởi động sẽ chạy lệnh swapon -a, lệnh này sẽ bắt đầu thực hiện hoán đổi trên
tất cả các không gian hoán đổi được liệt kê trong file /etc/fstab. Do đó lệnh swapon chỉ
thường được sử dụng khi cần hoán đổi thêm.
Bạn có thể quản lý việc sử dụng không gian hoán đổi với lệnh free. Nó sẽ cho biết tổng
số không gian hoán đổi được sử dụng $ free Total used free shared Buffers
Mem: 15152 14896 256 12404 2528 -/+ buffers: 12368 2784 Swap: 32452 6684 25768 $
Một không gian hoán đổi có thể bị loại bỏ bằng lệnh swapoff. Thông thường không cần
phải dùng lệnh này ngoại trừ đối với không gian hoán đổi tạm thời. Bất kì trang nào đang
được sử dụng trong không gian hoán đổi đều được đưa vào trước. Nếu không có đủ bộ
nhớ vật lý để chứa chúng thì chúng sẽ được đưa ra đến một không gian hoán đổi khác.
Nếu không có đủ bộ nhớ ảo để chứa tất cả các trang, Linux sẽ bắt đầu dừng lại (thrash),
sau một khoảng thời gian dài nó sẽ khôi phục nhưng trong lúc ấy hệ thống không thể sử
dụng. Bạn nên kiểm tra ( bằng lệnh free ) để xem có đủ bộ nhớ trống không trước khi loại
bỏ một không gian hoán đổi.
Tất cả không gian hoán đổi được sử dụng tự động nhờ lệnh swapon -a đều có thể được
loại bỏ với lệnh swapoff -a, lệnh này tìm thông tin trong file /etc/fstab để loại bỏ. Còn
các không gian hoán đổi được điều khiển bằng tay thì vẫn sử dụng bình thường.Đôi khi
có nhiều không gian hoán đổi được sử dụng mặc dù có nhiều bộ nhớ vật lý trống. Điều
này có thể xảy ra nếu tại một thời điểm có nhu cầu về hoán đổi, nhưng sau đó một tiến
trình lớn chiếm nhiều bộ nhớ vật lý kết thúc và giải phóng bộ nhớ. Dữ liệu đã đưa ra sẽ
không tự động đưa vào cho đến khi nó cần, vì vậy bộ nhớ vật lý sẽ còn trống trong một
thời gian dài. Bạn không cần phải lo lắng về điều này mà chỉ cần biết điều gì đang xảy ra.
*Partition Hoán Đổi
Partition hoán đổi là việc dành một phần của ổ cứng để sử dụng làm không gian hoán đổi.
Trong quá trình cài đặt hệ thống Linux, người dùng có thể chọn tạo một phân vùng đặc
biệt để sử dụng làm không gian hoán đổi. Điều này cho phép hệ thống truy cập nhanh
chóng vào không gian hoán đổi mà không gây ra chi phí hoặc tốn thời gian cho việc tìm
kiếm trong hệ thống tập tin. Ưu điểm:
Hiệu suất cao vì không gian hoán đổi được cấp phát trên một phân vùng riêng biệt.
Dễ dàng cấu hình trong quá trình cài đặt hệ thống. Nhược điểm:
Cần phải dự đoán trước và cấu hình kích thước phù hợp cho phân vùng hoán đổi, điều này
có thể gây ra lãng phí không gian ổ đĩa nếu kích thước không được ước lượng chính xác.
*Tập Tin Hoán Đổi
Tập tin hoán đổi là việc sử dụng một tập tin trên hệ thống tập tin để lưu trữ dữ liệu hoán
đổi. Trong Linux, tập tin hoán đổi thường được tạo ra và quản lý bởi hệ điều hành để sử dụng khi cần thiết. Ưu điểm:
Linh hoạt hơn vì không cần phải cấu hình trước kích thước hoặc vị trí của không gian hoán đổi.
Dễ dàng di chuyển hoặc sao lưu tập tin hoán đổi. Nhược điểm:
Hiệu suất thấp hơn so với partition hoán đổi do cần thao tác trên hệ thống tập tin.
Có thể tạo ra nhiều tập tin hoán đổi nhỏ, dẫn đến hiện tượng phân mảnh (fragmentation)
trong hệ thống tập tin.Sử dụng không gian hoán đổi là một phương pháp quan trọng trong
quản lý bộ nhớ của hệ thống Linux. Việc lựa chọn giữa partition hoán đổi và tập tin hoán
đổi phụ thuộc vào các yếu tố như hiệu suất, linh hoạt và yêu cầu cụ thể của hệ thống. Bằng
cách sử dụng một trong hai loại không gian hoán đổi này, người quản trị có thể đảm bảo
rằng hệ thống sẽ có đủ bộ nhớ để hoạt động một cách hiệu quả và ổn định.
*Định vị không gian hoán đổi
Người ta thường nói rằng bạn nên định vị không gian hoán đổi gấp đôi bộ nhớ vật lý,
nhưng đây không phải là một quy luật đúng. Bạn hãy xem cách làm đúng sau đây :
Dự đoán tổng bộ nhớ mà bạn cần. Đây là số lượng bộ nhớ lớn nhất mà bạn cần tại một
thời điểm nào đó, là tổng bộ nhớ cần thiết cho tất cả các chương trình mà bạn muốn chạy
cùng một lúc. Lệnh free và ps sẽ có ích để đoán lượng bộ nhớ cần dùng.
Cộng thêm một ít vào dự đoán ở bước 1, bởi vì dự đoán về kích thước các chương trình
có thể sai do bạn quên một số chương trình mà bạn muốn chạy, và để chắc chắn bạn nên
chuẩn bị một không gian phụ để dùng khi cần. Nên định vị dư hơn là thiếu nhưng không
dư nhiều quá sẽ gây lãng phí. Bạn cũng nên làm tròn lên thành một số chẵn megabyte.
Dựa trên những tính toán trên, bạn biết sẽ cần tổng cộng bao nhiêu bộ nhớ. Vì vậy, để định
vị không gian hoán đổi, bạn chỉ cần lấy tổng bộ nhớ sẽ dùng trừ cho bộ nhớ vật lý.
Nếu không gian hoán đổi mà bạn đã tính lớn hơn hai lần bộ nhớ vật lý thì bạn nên mua
thêm RAM, nếu không hiệu năng của máy sẽ thấp.
Tốt hơn hết là nên có một vài không gian hoán đổi cho dù theo sự tính toán của bạn là
không cần. Linux sử dụng không gian hoán đổi khá linh hoạt. Linux sẽ đưa các trang nhớ
không sử dụng ra ngoài cho dù bộ nhớ chưa cần dùng. Điều này giúp tránh việc chờ đợi
hoán đổi khi cần bộ nhớ.
2.1.Quản lý bộ nhớ vật lý *Bộ định vùng
Các bảng trang kernel ánh xạ tối đa bộ nhớ vật lý vào dãy địa chỉ bắt đầu tại
PAGE_OFFSET. Các trang vật lý dành cho đoạn mã và dữ liệu kernel sẽ được cách riêng
và không được sử dụng cho mục đích khác. Các trang vật lý khác được cung cấp cho bộ
nhớ ảo tiến trình, bộ nhớ đệm, và bộ nhớ ảo kernel khi cần thiết. Để làm điều này, chúng
ta cần cách để theo dõi trang vật lý nào đang được sử dụng và được sử dụng bởi ai.
Bộ định vùng (zone allocator) quản lý bộ nhớ vật lý. Bất kỳ mã kernel nào cũng có thể
gọi tới bộ định vùng thông qua hàm alloc_pages() để nhận một khối gồm 2^n trang được
cấp phát trên một đường biên tương ứng. Chúng ta cũng có thể trả khối trang này lại cho
bộ định vùng thông qua hàm free_pages(). Số mũ n được gọi là thứ tự của sự định vùng.
Các khối không cần phải được giải phóng theo cách mà chúng được cấp phát, nhưng phải
được giải phóng chính xác và khung trang đầu tiên của khối phải có một số tham chiếu
khác 0. Ví dụ, khi yêu cầu một khối 8 trang, sau đó giải phóng một khối 2 trang trong khối
8 trang đó, bạn cần tăng biến tham chiếu của trang đầu tiên trong khối 2 trang đó. Lý do
là khi bạn định vị một khối 8 trang, chỉ có biến tham chiếu của trang đầu tiên trong khối
được tăng, và đoạn mã giải phóng trang sẽ không giải phóng một trang có biến tham chiếu
bằng 0, vì vậy biến tham chiếu của các trang khác phải được điều khiển bằng tay. Việc
tăng tất cả các biến tham chiếu của các trang trong khối là không cần thiết và làm tăng
kích thước đoạn mã định vị trang. *Các vùng
Các dãy trang vật lý khác nhau có các thuộc tính khác nhau, phục vụ cho các mục đích
của kernel. Ví dụ, DMA (Direct Memory Access) cho phép các thiết bị ngoại vi đọc và
viết dữ liệu trực tiếp lên RAM mà không cần sự can thiệp của CPU, và chỉ có thể hoạt
động với địa chỉ vật lý nhỏ hơn 16MB. Một số hệ thống có bộ nhớ vật lý lớn hơn có thể
được ánh xạ từ PAGE_OFFSET đến 4GB, và những trang vật lý này không thể truy cập
trực tiếp vào kernel, vì vậy chúng phải được quản lý theo cách khác. Bộ định vùng quản
lý các sự khác biệt như vậy bằng cách chia bộ nhớ thành các vùng và xem mỗi vùng là
một đơn vị cho sự định vị.
Cấu trúc dữ liệu root được quản lý bởi bộ định vùng là zone_struct, bao gồm tất cả dữ liệu
liên quan đến việc quản lý một vùng cụ thể.
Zonelist_struct bao gồm một mảng các con trỏ zone_struct và một gfp_mask chỉ ra cơ chế
định vị nào có thể sử dụng zone_list. Zone_struct offset chỉ ra địa chỉ offset của nơi bắt
đầu một vùng trong bộ nhớ vật lý.
2.1.1.Ánh xạ bộ nhớ (Memory Mapping)
Khi một chương trình được thực thi, dữ liệu của nó cần phải được đưa vào không gian địa
chỉ ảo của tiến trình. Thay vì thực sự đưa file thực thi vào bộ nhớ vật lý, file này chỉ đơn
giản được liên kết với không gian địa chỉ ảo của tiến trình. Sau đó, dữ liệu của chương
trình được mang vào bộ nhớ như một phần của chương trình được tham chiếu bởi ứng
dụng đang chạy. Quá trình liên kết một file vào không gian địa chỉ ảo của tiến trình được
gọi là ánh xạ bộ nhớ.
Mỗi không gian địa chỉ ảo của tiến trình được mô tả bằng một cấu trúc dữ liệu được gọi
là mm_struct. Cấu trúc này chứa thông tin về file thực thi hiện đang được thực thi và các
con trỏ đến một số cấu trúc dữ liệu vm_area_struct. Mỗi vm_area_struct mô tả một phần
của không gian bộ nhớ ảo, bao gồm điểm bắt đầu và kết thúc của vùng bộ nhớ, quyền truy
cập của tiến trình đến bộ nhớ đó, và các hoạt động liên quan đến bộ nhớ đó.
Các hoạt động này bao gồm việc xử lý khi một tiến trình cố gắng truy cập vào bộ nhớ ảo
mà không có giá trị tương ứng trong bộ nhớ vật lý (qua lỗi trang), được gọi là hoạt động
nopage. Hoạt động nopage được sử dụng khi Linux cần các trang của một file thực thi trong bộ nhớ.
Khi một file thực thi được ánh xạ vào không gian địa chỉ ảo của tiến trình, một tập hợp
các cấu trúc dữ liệu vm_area_struct được tạo ra. Mỗi vm_area_struct mô tả một phần của
file thực thi, bao gồm mã thực thi, dữ liệu được khởi tạo (các biến), và dữ liệu không được
khởi tạo. Linux cũng hỗ trợ một số hoạt động bộ nhớ ảo chuẩn khác.
2.1.1.1.Cấp phát vùng nhớ
*Cấp phát vùng nhớ giản đơn
Vùng nhớ giản đơn là vùng nhớ có kích thước nhỏ hơn kích thước của bộ nhớ vật lý. Để
cấp phát vùng nhớ cho tiến trình, chúng ta sử dụng hàm malloc() của thư viện C chuẩn. void *malloc(size_t size); Trong đó:
size: là kích thước vùng nhớ muốn cấp phát, có kiểu size_t, một kiểu nguyên không dấu.
Không giống như trên DOS và Windows, khi sử dụng hàm này trong Linux, chúng ta
không cần phải khai báo thư viện malloc.h.
*Cấp phát vùng nhớ lớn
Vùng nhớ lớn có thể có kích thước vượt qua kích thước thực tế của bộ nhớ vật lý. Trong
quá trình yêu cầu cấp phát, vì chưa biết hệ thống có chấp nhận việc cung cấp bộ nhớ lớn
hay không, nên có thể hệ thống sẽ ngắt ngang yêu cầu nếu bộ nhớ đã cạn kiệt.
*Vùng nhớ được bảo vệ
Mặc dù mỗi tiến trình được cung cấp 4 GB không gian địa chỉ, nhưng tiến trình không thể
tự do đọc hoặc ghi dữ liệu vào bất kỳ vùng nhớ nào nếu không được hệ điều hành cấp
phát trước. Bạn chỉ có thể sao chép dữ liệu vào vùng nhớ mà hệ điều hành đã cấp phát
thông qua hàm malloc(). Nếu tiến trình cố gắng đọc hoặc ghi dữ liệu vào một vùng nhớ
mà chưa được cấp phát, hệ điều hành sẽ chấm dứt chương trình với lỗi truy cập không
hợp lệ, gọi là lỗi phân đoạn (segmentation fault), ngay cả khi vùng nhớ đó vẫn nằm trong
không gian địa chỉ 4 GB.
*Một số hàm cấp phát vùng nhớ khác - Hàm calloc()
Hàm calloc() không phổ biến như hàm malloc(). Nó cũng được sử dụng để cấp phát vùng
nhớ mới, nhưng được thiết kế để phân bổ vùng nhớ cho một mảng cấu trúc.
void *calloc(size_t nmemb, size_t size);
Hàm này có các tham số phức tạp hơn hàm malloc():
Tham số nmemb là số lượng phần tử của mảng (tức là số ô trong bảng).
Tham số size chỉ định kích thước của mỗi phần tử trong mảng (kích thước của mỗi ô trong bảng).
Vùng nhớ được cấp phát bởi hàm calloc() được khởi tạo với giá trị zero. Nếu thành công,
giá trị trả về của hàm là con trỏ trỏ đến phần tử đầu tiên của mảng, ngược lại hàm trả về
giá trị NULL. Tương tự như hàm malloc(), mỗi lần gọi tiếp theo, calloc() không đảm bảo
sẽ cung cấp cho chương trình vùng nhớ liên tục với lời gọi trước đó. - Hàm realloc()
Hàm realloc() cũng không phổ biến. Hàm này được sử dụng để thay đổi kích thước của
một khối nhớ đã được cấp phát trước đó.
void *realloc(void *ptr, size_t size);
Hàm này nhận con trỏ ptr trỏ đến vùng nhớ đã được cấp phát bởi các hàm cấp phát như
malloc(), calloc(), hoặc realloc() trước đó. Sau đó, hàm thực hiện co giãn hoặc thu nhỏ
khối nhớ theo kích thước size chỉ định. Hàm đảm bảo rằng vùng nhớ mới đạt được kích
thước yêu cầu.Nếu thành công, giá trị trả về của hàm là con trỏ trỏ đến vùng nhớ mới đã
thay đổi kích thước. Một điều cần lưu ý là trong chương trình, nên sử dụng một con trỏ
khác để nhận kết quả trả về từ hàm realloc(), không bao giờ sử dụng lại con trỏ đã chuyển
cho realloc() trước đó. Ngược lại, nếu vùng nhớ không thể thay đổi kích thước như yêu
cầu, hàm sẽ trả về con trỏ NULL. Do đó, nếu gán giá trị trả về của hàm realloc() cho một
con trỏ đã được sử dụng, khi hàm không thành công, con trỏ đó sẽ mất điểm đến vùng nhớ trước đó.
*Giải phóng vùng nhớ
Đối với các tiến trình chỉ yêu cầu cấp phát vùng nhớ nhỏ và sử dụng chúng trong một thời
gian ngắn trước khi kết thúc, hệ điều hành sẽ tự động giải phóng vùng nhớ khi tiến trình
kết thúc. Tuy nhiên, hầu hết các tiến trình đều có nhu cầu cấp phát vùng nhớ động khá lớn.
Trong trường hợp này, hệ điều hành không tự động giải phóng vùng nhớ, do đó chương
trình luôn cần phải tự giải phóng vùng nhớ khi không cần thiết nữa bằng cách sử dụng hàm free().
Khi sử dụng hàm free() từ thư viện stdlib.h:
Tham số ptr_to_memory là con trỏ trỏ đến vùng nhớ cần giải phóng. Thường thì con trỏ
này được trả về bởi các hàm cấp phát như malloc(), calloc(), hoặc realloc().
*Truy xuất con trỏ NULL
Trong Linux, việc đọc hoặc ghi trên con trỏ NULL được bảo vệ một cách nghiêm ngặt.
Bởi vì Linux sử dụng thư viện chuẩn GNU (bao gồm các hàm như sprintf và printf), việc
đọc con trỏ NULL là được phép. Thư viện GNU đơn giản trả về chuỗi "null" khi bạn cố
gắng đọc con trỏ NULL. Tuy nhiên, việc ghi vào con trỏ NULL là không được phép và sẽ gây ra lỗi.
CHƯƠNG III: CƠ CHẾ PHÂN TRANG VÀ PHÂN ĐOẠN
3.Cơ chế phân trang
Phân trang là một cơ chế quản lý bộ nhớ trong đó không gian bộ nhớ ảo của mỗi tiến trình
được chia thành các phần nhỏ gọi là "trang" (pages). Mỗi trang có kích thước cố định và
được quản lý bằng bảng trang (page table), mỗi mục trong bảng trang ánh xạ một địa chỉ
ảo đến một địa chỉ vật lý trong bộ nhớ RAM. Khi một tiến trình cần truy cập vào một
trang bộ nhớ, hệ thống sẽ kiểm tra xem trang đó có được nạp vào bộ nhớ vật lý hay không.
Nếu không, trang sẽ được sao chép từ đĩa vào bộ nhớ RAM trước khi tiến trình có thể sử
dụng nó. Phân trang giúp tối ưu hóa việc sử dụng bộ nhớ bằng cách cho phép hệ thống
chỉ nạp vào bộ nhớ những trang cần thiết, giảm bớt lãng phí bộ nhớ và tăng hiệu suất của hệ thống.
Vì có quá ít bộ nhớ vật lý so với bộ nhớ ảo nên HĐH phải chú trọng làm sao để không
lãng phí bộ nhớ vật lý. Một cách để tiết kiệm bộ nhớ vật lý là chỉ load những trang ảo mà
hiện đang được sử dụng bởi một chương trình đang thực thi. Ví dụ, một chương trình cơ
sở dữ liệu thực hiện một truy vấn vào cơ sở dữ liệu. Trong trường hợp này không phải
toàn bộ cơ sở dữ liệu được load vào bộ nhớ mà chỉ load những bản ghi có liên quan. Việc
mà chỉ load những trang ảo vào bộ nhớ khi chúng được truy cập dẫn đến nhu cầu về phân trang.
*Trang Lưu Trữ (Page Cache)
Cache là tầng nằm giữa phần quản lý bộ nhớ kernel và phần vào ra của đĩa. Các trang mà
kernel hoán đổi không được ghi trực tiếp lên đĩa mà được ghi vào cache. Khi cần vùng
nhớ trống thì kernel mới ghi các trang từ cache ra đĩa. Đặc tính chung của các trang trong
danh sách trang theo chuẩn LRU(Least Recently Used : ít sử dụng thường xuyên nhất) là :
- active_list : là những trang có page -> age > 0, chứa hoặc không chứa dữ liệu, và có thể
được ánh xạ bởi một mục trong bảng trang tiến trình. inactive_dirty_list : là những trang
có page -> age == 0, chứa hoặc không chứa dữ liệu, và không được ánh xạ bởi bất kì một
mục nào trong bảng trang tiến trình. inactive_clean_list : mỗi vùng có inactive_dirty_list
của riêng nó, chứa các trang clean với age == 0, và không được ánh xạ bởi bất kì một mục
nào trong bảng trang tiến trình. Trong khi quản lý lỗi trang, kernel sẽ tìm kiếm các trang
lỗi trong page cache. Nếu lỗi được tìm thấy thì nó được đưa đến active_list để đưa ra thông báo.
Quá trình quản lý page cache bao gồm các bước sau:
-Ghi Trang (Page): Các trang ghi bởi các tiến trình sẽ được ghi vào page cache và vẫn còn trên active_list.
-Trang Không Sử Dụng: Các trang không được sử dụng sẽ được gán một biến đếm tuổi
và chuyển từ active_list sang inactive_dirty list.Swap Out: Khi bộ nhớ đầy, các trang có
tuổi = 0 sẽ bị xóa từ bảng trang của tiến trình và chuyển sang inactive_dirty list để giải phóng bộ nhớ.
-Re-Fill Inactive Scan: Tiến trình sẽ quét các trang có thể chuyển sang inactive_dirty list.
-Truy Cập Trang Không Sử Dụng: Khi tiến trình truy cập vào trang không sử dụng, hàm
find_page_nolock() sẽ tìm trang trong page cache và phục hồi từ inactive_dirty list sang
active_list.Page Launder: Hàm này được kích hoạt để làm sạch các trang dirty và chuyển
các trang không sử dụng và sạch sang inactive_clean list.
Quá trình này tùy thuộc vào nhu cầu sử dụng bộ nhớ và có thể mất nhiều thời gian khi bộ
nhớ đầy, dẫn đến hiệu suất giảm.
*Vòng đời của một User Page
- Đọc Trang từ Đĩa Vào Bộ Nhớ:
Trang P được đọc từ đĩa vào bộ nhớ và lưu vào page cache.
Có ba trường hợp xảy ra khi đọc trang P:
Trong trường hợp của tiến trình A, khi truy cập trang P, nó được lưu vào page cache và
bảng trang của tiến trình.
Trong trường hợp của đầu đọc hoán đổi, trang P được đọc và lưu vào page cache là một
phần của cluster trên đĩa.
Trong trường hợp của đầu đọc cluster ánh xạ bộ nhớ, một chuỗi các trang liên tiếp tiếp
sau trang lỗi được đọc và lưu vào page cache.
- Trang Được Sử Dụng và Ghi:
Nếu trang P được ghi bởi tiến trình, nó trở thành "dirty" và vẫn nằm trong active_list.
- Trang Không Được Sử Dụng:
Nếu trang P không được sử dụng trong một khoảng thời gian, tuổi của nó sẽ giảm dần xuống 0.
Khi bộ nhớ đầy, hàm swap_out sẽ được gọi để loại bỏ trang P và các mục trong bảng trang của tiến trình.
- Xử Lý Khi Bộ Nhớ Đầy:
Nếu bộ nhớ đầy, hàm swap_out sẽ cố gắng lấy lại các trang từ không gian địa chỉ ảo của tiến trình A.
Trang P sẽ được loại bỏ khỏi bảng trang và có thể ghi ra đĩa.
- Xử Lý Thời Gian:
Thời gian xử lý trang phụ thuộc vào nhu cầu sử dụng bộ nhớ và hoạt động của hàm kswapd().
- Chuyển Trang Sang Inactive List:
Trang P được chuyển từ active_list sang inactive_dirty list khi không còn được sử dụng.
- Truy Cập Trang Bị Loại Bỏ:
Khi truy cập trang P, nó được phục hồi từ page cache và trở lại active_list.
- Xử Lý Khi Bộ Nhớ Đầy:
Việc xử lý khi bộ nhớ đầy có thể dẫn đến việc loại bỏ trang P và làm chậm bộ nhớ.
- Làm Sạch Trang Dirty:
Hàm page_launder() được kích hoạt để ghi các trang dirty ra đĩa và chuyển chúng sang inactive_clean_list.
Thông qua quá trình này, Linux duy trì vòng đời của các trang trong hệ thống bộ nhớ của
mình, giúp tối ưu hóa việc sử dụng và quản lý bộ nhớ. *Bảng trang
Mỗi bảng trang chứa số khung trang của bảng trang ở mức tiếp theo. Mỗi trường cung cấp
một địa chỉ offset đến một bảng trang cụ thể. Để chuyển địa chỉ ảo thành địa chỉ vật lý, bộ
xử lý phải lấy nội dung của các trường rồi chuyển thành địa chỉ offset đến trang vật lý
chứa bảng trang và đọc số khung trang của bảng trang ở mức tiếp theo. Việc này lặp lại
cho đến khi số khung trang của trang vật lý chứa địa chỉ ảo được tìm ra. Bây giờ, trường
cuối cùng trong địa chỉ ảo được sử dụng để tìm dữ liệu trong trang. Mỗi nền mà Linux
chạy trên đó phải cung cấp sự chuyển đổi các macro cho phép kernel có thể hiểu được các
bảng trang tương ứng trên nền đó. Do đó, kernel không cần biết định dạng của các mục
trong bảng trang cũng như cách sắp xếp của nó. Điều này giúp cho Linux thành công trong
việc sử dụng cùng một đoạn mã để xử lý các bảng trang đối với bộ xử lý Alpha ( có 3 mức
bảng trang ) và đối với bộ xử lý Intel x86 ( có 2 mức bảng trang ).
Định vị và giải phóng trang
Trong hệ thống, việc định vị và giải phóng trang vật lý là rất quan trọng để quản lý bộ nhớ
ảo hiệu quả. Một số nhu cầu cụ thể bao gồm việc load ảnh vào bộ nhớ và sau đó giải phóng
chúng khi đã xử lý xong, cũng như lưu trữ các cấu trúc dữ liệu kernel.
Tất cả các trang vật lý trong hệ thống được mô tả bởi cấu trúc dữ liệu mem_map, đó là
một danh sách các cấu trúc mem_map_t được khởi tạo khi hệ thống khởi động. Mỗi
mem_map_t mô tả một trang vật lý trong hệ thống và có các trường quan trọng sau:
Count: Số lượng người sử dụng của trang. Nếu count > 1, trang đang được chia sẻ bởi nhiều tiến trình.
Age: Mô tả "tuổi" của trang và quyết định liệu trang có bị loại bỏ hay hoán đổi không.
Map_nr: Số khung trang vật lý mà mem_map_t này mô tả.
Cơ chế và cấu trúc dữ liệu sử dụng để định vị và giải phóng trang được hỗ trợ bởi vector
free_area. Mỗi phần tử của free_area chứa thông tin về các khối của trang, với các khối
có kích thước tăng dần theo lũy thừa 2. Các khối trống của trang được xếp ở đây và các
bit của bitmap được sử dụng để theo dõi trạng thái của các khối. *Định vị trang
Trong Linux, thuật toán Buddy được sử dụng để định vị và giải phóng các khối trang một
cách hiệu quả. Thuật toán này cho phép định vị các khối có kích thước là lũy thừa của 2,
nghĩa là có thể định vị các khối gồm 1 trang, 2 trang, 4 trang,...
Khi có yêu cầu cấp phát, đoạn mã định vị sẽ tìm trong `free_area` một khối các trang có
kích thước như yêu cầu. Mỗi phần tử của `free_area` ánh xạ đến các khối trang trống có
kích thước tương ứng. Ví dụ, phần tử thứ 2 của mảng ánh xạ đến các khối gồm 4 trang
trống đã được định vị.