Sunday, November 29, 2009

Kiến trúc Unix/Linux 2

II. Unix/Linux
Chương I. Tổng quan hệ thống Unix
1. Cấu trúc hệ thống Unix
Unix có thể xem như một loại kim tự tháp với các lớp chức năng xếp chồng lên nhau và tạo ra các giao diện. Phần cứng (hardware) sẽ đề cập sau. Hệ Điều Hành (HĐH, hay Operating System-OS) tương tác trực tiếp với phần cứng, cung cấp các dịch vụ cơ bản cho các chương trình và ngăn cách các chương trình với phần cứng cụ thể. Nếu nhìn hệ thống như từ các lớp, thì OS thông thường được gọi là nhân hệ thống (System Kernel), nó được cách li với chương trình của người dùng. Bởi vì các chương trình ứng dụng nói chung, kể cả OS, độc lập với phần cứng, nên dễ dàng chạy trên các phần cứng khác nhau vì không phụ thuộc vào phần cứng cụ thể. Chẳng hạn Shell và các editors (vi, ed) ở lớp ngoài tương tác với kernel bằng cách phát sinh ra Gọi Hệ Thống (GHT) – system calls. GHT sẽ chỉ thị cho kernel làm những việc khác nhau mà chương trình gọi yêu cầu, thực hiện trao đổi dữ liệu (data) giữa kernel và chương trình đó. Một vài chương trình có tên trong hình là các chương trình chuẩn trong cấu hình của hệ thống và được biết tên dưới dạng các lệnh – commands. Lớp này cũng có thể bao hàm cả các chương trình của người dùng với tên là a.out, một loại tên chuẩn cho các tệp chạy được do bộ dịch C tạo ra. Còn có loại ứng dụng khác (APPS) được xây dựng trên lớp trên cùng của các chương trình có mức thấp hơn hiện diện ở lớp ngoài cùng của mô hình. Mặc dù mô hình mô tả hai cấp các APPS, nhưng người dùng có thể mở rộng ra các cấp thích hợp. Rất nhiều các hệ thống ứng dụng, các chương trình, cho cách nhìn ở mức cao, song tất cả đều dùng các dịch vụ cấp thấp được cung cấp bởi kernel qua GHT. Trong System V chuẩn có 64 GHT, trong đó có 32 GHT là thường dùng nhất (LINUX 2.x có nhiều hơn và khoản chừng 164 lệnh GHT).



Tập hợp các System calls và các thuật toán bên trong tạo thành thân (body) của kernel, do vậy việc nghiên cứu Unix trong sách này sẽ giản lược để nghiên cứu chi tiết các system calls cũng như sự tương tác giữa chúng. Và khái niệm “Unix system”, hay “kernel” hay “system” trong sách đều có ý nói tới kernel của hệ điều hành Unix và rõ ràng hơn ở từng bối cảnh trình bày.
2. Cách nhìn từ phía người dùng: tổ chức tệp
Phần này tóm lượt các nét đặc trưng ở mức cao của Unix chẳng hạn: hệ thống tệp (File system FS), môi trường xử lí, xây dựng các khối nguyên hàm, và sẽ được khai thác sau này.
2.1. Hệ thống tệp (File system - FS)

Hệ thống tệp của Unix được đặc tả bởi:
  • Cấu trúc cấp bậc (cây thư mục)
  • Cách xử lí nhất quán dữ liệu của tệp (chuổi các byte byte stream )
  • Khả năng tạo và hủy tệp (tạo mới, xóa)
  • Tính tăng trưởng động của tệp (thêm bớt, cắt dán)
  • Khả năng bảo vệ dữ liệu của tệp (bởi các kiểu thuộc tính như quyền truy nhập)
  • Xử lí các thiết bị ngoại vi như xử lí các tệp (cách nhìn thiết bị bởi mô tả kiểu tệp).



