Trường Đại Học Bách Khoa Hà Nội Viện Điện Tử Viễn Thông ====o0o==== BÁO CÁO BÀI TẬP DÀI MÔN HỆ ĐIỀU HÀNH Bài tập số 2: Chương trình sửa mã nguồn hệ điều hành Linux báo cáo dung lượng Ram và thu hồi bộ nhớ theo cơ chế slab cache Giáo viên hướng dẫn : PHẠM VĂN TIẾN Sinh viên thực hiện : Lương Kim Doanh Ngô Quang Thìn Trần Hoàng Điệp Nguyễn Trung Thành Lớp : KSTNĐTVTK52 HÀ NỘI 102011 Mục Lục: I.Giới thiệu 3 1.Giới thiệu chung 3 2. Giới thiệu slab cache 4 3. Object: 17 3.1. Khởi tạo object trong slab: 17 3.2. Cấp phát object: 17 3.3. Giải phóng object: 17 4. PerCPU object cache: 18 4.1. Cho phép CPU cache: 18 4.2. Cập nhật thông tin mỗi CPU: 18 4.3. Drainning a PerCPU cache: 19 4.4. Khởi tạo Slab allocator: 19 5. Giao tiếp với Buddy allocator: 19 III. Triển khai. 20 1. Giới thiệu thư mụcprocsys. 20 2. Linux Modules 22 2.1 Xây dựng Module 22 2.2 Exported Symbols 24 IV. Các module mã nguồn chỉnh sửa và thêm 29 1. Mục đích 29 2. Chi tiết 29 2.1. File kernel_sysctl.c 29 2.2. Sửa file sysctl.c 31 2.3. File KSTNDTVTK52.c 32 2.4. Sửa file Makefile 32 V. Kiểm thử và kết quả 32 1. Hiển thị 32 2.Test module 35 I.Giới thiệu 1.Giới thiệu chung Mục đích của việc quản lí bộ nhớ là cung cấp một phương thức trong đó bộ nhớ có thể được chia sẻ động giữa những người sử dụng, những tiến trình khác nhau với những mục đích khác nhau. Phương thức quản lí bộ nhớ sẽ thực hiện hai nhiệm vụ: Tối thiểu lượng thời gian yêu cầu quản lí bộ nhớ
Trang 1
Trường Đại Học Bách Khoa Hà Nội
Viện Điện Tử Viễn Thông
====o0o====
BÁO CÁO BÀI TẬP DÀI MÔN HỆ
ĐIỀU HÀNH
Bài tập số 2:
Chương trình sửa mã nguồn hệ điều hành Linux báo cáo dung lượng
Ram và thu hồi bộ nhớ theo cơ chế slab cache
Giáo viên hướng dẫn : PHẠM VĂN TIẾN Sinh viên thực hiện : Lương Kim Doanh
Trang 2
Mục Lục: I.Giới thiệu 3
1.Giới thiệu chung 3
2 Giới thiệu slab cache 4
3 Object: 17
3.1 Khởi tạo object trong slab: 17
3.2 Cấp phát object: 17
3.3 Giải phóng object: 17
4 Per-CPU object cache: 18
4.1 Cho phép CPU cache: 18
4.2 Cập nhật thông tin mỗi CPU: 18
4.3 Drainning a Per-CPU cache: 19
4.4 Khởi tạo Slab allocator: 19
5 Giao tiếp với Buddy allocator: 19
III Triển khai 20
1 Giới thiệu thư mục/proc/sys 20
2 Linux Modules 22
2.1 Xây dựng Module 22
2.2 Exported Symbols 24
IV Các module mã nguồn chỉnh sửa và thêm 29
1 Mục đích 29
2 Chi tiết 29
2.1 File kernel_sysctl.c 29
2.2 Sửa file sysctl.c 31
2.3 File KSTN-DTVT-K52.c 32
2.4 Sửa file Makefile 32
V Kiểm thử và kết quả 32
1 Hiển thị 32
2.Test module 35
Trang 3
I.Giới thiệu
1.Giới thiệu chung
Mục đích của việc quản lí bộ nhớ là cung cấp một phương thức trong đó bộ nhớ có thể được chia sẻ động giữa những người sử dụng, những tiến trình khác nhau với những mục đích khác nhau Phương thức quản lí bộ nhớ sẽ thực hiện hai nhiệm vụ:
- Tối thiểu lượng thời gian yêu cầu quản lí bộ nhớ
- Lượng bộ nhớ sử dụng sẵn có là lớn nhất (tối thiểu lượng nhớ tiêu tốn cho quản lí)Việc quản lí bộ nhớ lý tưởng là thực hiện tốt cả hai nhiệm vụ trên, nhưng thực tế không
dễ dàng thực hiện chúng cùng một lúc và chùng thường có xu hướng mâu thuẫn với nhau.Chúng ta có thể phát triển một thuật toán mà sử dụng ít bộ nhớ nhưng sẽ phải mất nhiều thời gian để quản lí bộ nhớ sẵn có Chùng ta cũng có thể phát triển một thuật toán mà quản lí bộ nhớ một cách hiệu quả về thời gian nhưng sẽ tốn nhiều tài nguyên hệ thống hơn Kết cục một ứng dụng cụ thể chỉ ưu tiên chọn một trong hai xu hướng phú hợp nhất
Việc quản lí bộ nhớ trước đây dựa vào chiến lược cấp phát dựa vào heap Trong phương thức này một khối bộ nhớ lớn (người ta gọi la heap) được sử dụng để cấp bộ nhớ cho mục đích của người sử dụng Khi người sử dụng cần một khối bộ nhớ, họ yêu cầu heap manager tìm kiếm lượng bộ nhớ thoả mãn và trả về khối tìm được Một vài thuật toán được sử dụng để tìm kiếm là first-fit (khối đấu tiên tìm được trong heap thỏa mãn yêu cầu) Khi người sử dụng hoàn tất công việc, khối bộ nhớ đươc trả lại cho heap
Vấn đề chủ yếu phát sinh với chiến lược cấp phát dựa vào heap này là sự phân mảnh (fragmentation) Khi khối bộ nhớ được cấp phát, chúng sau đó được trả lại theo thứ
tự khác nhau vào những thời điểm khác nhau Điều này dẫn đến việc để lại những chỗ trống trong heap, yêu cầu nhiếu thời gian hơn để quản lí bộ nhớ rỗi Thuật toán này hường đến việc quản lí tài nguyên bộ nhớ một cách hiệu quả nhưng yêu cầu thời gian để quản lí heap
Một phương pháp khác là cấp phát bộ nhớ theo kiểu bè bạn (buddy memory allocation), là kĩ thuất cấp phát nhanh hơn mà chia bộ nhó thành các phân vùng
(partition) mũ cơ số 2 và thử cấp phát bộ nhớ yêu cấu sử dụng phương pháp best-fit Khi
bộ nhớ cấp phát cho ngưới sử dụng được giải phóng, khối buddy được kiểm tra để xem
Trang 4
liệu bất kì hàng xóm nào của nó đang rỗi hay không Nếu có, những khối nhớ này được hợp lại để giảm những lãng phí bộ nhớ do phương pháp best-fit
2 Giới thiệu slab cache
Bộ cấp phát slab được sử dụng trong Linux dựa vào thuật toán được giới thiệu bởiJeff Bonwick cho hệ điều hành SunOS Giải pháp cấp phát của Jeff Bonwick xoay quanh việc đệm đối tượng (object caching) Bên trong kernel, một lượng đáng kể bộ nhớ được cấp phát cho một tập hữu hạn các đối tượng như miêu tả tập tin ( file descriptor) và các cấu trúc thông dụng khác Jeff nhận thấy rằng thời gian cần thiết để khởi tạo một object thông thường trong kernel vượt quá thời gian cần thiết để cấp phát và giải phóng nó Jeff đưa ra kết luận rằng thay vì giải phóng bộ nhớ trả lại global pool, ta nên để bộ nhớ duy trìtrạng thái khởi tạo cho mục đích sử dụng nó Ví dụ, nếu bộ nhớ được cấp phát cho mutex.Những lần cấp phát sau của bộ nhớ không càn thực hiện khởi tạo vì nó đã ở trạng thái mong muốn từ lần giải cấp phát trước (deallocation) và gọi đến hàm hủy (deconstructor)
Bộ cấp phát slab sử dụng ý tưởng này và nhiều ý tưởng khác để xây dựng một bộ cấp phát hiệu quả về không gian và thời gian
Slab allocator chứa một số cache mà liên kết với nhau bằng danh sách liên kết vòng đôi (doubly linked circular list) gọi là cache main Một cahe, trong ngữ cảnh slab allocator, là một
Trang 5- Cấp phát những khối bộ nhớ nhỏ để giúp loại bỏ phân mảnh nội gây ra bởi hệ thống buddy
- Đệm những đối tượng thường sử dụng sao cho hệ thống phải tốn thời gian khởi tạo, cấp phát, hủy đối tượng
- Sắp xếp những object trong cache L1 và L2 để sử dụng hardware cache tốt hơn
II: Nội dung
1.Cache:
Mỗi cache tồn tại cho 1 kiểu object mà được đệm Danh sách đầy đủ những cache hiện cólúc hệ thống đang chạy có thể được liệt kê sử dụng lệnh: cat/proc/slabinfo
Trang 6
Mỗi cột tương ứng với 1 trường của cấu trúc kmem_cache:
name: tên cache mà người sử dụng có thể đọc được VD:TCPv6
active_objs: số lượng object đang sử dụng
num_objs: tổng số lượng object có thể sử dụng được bao gồm cả những object chưa được sử dụng
objsize: kích thước của object trong cache
active_slabs : số lượng slab đang được sử dụng của cache
num_clabs: số lượng slab cấp phát cho cache
Nếu SMP được enable thì thêm 2 trường nữa được thêm vào:
- limit: Số lượng free object mà pool (object pools) có thể có trước khi một nửa được trả lại cho global pool
- batchcount: là số lượng object được cấp phát cho CPU khi không có object free
Trang 7
Để tăng tốc cấp phát và giải phóng object và slab , chúng được chia a thành ba danh sách : slabs_full, slabs_partial và slab_free Tất cả các object trong slabs_full đã được sử dụng, slabs_partial có object, vì vậy được dung chủ yếu cho việc cấp phát đối tượng, slabs_free không
có đối tượng nào được cấp phát, vì vậy được dung chủ yếu cho việc hủy slab
1.1.Cache Descriptor:
Tất cả thông tin về 1 cache được lưu trong cấu trúc kmem_cache được khai báo trong Include/linux/slub_def.h (nếu kernel sử dụng SLUB thì cấu trúc này nằm trong file include/linux/slub_def.h)
1.2.Những cờ static của cache:
SLUB_HWCACHE_ALIGN: đặt object vào L1 CPU cache
SLUB_CACHE_DMA: cấp phát slab từ vùng nhớ ZONE_DMA
Nếu tùy chọn CÒNIG_SLAB_DEBUG được set sẽ có thêm 1 số tùy chọn:
SLAB_DEBUG_FREE: khi thi hành việc kiểm tra object free
SLAB_RED_ZONE: đánh dấu object đầu và object cuối và bẫy tràn
SLAB_POISON : “nhiễm độc” object làm cho nó không được cấp phát hay khởi tạo
Để ngăn việc sử dụng sai các cờ, một CREATE_MASK được tạo ra trong mm/slab.c chứa tất cả các cở được cho phép Khi một cache được tạo ra, các cờ yêu cầu được so sánh với CREATE_MASK và được coi như là một lỗi nếu sử dụng sai cờ
1.3 Cache coloring:
Để sử dụng hardware cache tốt hơn, slab allocator sẽ dịch (offset) object trong các slab khác nhau các khoảng khác nhau phụ thuộc vào khoảng trống còn lại trong slab Độ dịch được tính theo đơn vị BYTES_PER_WORD nếu SLAB_HWCACHE_ALIGN không được set, được tính theo L1_CACHE_BYTES nếu SLAB_HWCACHE_ALIGN được set
Sau quá trình tạo cache, object trong slab được cấp phát, phần dư được sử dụng để dịch các object (cache coloring) Hai số được khai báo trong cấu trúc kmem_cache liên quan đến việc dịch object:
Colour:số lần dịch có thể đối với một bộ dịch
Colour_off: độ dịch
Ví dụ, để thuận tiện, giả sử s_mem ( địa chỉ object đầu tiên) trên slab là 0 và có 100 byte
dư trên lab và độ dịch là 32 byte trên L1 cache trên một Pentium II Trong kịch bản này,
Trang 8
object của slab đầu tiên đựoc tạo ra bắt đầu từ 0 Object của slab thứ 2 sẽ bắt đầu ở byte 32, object của slab thứ 3 sẽ bắt đầu ở byte 64, object của slab thứ 4sẽ bắt đầu ở byte 96, và object của slab thứ 5sẽ bắt đầu ở byte 0 Với cách dịch như vậy, các object từ mỗi slab sẽ không bị cache hit trên cùng 1 line Giá trị của colour =3, colour_off= 32
1.4.Tạo cache:
Hàm kem_cache_create () có nhiệm vụ tạo cache mới và thêm nó vào cache chain
Công việc tạo cache được thực hiện như sau:
Thi hành việc kiểm tra cơ bản cho việc sử dụng sai
Thực hiện kiểm tra debug nếu CONFIG_SLAB_DEBUG được set
Cấp phát một kmem_cache từ cache_cache slab cache
Chỉnh kích cỡ object đến kích cỡ word
Tính toán xem có bao nhiêu object sẽ điền đủ một slab
Chỉnh kích cỡ đối tượng theo L1
Tính toán color
Khởi tạo những trường hợp còn lại của cache descriptor
Thêm cache mới vào cache chain
1.5 Cache reaping :
Trang 91.6 Cache shrinking :
Khi một cache được chọn để shrink chính nó, những bước sau được thực hiện :
delete hết tất cả các object trong mỗi CPU cache
delete tất cả các slab từ slabs_free nếu cờ grow không được set
Trang 10
Kmem_cache_shrink() xóa tất cả các slab từ slabs_free và trả lại số page được giải phóng , hàm
cơ sở này được export cho những người sử dụng slab allocator
Trang 11
Hàm thứ hai,_kmem_cache_shrink() giải phóng tất cả các slab từ slabs_free và sau đó kiểm tra lại xem slabs_partial và slabs_full có rỗng khống Hàm này chỉ được sử dụng bên trong module và rất quan trọng khi hủy cache, nó không quan tâm số page được giải phóng chỉ miễn làcache rỗng
1.7 Cache Destroying:
Khi 1 mode unload, nó có trách nhiệm phải xóa bỏ bất kì cache nào với hàm
kmem_cache_destroy() Mã nhân kennel không chủ động destroy cache của nó bởi vì sự tồn tại của chúng là cần thiết cho hệ thống Những bước được thực hiện để xóa bỏ cache:
Delete cache từ cache chain
Shrink cache để delete tất cả các slab
giải phogns bất kì cache của mỗi CPU (kfree())
Delete cache descriptor từ cache_cache
1.8: Kích cỡ cache:
Linux duy trì hai tập hợp cache dùng cho việc cấp phát lượng bộ nhớ nhỏ mà bình thườngcấp phát page không phù hợp.Một tập hợp được sử dụng cho DMA, và tập hợp còn lại phù hợp với sử dụng thông thường Tên mà người sử dụng có thể đọc được là cache cỡ -N và cache cỡ -N(DMA) Thông tin về kích cỡ cache được lưu trong cấu trúc cache_sizes
Cs_size: kích cỡ của khối bộ nhớ
Cs_cachep: cache của các khối dùng cho việc sử dụng bộ nhớ thông thường
Trang 12
Cs dmacachep: cache của các khối dùng cho việc sử dụng DMA
Bời vì số lượng các cache trong hệ thống là giới hạn, một dãy kích cỡ cache tĩnh được khởi tạo tại thời điểm biên dịch, bắt đầu với 32 bytes trên máy dùng page có kích cỡ 4KB và 64KB với kích cỡ trang lớn hơn:
Trang 13
truct list_head list;
unsigned long colouroff;
void *s_mem; /* bao gồm colour offset*/
unsigned int inuse; /* số lượng object đang được sử dụng trong slab */
kmem_bufctl_t free
unsigned short nodeid;}
list : đây là danh sách liên kết của slab (slabs_free, slabs_partial, slabs_full)
colouroff: độ dịch từ địa chỉ cơ sở của object đầu tiên trong slab
free: đây là 1 dãy bufctl được sử dụng để lưu vị trí của free object
Cấu trúc quản lý slab được giữ trong ( khi cờ CFLGS_OFF_SLAB được clear) hoặc nằm ngòai slab
Trang 14
2.1 Lưu trữ slab Descriptor:
Nếu kích cỡ object lớn hơn 1 ngưỡng ( 512 byte đối với x86), CFLGS_OFF_SLAB được set và slab descriptor được giữ ngòai slab ở 1 trong những kích cỡ cache Kích cỡ cache là đủ lớn để chứa cấu trúc slab, và dãy kmem_bufctl_t Cách khác là lưu trữ cấu trúc slab và dãy
kmem_bufclt_t trong slab Dãy kmem_bufctl_t giữ chỉ số của những object chưa được cấp phát, sẵn sàng được sử dụng khi cần thiết
Trang 15
Những tác vụ được thực hiện để tạo slab như sau :
.Thực thi những kiểm tra cơ sở để đảm bảo không có sử dụng sai
Tính toán color offset cho object trong slab
Cấp phát bộ nhớ cho slab và slab descriptor
Liên kết những page được sử dụng cho slab đến slab và cache descriptor
.Khởi tạo object trong slab
.Thêm slab đến cache
2.3 Tracking free object:
Trang 16
Slab allocator có 1 phương pháp đơn giản và nhanh chóng xác định chỗ của object rỗi Object thuộc về slab và cache được xác định bằng cấu truc page và dãy kmem_bufctl_t, một dãy
số nguyên các chỉ số của object Số phần tử này bằng số object trong slab
typedef unsigned int kmem_bufctl_t;
Bởi vì dãy này đứng sau slab descriptor và không có con trỏ trực tiếp đến phần tử đầu tiên một cách trực tiếp, một macro trợ giúp được khai báo
static inline kmem_bufctl_t *slab_bufctl(struct slab *slabp)
{
return (kmem_bufctl_t*)(slabp+1);
}
Khi cấp phát một object, kmem_cache_alloc() thực hiện công việc câp nhật dãy
kmem_bufctl () Trường slab free giữ chỉ số của free object đầu tiên, chỉ số free object tiếp theo là kmem_bufctl_t [slabfree] diễn giải như sau:
objp= slabp->_mem+ slabp->free*cachep-> objsize;
slabp->free= slab_bufctl (slabp) [slabp-> free];
2.4 Tính toán số object trong 1 Slab:
Trong quá trình tạo cache, hàm cache_ estimate() được gọi để tính xem có bao nhiêu object được lưu trên 1 slab, tính trong 2 trường hợp slab descriptor được lưu trong hay ngoài slab
và kích cỡ của kmem_bufctl_t cần được track nếu 1 object là rỗi hay không Nó trả lại số object
có thể chứa được và số byte lãng phí Số byte lãng phí có ý nghĩa quan trọng nếu cache coloring được sử dụng Tính toán này được thực hiện theo những bước sau:
.Khởi tạo số byte lãng phí bằng tổng kích cỡ slab, nghĩa là PAGE_SIZEgfporder
Trừ đi lượng không gian yêu cầu để lưu slab descriptor
.Đếm số lượng object có thể được lưu bao gồm kích thước của kmem_bufctl_t nếu slab
descriptor được lưu ở trong slab Tiếp tục tăng kích cỡ cho tới khi slab được điền đầy
Trả lại số object và số byte lãng phí
2.5 Slab Destroying:
Trang 17
Khi một cache bị shring hay destroy, slab bị delete Bởi vị object có thể có hàm hủy (destructor) hàm này phải được gọi Các công việc để delete một slab như sau:
Nếu có thể, gọi hàm destructor cho mọi object trong slab
Nếu debug được enable , kiểm tra vết red_zone và poison
Giải phóng page của slab sử dụng
Trang 18
3 Object:
3.1 Khởi tạo object trong slab:
Khi slab được tạo ra, tất cả object bên trog nó được đặt ở trong một trạng thái khởi tạo Nếu hàm khởi tạo (constructor) sử dụng được, nó được gọi cho mỗi object và người ta mong muốn object được giữ ở trạng thái khởi tạo lúc giải phóng
để tạo ra slab mới trong danh sách slabs_free Bước cuối cùng là cấp phát object từ slab đã chọn
Trong trường hợp SMP, ta thực hiện thêm một bước nữa Trước khi cấp phát một object, slab allocator sẽ kiểm tra xem nếu có một object có thể sử dụng tu per-CPU cache và sẽ sử dụng nếu có Nếu không có, nó sẽ cấp phát một số batchcount các object trong một khối và đặt chúng vào per-CPU cache của nó
3.3 Giải phóng object:
Kmem cache free() được sử dụng để giải phóng object và tác vụ của nó tương đối đơn giản Giống như kmem_cache_alloc(), hoạt động của kmem_cache_free() khác nhau trong hai trường hợp UP và SMP Sự khác nhau cơ bản giữa hai trường hợp là ở chỗ, trong trường hợp của
UP, object được trả lại trực tiếp cho slab nhưng trong trường hợp của SMP, object được trả lại cho per-CPU cache Trong cả hai trường hợp, hàm hủy (destructor) cho object sẽ được gọi nếu
có thể Hàm hủy có nhiệm vụ trả object về trạng thái khởi tạo
4 Per-CPU object cache:
Một trong những tác vụ mà slab allocator đảm nhiệm là tăng hiệu suất sử dụng cache cứng Mục đích của việc tính toán hiêụ năng cao nhìn chung là sử dụng dữ liệu trên cùng một CPU lâu nhất có thể Linux thưc hiên điều này bằng cách cố gắng giữ object trong cùng một CPU cache với per-CPU object cache, đơn giản được gọi là cpu cache cho mỗi cpu trên hệ thống Khi cấp phát hay giải phóng object, chúng được đặt ở cpu cache Khi không có object nàođược giải phóng, một nhóm object được đặt vào trong pool Khi pool trở nên quá lớn, một nửa bịloại và đặt vào global cache Với cách này, cache cứng trên cùng một CPU sẽ được sử dụng lâu nhất có thể Lợi ích thứ hai cưa phương pháp này là không phải sử dụng spinlock khi CPU truy cập pool bởi vì CPU sẽ không thể truy cập được dữ liệu cục bộ khi object đã được gán cho CPU này