Powered by Blogger.

Linux kernel làm việc như thế nào?

GIT – Nói một cách đại thể, nhân  () là lớp phần mềm nằm giữa phần cứng và các chương trình ứng dụng chạy trên một máy tính. Nói một cách chặt chẽ theo đúng nghĩa khoa học máy tính, danh từ “” chỉ dùng để chỉ phần nhân mà Linus Torvalds đã viết đầu những năm 90.

Tất cả các phần khác bạn thấy trong một bản Linux: Bash shell, KDE, trình duyệt, X và nhiều cái khác là những ứng dụng chạy trên nhân. Để hình dung, một bản Red Hat Linux 5 chiếm khoảng 2,5GB ổ cứng thì trong đó phần nhân, bao gồm tất cả các module, chỉ có 47MB, tức là khoảng 2%.
Bên trong nhân.
Vậy thì nhân thực sự làm cái gì? Hình vẽ dưới đây cho thấy một bức tranh tổng thể. Nhân cung cấp các dịch vụ cho các phần mềm ứng dụng chạy trên nó qua một tập hợp các cổng vào (entry points) được gọi là các lời gọi hệ thống (system calls).
Linux kernel làm việc như thế nào?
Hình 1: Nhân dùng những lời gọi hệ thống như ‘read’ và ‘write’ để cung cấp một lớp trừu tượng các phần cứng cụ thể cho các ứng dụng.
Đối với người lập trình thì lời gọi hệ thống cũng giống như các lời gọi hàm thông thường, mặc dù lời gọi hệ thống chuyển cách điều hành của bộ xử lý từ vùng user sang vùng nhân. Toàn bộ các lời gọi hệ thống tạo nên một “máy ảo Linux” là sự trừu tượng hóa các phần cứng bên dưới nó.
Một trong những lớp trừu tượng rõ ràng nhất do nhân cung cấp là hệ thống file. Ví dụ dưới đây là một chương trình ngắn viết bằng C mở một file và copy nội dung file ra một thiết bị ra tiêu chuẩn:
#include <fcntl.h>
int main()
{
int fd, count; char buf[1000];
fd=open(“mydata”, O_RDONLY);
count = read(fd, buf, 1000);
write(1, buf, count);
close(fd);
}
Trong ví dụ trên có bốn lời gọi hệ thống: open, read, write và close. Đừng quan tâm đến cú pháp của lệnh, bây giờ chưa quan trọng. Điểm quan trọng là: thông qua các lời gọi hệ thống đó, nhân Linux cung cấp hình ảnh của một file – một dãy các byte dữ liệu có tên – và tránh cho bạn khỏi phải quan tâm đến những chi tiết nằm bên dưới như track, sector, head, …của cách lưu trữ file đó trên ổ cứng mà bạn phải biết và giao tiếp nếu bạn muốn nói chuyện trực tiếp với phần cứng. Đó là cái mà ta gọi là sự trừu tượng hóa.
Như bạn đã thấy ở hình trên, nhân phải làm rất nhiều việc để cung cấp được một hình ảnh trừu tượng của file mặc dù hệ thống file đó có thể có nhiều format khác nhau (ext3, fat, ntfs, …), nằm trên nhiều thiết bị lưu trữ như ổ cứng, CD,  và thậm chí có thể nằm trên các máy khác trong mạng truy cập bằng các giao thức mạng như  hoặc CIFS (mà cuối cùng bạn vẫn chỉ truy cập đến nó bằng các lời gọi hệ thống, phần còn lại do nhân đảm nhiệm).
Đằng sau hậu trường.
Hệ thống file là sự trừu tượng dễ thấy nhất mà nhân cung cấp. Nhưng nhân còn có những hoạt động khác kém trực quan hơn. Ví dụ, nhân chịu trách nhiệm lập lịch xử lý. Ở mỗi thời điểm, bao giờ cũng có vài tiến trình (process) chờ để chạy. Nhân lập lịch bố trí thời gian để bộ xử lý chạy các tiến trình đó xen kẽ nhau sao cho sau một khoảng thời gian (vài giây) ta có cảm giác các tiến trình đó chạy đồng thời. Dưới đây là một chương trình ví dụ khác:
#include <stdlib.h>
main()
{
if (fork()) {
write(1, “Parent\n”, 7);
wait(0);
exit(0);
}
else {
write(1, “Child\n”, 6);
exit(0);
}
}
Chương trình này tạo ra hai tiến trình: một tiến trình cha và một tiến trình con, mỗi cái viết một message vào thiết bị xuất rồi kết thúc. Đừng quan tâm đến cú pháp của lệnh vội mà chỉ cần thấy rằng các lời gọi fork(), exit(), wait() tạo các tiến trình, kết thúc và đồng bộ chúng. Các lời gọi đơn giản đó che giấu cả một quá trình phức tạp của nhân để quản lý và lập lịch thực hiện các tiến trình.
Một chức năng khác của nhân còn ít rõ ràng hơn ngay cả với nhà lập trình là quản lý bộ nhớ. Mỗi tiến trình đều ảo tưởng rằng chúng có một vùng không gian địa chỉ bộ nhớ riêng. Thực ra chúng dùng chung bộ nhớ RAM với các tiến trình khác. Và nếu RAM không đủ, một phần không gian địa chỉ còn được đưa ra vùng swap trên ổ cứng. Nhân phải điều khiển sao cho một tiến trình không xâm phạm vào vùng địa chỉ của tiến trình khác.
Nhân cũng thực hiện các giao thức mạng như IP, TCP và UDP để liên lạc giữa máy với máy và tiến trình với tiến trình thông qua mạng. Ở đây cũng lại có các ảo tưởng. TCP tạo nên ảo tưởng rằng giữa hai tiến trình có một kết nối cố định như sợi dây đồng giữa hai máy điện thoại, thực ra thì không có kết nối cố định đó. Lưu ý rằng các giao thức ứng dụng như FTP, DNS, HTTP được thực hiện bởi các chương trình, không phải nhiệm vụ của nhân.
Linux (cũng giống như  trước nó) nổi tiếng về an ninh. Chính nhân là nơi theo dõi các mã định danh của người dùng và nhóm người dùng (user ID và group ID) của mỗi tiến trình đang chạy để quyết định có cho phép hay không mỗi lần tiến trình đòi truy cập một tài nguyên (như mở file để ghi vào đó) bằng cách kiểm tra quyền truy cập của tài nguyên đó.
Cuối cùng, nhân có rất nhiều các module. Mỗi module biết các chi tiết của phần cứng để nói chuyện với chúng: làm thế nào để đọc một sector trên ổ cứng, làm thế nào để tìm một gói dữ liệu từ card mạng, v.v và v.v. Đôi khi các module đó được gọi là các driver của thiết bị.
Cấu trúc module của nhân.
Các nhân Linux đời đầu có cấu trúc đơn khối (monolithic): các bộ phận của nhân liên kết cố định với nhau tạo nên một file chạy lớn.
Nhân Linux hiện nay có cấu trúc module: mỗi module chức năng chỉ được tải vào nhân khi cần đến. Do đó nhân có kích thước nhỏ và có thể tải hoặc thay thế các module trong một nhân đang chạy mà không cần phải reboot.
Lúc boot, phần lõi của nhân được tải vào bộ nhớ từ một file trong thư mục /boot, thường là file vmlinuz-<phiên bản của nhân> (ví dụ: vmlinuz-2.6.31.2-desktop586-0.rc1.1mnb). Các module của nhân nằm trong thư mục /lib/modules/<phiên bản của nhân>. Có thể biết phiên bản nhân bằng lệnh uname -r.
Quản lý các module.
Thường thì Linux tự quản lý các module. Nhưng cũng có những lệnh để xem và quản lý module nếu cần. Ví dụ lệnh lsmod cho biết danh sách các module hiện đang được tải vào bộ nhớ:
# lsmod
pcspkr 4224 0
hci_usb 18204 2
psmouse 38920 0
bluetooth 55908 7 rfcomm,l2cap,hci_usb
yenta_socket 27532 5
rsrc_nonstatic 14080 1 yenta_socket
isofs 36284 0
Các trường trong danh sách trên gồm: tên module, kích thước, số lần dùng và danh sách các module phụ thuộc vào nó. Số lần dùng là quan trọng để ngăn việc tải ra (unload) một module hiện đang dùng. Linux chỉ tải ra được các module nào có số lần dùng =0.
Có thể tải vào, tải ra một module bằng lệnh modprobe. Ví dụ, lệnh lsmod ở trên cho thấy module isofs có số lần dùng =0 và không có module nào phụ thuộc vào nó (isofs là module hỗ trợ các hệ thống file trên đĩa CD). Vậy có thể tải nó ra bằng lệnh sau:
# modprobe -r isofs
Nếu chạy lại lệnh lsmod sẽ thấy không còn module isofs nữa và tiết kiệm được 36,284 byte bộ nhớ. Nếu cho một đĩa CD vào ổ và để nó tự mount, nhân sẽ lại tải module isofs vào và số lần dùng nó được tăng lên thành 1. Nếu bây giờ ta lặp lại lệnh trên để tải nó ra thì sẽ bị báo lỗi vì nó đang được dùng:
# modprobe -r isofs
FATAL: Module isofs is in use.
Trong khi lệnh lsmod chỉ liệt kê các module đang được tải, lệnh lsmod -l sẽ liệt kê tất cả các module đã được cài đặt nằm trong thư mục /lib/modules/KERNELVERSION.
Lệnh modinfo liệt kê các thông tin về một module. Ví dụ dưới đây là thông tin về modulesnd-hda- (Intel sound driver):
# modinfo snd-hda-intel
filename: /lib/modules/2.6.20-16-generic/kernel/sound/pci/hda/snd-hda-intel.ko
description: Intel HDA driver
license: GPL
srcversion: A3552B2DF3A932D88FFC00C
alias: pci:v000010DEd0000055Dsv*sd*bc*sc*i*
alias: pci:v000010DEd0000055Csv*sd*bc*sc*i*
depends: snd-pcm,snd-page-alloc,snd-hda-codec,snd
vermagic: 2.6.20-16-generic SMP mod_unload 586
parm: index:Index value for Intel HD audio interface. (int)
parm: id:ID string for Intel HD audio interface. (charp)
parm: model:Use the given board model. (charp)
parm: position_fix:Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size). (int)
parm: probe_mask:Bitmask to probe codecs (default = -1). (int)
parm: single_cmd:Use single  to communicate with codecs (for debugging only). (bool)
parm: enable_msi:Enable Message Signaled Interrupt (MSI) (int)
parm: enable:bool
Các dòng bắt đầu bằng từ parm (viết tắt của parameter) là các tham số của module. Nếu muốn tìm hiểu về các tham số này, hãy cài mã nguồn của nhân. Sau đó tìm hiểu trong thư mục /usr/src/KERNELVERSION/Documentation
Hệ thống file /proc
Nhân Linux cũng cung cấp nhiều thông tin qua hệ thống file /proc. Để hiểu nó ta cần mở rộng khái niệm file.
Thay cho việc nghĩ file như các thông tin tĩnh lưu trên ổ cứng, CD, … ta cần xem file như bất kỳ loại thông tin gì có thể truy cập qua các lời gọi hệ thống như open/read/write/closeđã nói ở trên và như vậy nghĩa là có thể truy cập bằng các chương trình xem file thông thường như cat hoặc less.
Các ‘file’ trong thư mục /proc là sự trừu tượng hóa ảnh của nhân, cung cấp nhiều thông tin về cấu trúc dữ liệu bên trong nhân. Ví dụ: nội dung file /proc/modules (hoặc thư mục /proc/modules) cho danh sách các module của nhân đang được tải vào bộ nhớ.
Tương tự, nội dung file /proc/meminfo cung cấp nhiều thông tin về bộ nhớ hơn là dùng các lệnh vmstat hoặc top.
Đặc biệt là các file nằm trong thư mục /proc/sys cho biết nhiều thông tin về hệ thống. Ví dụ dùng lệnh sau xem file ip_forward:
# cat /proc/sys/net/ipv4/ip_forward
0
Kết quả ’0′ nghĩa là chức năng gateway của nhân hiện đang tắt.
Thú vị hơn khi biết rằng ta có thể viết vào các file đó. Lệnh sau đây sẽ bật chức năng trên lên:
# echo 1 > /proc/sys/net/ipv4/ip_forward
Thay cho dùng lệnh cat hoặc echo như trên để xem và thay đổi các settings trong /proc/sys, có thể dùng lệnh  trực quan hơn. Hai lệnh dưới đây tương đương với hai lệnh cat và echo ở trên:
# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0
# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
Đường dẫn sau lệnh sysctl dùng dấu chấm (.) thay cho dấu sược trái (/) thông thường và các đường dẫn trong đó là tương đối so với /proc/sys.
Lưu ý những thay đổi kiểu như trên chỉ có giá trị với kernel đang chạy và không được lưu lại. Khi reboot lại sẽ mất. Nếu muốn thay đổi cố định các settings, phải sửa file /etc/sysctl.conf.
    Blogger Comment
    Facebook Comment