FS được tổ chức như một cây bắt đầu từ một nút đơn gọi là root, được biểu diễn như sau: “ /”; từ đó sẽ có các thư mục khác tạo thành nhánh của cây, trong các nhánh có thể có các nhánh (con) khác. Dưới các nhánh sẽ là tệp. Tệp có thể là tệp bình thường (regural files) hay cũng có thể là tệp đặc biệt (special files). Tệp được truy nhập qua đường dẫn (path name) mô tả cách thức định vị được tệp trong FS. Đường dẫn đầy đủ, hay đường dẫn tuyệt đối, bắt đầu bởi dấu / và nó xác định sẽ tìm tệp bằng cách đi từ root qua cấu trúc cây thư mục theo các nhánh chỉ thị trong đường dẫn. Ví dụ trong hình ta có: /usr/src/cmd/date.c là đường dẫn tuyệt đối tới tệp date.c. Đường dẫn không bắt đầu từ root gọi là đường dẫn tương đối, chỉ tới thư mục hiện tại của tệp.
Các chương trình trong Unix không có hiểu biết gì về định dạng (format) bên trong của dữ liệu của tệp. Kernel lưu dữ liệu của tệp, xử lí dữ liệu tệp như một dòng các bytes (byte stream) không có định dạng. Do vậy cú pháp truy nhập dữ liệu trong tệp được định nghĩa bởi hệ thống và nhất quán như nhau cho tất cả các chương trình, nhưng ngữ nghĩa của dữ liệu thì chương trình ứng dụng phải xử lí.
Ví dụ: Chương trình troff xử lí văn bản có định dạng hoài vọng sẽ tìm thấy các kí tự “dòng mới” (“ new line ”) ở cuối mỗi dòng văn bản, còn chương trình kế toán acctcom hoài vọng tìm thấy những bản ghi có độ dài cố định. Cả hai chương trình dùng cùng các dịch vụ hệ
thống để truy nhập dữ liệu trong tệp theo cách byte stream, nhưng bên trong mỗi chương trình lại dùng cách phân tích cú pháp khác nhau thích hợp cho nó. Nếu một chương trình phát hiện thấy định dạng là không đúng, thì bản thân chương trình sẽ thực hiện một hành vi khác để xử lí (chứ không phải hệ thống làm điều đó).
Thư mục cũng là một loại tệp, hệ thống xử lí dữ liệu trong thư mục cũng bằng byte stream, nhưng dữ liệu ở đây chứa tên các tệp trong thư mục có khuôn dạng dự đoán được, sao cho OS và các chương trình, ví dụ ls, có thể nhận ra các tệp trong thư mục.
Việc truy nhập tệp được kiểm soát bởi quyền truy nhập (access permission) kết hợp với tệp. Quyền truy nhập được lập ra một cách độc lập để kiểm soát truy nhập đọc (read), ghi (write), và thực hiện (execute) cho ba lớp người sử dụng: người sở hữu tệp (u - user), nhóm người được truy nhập (g - group), những người khác (o - other). Người dùng có thể tạo tệp nếu họ được phép và các tệp mới tạo sẽ là các nhánh lá của cấu trúc thư mục hệ thống.
Đối với người dùng, Unix xử lí các thiết bị như thể đó là các tệp. Các thiết bị được mô tả bởi các tệp thiết bị đặc biệt và nằm ở một nhánh trong cấu trúc hệ thống thư mục (/dev). Các chương trình truy nhập các thiết bị bằng cú pháp giống như đã dùng để truy nhập tệp bình thường, các thiết bị cũng được bảo vệ cùng phương thức như các tệp, qua việc ấn định quyền truy nhập. Bởi vì tên các thiết bị cũng giống như tên các tệp bình thường và các thao tác trên chúng là như nhau, nên hầu hết các chương trình đều không biết tới kiểu tệp bên trong của tệp mà chúng thao tác.

2.2. Môi trường xử lý
Một chương trình - program là một tệp thực thi và một tiến trình (TT – procces) là một khoảnh khắc (instance) của chương trình được thực hiện theo trục thời gian. TT bao gồm:
  • mã trình thực thi
  • dữ liệu (data) của TT
  • program (user) stack
  • CPU program counter
  • kernel stack
  • CPU registers
và các thông tin khác cần thiết để chạy trình. Các dữ liệu này tạo ra bối cảnh (context) của TT, mỗi TT có bối cảnh riêng biệt. Có rất nhiều TT được thực hiện đồng thời trên Unix (đặc tính này còn gọi là đa trình - multiprogramming hay đa nhiệm - multitasking) theonguyên lí phân chia thời gian (time sharing), mà tổng số các TT về logic là không có giới hạn. Có nhiều GHT cho phép các TT tạo ra các TT mới, kết thúc các TT, đồng bộ các giai đoạn thực hiện TT, kiểm soát phản ứng với các sự kiện khác nhau. Các TT sử dụng GHT độc lập với nhau. Ví dụ chạy đa trình với 4 chương trình A, B, C, D trên một CPU:



Hãy xét ví dụ sau:
Code:
main (argc, argv)
int argc;
char *argv[];
{
if (fork () == 0)
execl (“copy”, ”copy”, argv[1], argv[2], 0);
wait((int *) 0);
printf (“copy done\n”);
}
Chương trình trên dùng GHT fork() để tạo ra một TT mới. TT mới gọi là TT con sẽ nhận được giá trị trả lại là 0 từ lệnh fork và nó kích hoạt execl để chạy trình copy. Lệnh execl sẽ phủ lên không gian địa chỉ của TT con bằng mã của trình “copy”, với giả định trình “copy”
nằm cùng trong thư mục hiện hành của main, và chạy trình copy với các thông số do người dùng đưa vào. Nếu execl hoàn tất nó sẽ không trở về địa chỉ xuất phát trong main vì nó chạy trong một miền địa chỉ mới khác. Trong khi đó TT bố đã kích hoạt fork() lại nhận được giá trị trả lại khác 0 từ GHT wait(), nó “treo” việc thực hiện để đợi cho đến khi “copy” kết thúc và in ra thông báo “copy done “ và sau đó kết thúc thực hiện main bằng exit (exit() là ngầm định khi kết thúc main trong C).
Một cách tổng quát, GHT cho phép người dùng viết các chương trình thực hiện các thao tác rất tinh tế mà bản thân kernel không cần có nhiều chức năng hơn là cần thiết. Có thể đề cập tới một số các chức năng, chẳng hạn các bộ dịch (compilers), bộ soạn thảo (editors) thuộc lớp các chương trình cấp người dùng (user level) và quan trọng hàng đầu là shell, là trình thông dịch mà người dùng sử dụng ngay sau khi log in vào hệ thống: shell thông dịch các từ trong dòng lệnh thành tên lệnh máy, phát sinh TT con và TT con thực hiện lệnh đưa vào, xử lí các từ còn lại trong dòng lệnh như các thông số của lệnh.
Shell thực hiện ba kiểu lệnh:
  1. Lệnh là tệp có thể thực hiện được chứa mã máy phát sinh do bộ dịch tạo ra từ mã
    nguồn (chương trình C chẳng hạn)
  2. Lệnh là tệp chứa một xâu các dòng lệnh của shell
  3. Là các lệnh bên trong của shell. Các lệnh bên trong này làm cho shell trở thành một ngôn ngữ lập trình rất mạnh trong Unix.
Shell là chương trình thuộc lớp người dùng, không phải là phần của kernel, cho nên có thể dể dàng biến cải cho mỗi môi trường đặc thù. Bản thân shell cũng có ba loại khác nhau thích hợp cho các nhu cầu sử dụng khác nhau và hệ thống có thể chạy các shell đó đồng thời.
Sức mạnh của mỗi kiểu shell thể hiện ở khả năng lập trình của mỗi kiểu.
Mỗi TT được thực hiện trong Unix có một môi trường (execution environment) thực hiện, bao gồm cả thư mục hiện hành. Thư mục hiện hành của TT là thư mục dùng để chỉ đường dẫn không bắt đầu bằng “ /”. Người dùng có thể thực hiện nhiều TT cùng một lúc, và các TT lại có thể tạo ra các TT khác một cách động, và đồng bộ việc thực hiện các TT đó. Đặc tính này tạo ra một môi trường thực hiện chương trình rất mạnh trong Unix.
2.3 Xây dựng các hàm chức năng cơ bản (primitives)
Như đã đề cập, tính triết lí của Unix là để cung cấp cho OS các nguyên hàm (primitives) mà người dùng sẽ sử dụng để viết các chương trình (chức năng) nhỏ, có tính modul, được dùng như các khối xây dựng để tạo ra các chương trình lớn và phức tạp. Một trong các primitive đó là khả năng tái định tuyến vào/ra (redirect I/O). Tiếp theo là pipe, một cơ chế linh hoạt cho phép truyền dữ liệu giữa các TT, hay lệnh ngay từ bàn phím. Ví dụ, khi dùng các chương trình nhỏ để tạo các chương trình lớn và phức tạp, người lập trình sử dụng các primitives redirect I/O và pipe để hợp nhất các phần đó lại.
3. Các dịch vụ của Unix/Linux
Trong hình mô tả các lớp của kernel, cho thấy lớp kernel nằm ngay bên dưới lớp các trình ứng dụng của người dùng. Kernel thực hiện vô số các thao tác cơ bản (primitives) thay mặt cho các TT của nguời dùng để hỗ trợ cho giao diện người dùng. Các thao tác đó bao hàm các dịch vụ mà kernel cấp:
  • Kiểm soát việc thực hiện các TT gồm có: cho phép TT tạo TT mới, kết thúc TT, treo việc thực hiện và trao đổi thông điệp giữa các TT.
  • Lập biểu để các TT được thục hiện trên CPU. Các TT chia xẻ CPU theo phương thức phân chia thời gian, một TT sẽ bị treo sau khi thời gian phân bổ đã hết, kernel lấy TT khác đưa vào thực hiện. Sau này kernel sẽ lại lựa chọn TT bị treo để đưa vào thực hiện trở lại.
  • Cấp phát bộ nhớ cho TT đang thực hiện, cho phép TT chia sẻ không gian địa chỉ của TT dưới những điều kiện nhất định, bảo vệ miền địa chỉ riêng của TT đối với các TT khác. Nếu hệ thống chạy trong hoàn cảnh thiếu bộ nhớ, kernel sẽ giải phóng bộ nhớ bằng cách ghi lại các TT tạm thời vào bộ nhớ dự phòng (còn gọi là thiết bị swap). Nếu toàn bộ TT được ghi vào swap, thì hệ Unix gọi là hệ tráo đổi (swapping system). Nếu kernel ghi các trang của bộ nhớ lên swap, thì hệ đó gọi là hệ lưu trang (paging system).
  • Cấp phát bộ nhớ thứ cấp để cất và tìm lại dữ liệu của người dùng có hiệu quả. Dịch vụ này cấu tạo nên hệ thống tệp. Kernel cấp vùng nhớ thứ cấp cho tệp của người dùng, khôi phục lại vùng nhớ, xây dựng cấu trúc tệp theo một cách thức hiểu được, bảo vệ tệp của người dùng trước các truy nhập bất hợp pháp.
  • Cho phép các TT truy nhập các thiết bị ngoại vi, ví dụ t/b đầu cuối, đĩa, t/b mạng.
  • Kernel cung cấp các dịch vụ một cách thông suốt, chẳng hạn kernel ghi nhận tệp cần thao tác thuộc loại tệp bình thường hay tệp thiết bị, nhưng ẩn điều đó đối với TT của người dùng; hay ví dụ, kernel tạo khuôn dữ liệu trong tệp để ghi (đĩa), nhưng lại ẩn khuôn dạng đó đối với TT người dùng (user). Tương tự như vậy đối với các dịch vụ hệ thống cung cấp cho các TT user dùng ở mức độ cấp người dùng. Ví dụ dịch vụ hệ thống mà shell dùng để đóng vai trò là trình thông dịch lệnh: cho phép shell đọc đầu vào từ t/b đầu cuối, phát sinh động các TT, đồng bộ việc thực hiện các TT, tạo pipe, đổi hướng I/O. Người dùng cấu tạo các phiên bản shell riêng mà không tác động tới những users khác. Các trình đó cùng dùng các dịch vụ của kernel ở mức shell chuẩn.
4. Phần cứng
Tiến trình người dùng (TT user) trên Unix được chia ra làm hai mức độ: Chế độ người
dùng (user mode) và chế độ nhân (kernel mode). Khi TT thục hiện một GHT, chế độ thực hiện TT sẽ chuyển từ user mode sang kernel mode: OS thực hiện và cố gắng phục vụ các yêu cầu của user, trả lại kết quả và thông báo lỗi nếu có. OS lưu lại các hoạt động có liên quan tới TT user, thao tác các ngắt, lập biểu chạy TT, quản lí bộ nhớ... Có loại máy hỗ trợ nhiều mức hơn, tuy nhiên trong Unix hai mức này là đủ.
Sự khác biệt của hai mức này là:
  • Các ứng dụng chạy trong chế độ xử lí không có đặc quyền, user mode, liên lạc với hệ thống qua một tập các giao tiếp giới hạn (kể cả một số lệnh của CPU), cũng như bị hạn chế truy nhập dữ liệu hệ thống. TT ứng dụng có thể truy nhập các lệnh và dữ liệu của nó, không được truy nhập lệnh và dữ liệu của kernel cũng như của các TT khác. Khi TT trong user mode thực hiện một GHT, kernel “bẩy“ GHT đó, chuyển chế độ thực hiện vào kernel mode. Kernel kiểm soát TT, xác thực các đối (ví dụ quyền truy nhập, quyền thao tác dữ liệu) mà TT chuyển cho GHT và thực hiện GHT đó. Khi GHT kết thúc, Kernel chuyển TT ngược lại vào user mode trước khi trả điều khiển lại cho TT, cho phép TT tiếp tục chạy. Bằng cách đó kernel bảo vệ được chính nó cũng như các dữ liệu khỏi bị TT user làm tổn hại.
  • Thực hiện mã của HĐH chạy trong chế độ đặc quyền của CPU, gọi là kernel mode.
    Trong chế độ này HĐH chạy và thực hiện các GHT mà TT user đã gọi. TT trong
    kernel mode có thể truy nhập vào không gian địa chỉ của nó ở cả hai vùng kernel và user. Việc truy nhập tài nguyên hệ thống (các cấu trúc dữ liệu hê thống và phần cứng) không có giới hạn đối với kernel. Một số các lệnh máy là đặc quyền chỉ kernel mode mới dùng được.
  • OS duy trì các thông tin (records) bên trong để phân biệt các TT thực hiện trên hệ thống. Mặc dù hệ thống chạy một trong hai chế độ nói trên, song kernel chạy trên danh nghĩa của TT user. Kernel không phải là tập hợp của các TT riêng biệt chạy song song với các TT user, mà là một phần của mỗi TT user. Trong văn ngữ khi nói “kernel thực hiện...” thì điều đó có nghĩa là TT chạy trong chế độ kernel thực hiện... cái gì đó. Ví dụ, shell đọc đầu vào qua GHT và được mô tả như sau: Kernel thực hiện nhân danh TT shell, kiểm soát thao tác thiết bị đầu cuối, trả lại các kí tự nhận vào cho shell. Đến đây shell, chạy trong user mode, thông dịch xâu kí tự nhận được từ người dùng và thực hiện một số hành động mà có thể các hành động đó kích hoạt GHT khác dẫn tới TT shell lại trở vào kernel mode.
  • Trong môi trường đa người dùng như Unix, các thiết bị hoạt động trên cơ sở độc lập có ý nghĩa rất căn bản. Unix nhìn nhận các thiết bị như một tệp đặc biệt. Khi một t/b mới cần đưa vào hệ, người quản trị thực hiện thêm một liên kết cần thiết vào kernel. Liên kết này được biết như là phần mềm thiết bị (device driver) , đảm bảo rằng kernel và thiết bị được gắn lại theo cùng một phương thức mỗi khi t/b đuợc đưa vào phục vụ. Điểm mấu chốt để t/b là độc lập, liên quan tới khả năng tự thích nghi của kernel: Unix không có giới hạn số lượng của bất kì loạt t/b nào khi thích ứng vào hệ vì mỗi t/b được nhìn nhận độc lập qua liên kết riêng biệt với kernel.
4.1. Ngắt và Ngoại lệ
Unix cho phép các t/b như I/O, đồng hồ hệ thống ngắt CPU theo cách dị bộ. Khi chấp nhận ngắt, kernel sẽ bảo vệ bối cảnh (context) hiện tại của TT đang thực hiện, xác định lí do của ngắt, và phục vụ cho yêu cầu ngắt đó. Sau khi xử lí xong kernel khôi phục lại context của TT trước đó và tiếp tục thực hiện như không có gì đã xảy ra. Phần cứng thông thường có cơ chế để đặt các cấp độ ưu tiên và che các ngắt theo thứ tự mà ngắt được thao tác. Các trường hợp ngoại lệ là sự kiện không trông đợi gây ra bởi một TT, ví dụ truy nhập vào
vùng địa chỉ cấm, thực hiện lệnh đặc quyền, phép chia cho zero... Các ngoại lệ này khác với ngắt bởi chúng phát sinh do các sự kiện bên ngoài một TT. Ngoại lệ xảy ra ở giữa chừng đang thực hiện một lệnh, và hệ thống sẽ tái khởi động lại lệnh sau khi đã thao tác ngoại lệ. Ngắt được xem là xảy ra giữa hai lệnh và hệ thống chạy lệnh tiếp theo sau xử lí ngắt. Unix dùng cùng một cơ chế để thao tác ngắt cũng như thao tác các ngoại lệ.
4.2. Các mức độ thực hiện xử lí
Đôi khi kernel phải ngăn chặn sự xuất hiện của ngắt trong lúc thực hiện những hoạt động có tính chất đặc biệt mà ngắt có thể làm hỏng dữ liệu hay rối loạn các con trỏ. Các máy tính thường có một số lệnh đặc biệt để làm công việc này gọi là đặt các mức độ xử lí theo
mức, có thể che các ngắt mức thấp và cho phép ngắt mức cao.



4.3. Quản lí bộ nhớ
Kernel thường trú trong bộ nhớ chính và thực hiện TT hiện thời (hay ít ra là một phần của TT đó). Khi compiler dịch một chương trình, nó tạo ra tập các địa chỉ của chương trình đó cho các biến, cấu trúc dữ liệu, địa chỉ của lệnh... Compiler phát sinh ra địa chỉ cho một máy ảo, như thể không có chương trình nào khác sẽ được thực hiện đồng thời trên máy vật lí. Khi một chương trình chạy trong máy tính, kernel sẽ cấp cho trình một không gian địa chỉ trong bộ nhớ vật lí, nhưng không gian địa chỉ ảo này không nhất thiết phải đồng nhất với địa chỉ vật lí. Kernel phối hợp với phần cứng để ánh xạ từ địa chỉ ảo vào địa chỉ vật lí. Cách ánh xạ phụ thuộc vào đặc thù của phần cứng và các phần của Unix sẽ thích ứng theo. Ví dụ loại máy hỗ trợ theo trang (paging) hay theo hoán đổi (swapping), kernel có các hàm cơ sở tương tự cho mỗi loại cấu hình.

No comments:

Post a Comment