21
CHƯƠNG 2. QUẢN LÝ TIẾN TRÌNH
2.1. Khái niệm tiến trình
2.1.1. Tiến trình và các loại tiến trình
a. Tiến trình (process):
Tiến trình là một khái niệm cơ bản trong hệ điều hành. Tiến trình là một chương trình
đang chạy. Mỗi tiến trình có không gian địa chỉ riêng (address space), đó là vùng bộ nhớ dành
riêng cho tiến trình, tiến trình có thể đọc và ghi trong vùng bộ nhớ này. Vùng bộ nhớ này
chứa chương trình, dữ liệu của chương trình và stack.
Trong hệ thống chia xẻ thời gian (timesharing system), hệ điều hành định kỳ ngưng tạm
thời (treo, suspend) một tiến trình đang chạy và cho một tiến trình khác chạy.
Khi tiến trình được chạy lại, tiến trình được khôi phục lại trạng thái giống như trước khi
bị ngưng, do vậy tất cả các thông tin về tiến trình sẽ được cất giữ trước khi bị treo. Các thông
tin này được lưu trong bảng điều hành (operating system table) của hệ điều hành, mỗi mục
trong bảng điều hành chứa thông tin của một tiến trình.
Hàm gọi hệ thống liên quan đến tiến trình về cơ bản gồm có tạo tiến trình và chấm dứt
tiến trình. Một tiến trình có thể tạo những tiến trình khác, các tiến trình này được gọi là tiến
trình con (child process), và ta có một cây tiến trình.
Hình 2.1. Cây tiến trình.
Mỗi người được quyền sử dụng hệ thống sẽ có một số hiệu nhận diện UID (User
Identification), chỉ có người quản trị hệ thống mới được quyền gán UID cho người sử dụng.
Tiến trình khi chạy mang UID của người sử dụng chạy nó. Tiến trình con sẽ có UID giống
như tiến trình cha. Người sử dụng có thể thuộc một nhóm nào đó và cũng có số hiệu nhận
diện nhóm GID (Group Identification).
Trong hệ thống có một UID quan trọng, được gọi là superuser, có quyền cao nhất. Chỉ
có người quản trị hệ thống mới có mật khẩu để trở thành superuser. Superuser có thể vi phạm
bất cứ sự bảo vệ an toàn hệ thống.
b. Các loại tiến trình:
Các tiến trình trong hệ thống có thể chia thành hai loại: tiến trình tuần tự và tiến trình
song song. Tiến trình tuần tự là các tiến trình mà điểm khởi tạo của nó là điểm kết thúc của
tiến trình trước đó. Tiến trình song song là các tiến trình mà điểm khởi tạo của tiến trình này
mằn ở thân của các tiến trình khác, tức là có thể khởi tạo một tiến trình mới khi các tiến trình
trước đó chưa kết thúc. Tiến trình song song được chia thành nhiều loại:
22
Tiến trình song song độc lập: là các tiến trình hoạt động song song nhưng không có
quan hệ thông tin với nhau, trong trường hợp này hệ điều hành phải thiết lập cơ chế bảo vệ dữ
liệu của các tiến trình, và cấp phát tài nguyên cho các tiến trình một cách hợp lý.
Tiến trình song song có quan hệ thông tin: trong quá trình hoạt động các tiến trình
thường trao đổi thông tin với nhau, trong một số trường hợp tiến trình gửi thông báo cần phải
nhận được tín hiệu từ tiến trình nhận để tiếp tục, điều này dễ dẫn đến bế tắc khi tiến trình nhận
tín hiệu không ở trong trạng thái nhận hay tiến trình gửi không ở trong trạng thái nhận thông
báo trả lời.
Tiến trình song song phân cấp: Trong qua trình hoạt động một tiến trình có thể khởi tạo
các tiến trình khác hoạt động song song với nó, tiến trình khởi tạo được gọi là tiến trình cha,
tiến trình được tạo gọi là tiến trình con. Trong mô hình này hệ điều hành phải giải quyết vấn
đề cấp phát tài nguyên cho các tiến trình con. Tiến trình con nhận tài nguyên ở đâu, từ tiến
trình cha hay từ hệ thống. Để giải quyết vấn đề này hệ điều hành đưa ra 2 mô hình quản lý tài
nguyên: Thứ nhất, mô hình tập trung, trong mô hình này hệ điều hành chịu trách nhiệm phân
phối tài nguyên cho tất cả các tiến trình trong hệ thống. Thứ hai, mô hình phân tán, trong mô
hình này hệ điều hành cho phép tiến trình con nhận tài nguyên từ tiến trình cha, tức là tiến
trình khởi tạo có nhiệm vụ nhận tài nguyên từ hệ điều hành để cấp phát cho các tiến trình mà
nó tạo ra, và nó có nhiệm vụ thu hồi lại tài nguyên đã cấp phát trả về cho hệ điều hành trước
khi kết thúc.
Tiến trình song song đồng mức: là các tiến trình hoạt động song song sử dụng chung tài
nguyên theo nguyên tắc lần lượt, mỗi tiến trình sau một khoảng thời gian chiếm giữ tài
nguyên phải tự động trả lại tài nguyên cho tiến trình kia.
Các tiến trình tuần tự chỉ xuất hiện trong các hệ điều hành đơn nhiệm đa chương, như hệ
điều hành MS_DOS, loại tiến trình này tồn tại nhiều hạn chế, điển hình nhất là không khai
thác tối đa thời gian xử lý của CPU. Các tiến trình song song xuất hiện trong các hệ điều
hành đa nhiệm đa chương, trên cả hệ thống một CPU và nhiều CPU. Nhưng sự song song
thực, chỉ có ở các hệ thống nhiều CPU, trong hệ thống này mỗi CPU chịu trách nhiệm thực
hiện một tiến trình. Sự song song trên các hệ thống một CPU là sự song song giả, các tiến
trình song song trên hệ thống này thực chất là các tiến trình thay nhau sử dụng CPU, tiến trình
này đang chạy thì có thể dừng lại để nhường CPU cho tiến trình khác chạy và sẽ tiếp tục lại
sau đó khi có được CPU. Đây là trường hợp mà ở trên ta cho rằng: điểm khởi tạo của tiến
trình này nằm ở thân của tiến trình khác.
Hình vẽ sau đây minh họa sự khác nhau, về mặt thực hiện, giữa các tiến trình song
song/ đồng thời trong hệ thống một CPU với các tiến trình song song/ đồng thời trong hệ
thống nhiều CPU.
Trong giáo trình này chúng ta chỉ khảo sát sự hoạt động của các tiến trình song song
(hay đồng thời) trên các hệ thống một CPU.
Đối với người sử dụng thì trong hệ thống chỉ có hai nhóm tiến trình. Thứ nhất, là các
tiến trình của hệ điều hành. Thứ hai, là các tiến trình của chương trình người sử dụng. Các
tiến trình của hệ điều hành hoạt động trong chế độ đặc quyền, nhờ đó mà nó có thể truy xuất
vào các vùng dữ liệu được bảo vệ của hệ thống. Trong khi đó các tiến trình của chương trình
người sử dụng hoạt động trong chế độ không đặc quyền, nên nó không thể truy xuất vào hệ
23
thống, nhờ đó mà hệ điều hành được bảo vệ. Các tiến trình của chương trình người sử dụng có
thể truy xuất vào hệ thống thông qua các tiến trình của hệ điều hành bằng cách thực hiện một
lời gọi hệ thống.
2.1.2. Mô hình tiến trình
Đa số các hệ điều hành đều muốn đưa sự đa chương, đa nhiệm vào hệ thống. Tức là,
trong hệ thống có thể có nhiều chương trình hoạt động đồng thời (concurrence) với nhau. Về
nguyên tắc, để thực hiện được điều này thì hệ thống phải có nhiều CPU, mỗi CPU có nhiệm
vụ thực hiện một chương trình, nhưng mong muốn của hệ điều hành cũng như người sử dụng
là thực hiện sự đa chương trên các hệ thống chỉ có một CPU, và trên thực tế đã xuất hiện
nhiều hệ điều hành thực hiện được điều này, hệ điều hành windows9x, windowsNT/2000
chạy trên máy tính cá nhân là một ví dụ. Để thực hiện được điều này hệ điều hành đã sử dụng
mô hình tiến trình để tạo ra sự song song giả hay tạo ra các CPU logic từ CPU vật lý. Các
CPU logic có thể hoạt động song song với nhau, mỗi CPU logic chịu trách nhiệm thực hiện
một tiến trình.
Trong mô hình tiến trình hệ điều hành chia chương trình thành nhiều tiến trình, khởi tạo
và đưa vào hệ thống nhiều tiến trình của một chương trình hoặc của nhiều chương trình khác
nhau, cấp phát đầy đủ tài nguyên (trừ CPU) cho tiến trình và đưa các tiến trình sang trạng thái
sẵn sàng. Hệ điều hành bắt đầu cấp CPU cho một tiến trình trong số các tiến trình ở trạng thái
sẵn sàng để tiến trình này hoạt động, sau một khoảng thời gian nào đó hệ điều hành thu hồi
CPU của tiến trình này để cấp cho một tiến trình sẵn sàng khác, sau đó hệ điều hành lại thu
hồi CPU từ tiến trình mà nó vừa cấp để cấp cho tiến trình khác, có thể là tiến trình mà trước
đây bị hệ điều hành thu hồi CPU khi nó chưa kết thúc, và cứ như thế cho đến khi tất cả các
tiến trình mà hệ điều hành khởi tạo đều hoạt động và kết thúc được. Điều đáng chú ý trong mô
hình tiến trình này là khoảng thời gian chuyển CPU từ tiến trình này sang tiến trình khác hay
khoảng thời gian giữa hai lần được cấp phát CPU của một tiến trình là rất nhỏ nên các tiến
trình có cảm giác luôn được sở hữu CPU (logic) hay hệ thống có cảm giác các tiến trình/
chương trình hoạt động song song nhau. Hiện tượng này được gọi là sự song song giả.
Giả sử trong hệ thống có 3 tiến trình sẵn sàng P
1
, P
2
, P
3
thì quá trình chuyển CPU giữa 3
tiến trình này có thể minh họa như sau:
P1
P2
P3
t
a
P1
P2
P3
t
b
Hình 2.2:
Sự thực hiện đồng thời của các tiế
n trình trong
hệ thống một CPU (a) và hệ thống nhiều CPU (b).
24
Thời điểm Trạng thái các tiến trình
t
1
P
1
: được cấp CPU
t
2
P
1
: bị thu hồi CPU (khi chưa kết thúc)
P
3
: được cấp CPU
t
3
P
3
: bị thu hồi CPU (khi chưa kết thúc)
P
1
: được cấp CPU
t
4
P
1
: kết thúc và trả lại CPU
P
2
: được cấp CPU
t
5
P
2
: kết thúc và trả lại CPU
P
3
: được cấp CPU
t
6
P3: kết thúc và trả lại CPU
Hình 2.3 minh họa quá trình thực hiện của 3 tiến trình P
1
, P
2
, P
3
ở trên:
Chúng ta đều biết, chức năng cở bản của CPU là thực hiện các chỉ thị máy (machine
instrustion) thường trú trong bộ nhớ chính, các chỉ thị này được cung cấp từ một chương
trình, chương trình bao gồm một dãy tuần tự các chỉ thị. Và theo trên, tiến trình là một bộ
phận của chương trình, nó cũng sở hữu một tập lệnh trong bộ nhớ chính, một con trỏ lệnh,…
Nên xét về bản chất, thì việc chuyển CPU từ tiến trình này sang tiến trình khác thực chất là
việc điều khển CPU để nó thực hiện xen kẽ các chỉ thị bên trong tiến trình. Điều này có thể
thực hiện dễ dàng bằng cách thay đổi hợp lý giá trị của con trỏ lệnh, đó chính là cặp thanh ghi
CS:IP trong các CPU thuộc kiến trúc Intel, để con trỏ lệnh chỉ đến các chỉ thị cần thực hiện
trong các tiến trình. Để thấy rõ hơn điều này ta hãy xem ví dụ sau đây:
Giả sử hệ thống cần thực hiện đồng thời 3 tiến trình P
1
, P
2
, P
3
, bắt đầu từ tiến trình P
1
.
Các chỉ thị của các tiến trình này được nạp vào bộ nhớ tại các địa chỉ như sau:
Tiến trình P
1
: Tiến trình P
2
: Tiến trình P
3
:
a + 0 b + 0 c + 0
a + 1 b + 2 c + 1
a + 3 b + 3 c + 4
a + 5 c + 6
Trong đó: a: là địa chỉ bắt đầu của chương trình của tiến trình P
1.
b: là địa chỉ bắt đầu của chương trình của tiến trình P
2.
c: là địa chỉ bắt đầu của chương trình của tiến trình P
3.
Thì giá trị của con trỏ lệnh, chính xác là giá trị cặp thanh ghi CS:IP, lần lượt là: a + 0, b
+ 0, c + 0, a + 1, b + 2, c + 1, a + 3, b + 3, c + 4, a + 5, c + 6. Tức là, CPU thực hiện xen kẽ
các chỉ thị của 3 tiến trình P
1
, P
2
, P
3
từ lệnh đầu tiên đến lệnh cuối cùng, cho đến khi tất cả
các chỉ thị của 3 tiến trình đều được thực hiện. Nhưng khoảng thời gian từ khi con trỏ lệnh = a
+ 0 đến khi = a + 1, hay từ khi = b + 0 đến khi = b + 2, … là rất nhỏ, nên hệ thống có “cảm
giác” 3 tiến trình P
1
, P
2
, P
3
hoạt động đồng thời với nhau.
P1
P2
P3
t
Hình
2.3: Sự hoạt động “song song” của các tiến trình P
1
, P
2
, P
3.
t
1
t
2
t
3
t
4
t
5
t
6
25
Ví dụ trên đây cho ta thấy bản chất của việc thực hiện song song (hay đồng thời) các
tiến trình trên các hệ thống một CPU.
Rõ ràng với mô hình tiến trình hệ thống có được 2 điều lợi:
Tiết kiệm được bộ nhớ: vì không phải nạp tất cả chương trình vào bộ nhớ mà chỉ nạp
các tiến trình cần thiết nhất, sau đó tùy theo yêu cầu mà có thể nạp tiếp các tiến trình khác.
Cho phép các chương trình hoạt động song song nên tốc độ xử lý của toàn hệ thống tăng
lên và khai thác tối đa thời gian xử lý của CPU.
Việc chọn thời điểm dừng của tiến trình đang hoạt động (đang chiến giữ CPU) để thu
hồi CPU chuyển cho tiến trình khác hay việc chọn tiến trình tiếp theo nào trong số các tiến
trình đang ở trạng thái sẵn sàng để cấp CPU là những vấn đề khá phức tạp đòi hỏi hệ điều
hành phải có một cơ chế điều phối thích hợp thì mới có thể tạo ra được hiệu ứng song song
giả và sử dụng tối ưu thời gian xử lý của CPU. Bộ phận thực hiện chức năng này của hệ điều
hành được gọi là bộ điều phối (dispatcher) tiến trình.
2.1.3. Khối quản lý tiến trình (Proccess Control Block – PCB)
Để quản lý các tiến trình và tài nguyên trong hệ thống, hệ điều hành phải có các thông
tin về trạng thái hiện thời của mỗi tiến trình và tài nguyên. Trong trường hợp này hệ điều hành
xây dựng và duy trì các bảng thông tin về mỗi đối tượng (memory, devices, file, process) mà
nó quản lý, đó là các bảng: memory table cho đối tượng bộ nhớ, I/O table cho đối tượng thiết
bị vào/ra, file table cho đối tượng tập tin, process table cho đối tượng tiến trình. Memory table
được sử dụng để theo dõi cả bộ nhớ thực lẫn bộ nhớ ảo, nó phải bao gồm các thông tin sau:
Không gian bộ nhớ chính dành cho tiến trình. Không gian bộ nhớ phụ dành cho tiến trình. Các
thuộc tính bảo vệ bộ nhớ chính và bộ nhớ ảo. Các thông tin cần thiết để quản lý bộ nhớ ảo. Ở
đây chúng tôi điểm qua một vài thông tin về memory table, là để lưu ý với các bạn rằng:
nhiệm vụ quản lý tiến trình và quản lý bộ nhớ của hệ điều hành có quan hệ chéo với nhau, bộ
phận quản lý tiến trình cần phải có các thông tin về bộ nhớ để điều khiển sự hoạt động của
tiến trình, ngược lại bộ phận quản lý bộ nhớ phải có các thông tin về tiến trình để tổ chức nạp
tiến trình vào bộ nhớ, … Điều này cũng đúng với các bộ phận quản lý vào/ ra và quản lý tập
tin. Trong phần trình bày sau đây chúng tôi chỉ đề cập đến Process Table của hệ điều hành.
Để quản lý và điều khiển được một tiến trình, thì hệ điều hành phải biết được vị trí nạp
tiến trình trong bộ nhớ chính, phải biết được các thuộc tính của tiến trình cần thiết cho việc
quản lý tiến trình của nó:
Định vị của tiến trình (process location): định vị của tiến trình phụ thuộc vào chiến
lược quản lý bộ nhớ đang sử dụng. Trong trường hợp đơn giản nhất, tiến trình, hay chính xác
hơn là hình ảnh tiến trình, được lưu giữa tại các khối nhớ liên tục trên bộ nhớ phụ (thường là
đĩa), để tiến trình thực hiện được thì tiến trình phải được nạp vào bộ nhớ chính. Do đó, hệ
điều hành cần phải biết định vị của mỗi tiến trình trên đĩa và cho mỗi tiến trình đó trên bộ nhớ
chính. Trong một số chiến lược quản lý bộ nhớ, hệ điều hành chỉ cần nạp một phần tiến trình
vào bộ nhớ chính, phần còn lại vẫn nằm trên đĩa. Hay tiến trình đang ở trên bộ nhớ chính thì
có một phần bị swap-out ra lại đĩa, phần còn lại vẫn còn nằm ở bộ nhớ chính. Trong các
trường hợp này hệ điều hành phải theo dõi tiến trình để biết phần nào của tiến trình là đang ở
trong bộ nhớ chính, phần nào của tiến trình là còn ở trên đĩa.
Đa số các hệ điều hành hiện nay đều sử dụng chiến lược quản lý bộ nhớ mà trong đó
26
không gian địa chỉ của tiến trình là một tập các block, các block này có thể không liên tiếp
nhau. Tùy theo chiến lược bộ nhớ sử dụng mà các block này có thể có chiều dài cố định
(chiến lược phân phân trang bộ nhớ) hay thay đổi (chiến lược phân đoạn bộ nhớ) hay kết hợp
cả hai. Hệ điều hành cho phép không nạp tất cả các trang (page) và/hoặc các đoạn (segment)
của tiến trình vào bộ nhớ. Do đó, process table phải được duy trì bởi hệ điều hành và phải cho
biết vị trí của mỗi trang/ đoạn tiến trình trên hệ thống. Những điều trên đây sẽ được làm rõ ở
phần chiến lược cấp phát bộ nhớ trong chương Quản lý bộ nhớ của giáo trình này.
Các thuộc tính của tiến trình: Trong các hệ thống đa chương, thông tin về mỗi tiến
trình là rất cần cho công tác quản lý tiến trình của hệ điều hành, các thông tin này có thể
thường trú trong khối quản lý tiến trình. Các hệ điều hành khác nhau sẽ có cách tổ chức PCB
khác nhau, ở đây chúng ta khảo sát một trường hợp chung nhất. Các thông tin trong PCB có
thể được chia thành ba nhóm chính:
Hình 2.4. Ví dụ một PCB.
Định danh tiến trình (process number, process identification - PID): mỗi tiến trình được
gán một định danh duy nhất để phân biệt với các tiến trình khác trong hệ thống. Định danh
của tiến trình có thể xuất hiện trong memory table, I/O table. Khi tiến trình này truyền thông
với tiến trình khác thì định danh tiến trình được sử dụng để hệ điều hành xác định tiến trình
đích. Khi tiến trình cho phép tạo ra tiến trình khác thì định danh được sử dụng để chỉ đến tiến
trình cha và tiến trình con của mỗi tiến trình. Tóm lại, các định danh có thể lưu trữ trong PCB
bao gồm: định danh của tiến trình này, định danh của tiến trình tạo ra tiến trình này, định danh
của người sử dụng.
Thông tin trạng thái CPU (CPU state information): bao gồm các thanh ghi User-visible,
các thanh ghi trạng thái và điều khiển, các con trỏ stack.
Thông tin điều khiển tiến trình (process control information): bao gồm thông tin trạng
thái và lập lịch, cấu trúc dữ liệu, truyền thông liên tiến trình, quyền truy cập tiến trình, quản lý
bộ nhớ, tài nguyên khởi tạo và tài nguyên sinh ra.
PCB là một trong những cấu trúc dữ liệu trung tâm và quan trọng của hệ điều hành. Mỗi
PCB chứa tất cả các thông tin về tiến trình mà nó rất cần cho hệ điều hành. Có nhiều modun
thành phần trong hệ điều hành có thể read và/hoặc modified PCB như: lập lịch tiến trình, cấp
phát tài nguyên cho tiến trình, ngắt tiến trình, v.v… Có thể nói các thiết lập trong PCB định
27
nghĩa trạng thái của hệ điều hành.
2.2. Các trạng thái tiến trình
Từ khi được đưa vào hệ thống cho đến khi kết thúc tiến trình tồn tại ở các trạng thái
khác nhau. Trạng thái của tiến trình tại một thời điểm được xác định bởi hoạt động hiện thời
của tiến trình tại thời điểm đó. Tiến trình có thể ở một trong các trạng thái sau:
Tạo mới (new): tiến trình vừa được tạo ra.
Sẵn sàng (ready ): Ngay sau khi khởi tạo tiến trình, đưa tiến trình vào hệ thống và cấp
phát đầy đủ tài nguyên (trừ CPU) cho tiến trình, hệ điều hành đưa tiến trình vào trạng thái
ready. Hay nói cách khác, trạng thái ready là trạng thái của một tiến trình trong hệ thống đang
chờ được cấp CPU để bắt đầu thực hiện.
Đang thực thi (running): Là trạng thái mà tiến trình đang được sở hữu CPU để hoạt
động, hay nói cách khác là các chỉ thị của tiến trình đang được thực hiện/ xử lý bởi CPU.
Chờ (waitng): Là trạng thái mà tiến trình đang chờ để được cấp phát thêm tài nguyên,
để một sự kiện nào đó xảy ra, hay một quá trình vào/ra kết thúc.
Kết thúc (terminated): Hoàn tất việc thực thi.
Quá trình chuyển trạng thái của các tiến trình trong được mô tả bởi sơ đồ sau:
Hình 2.5. Sơ đồ chuyển trạng thái của tiến trình.
Trong đó:
Admitted: Tiến trình được khởi tạo, được đưa vào hệ thống, được cấp phát đầy đủ tài
nguyên chỉ thiếu CPU.
Scheduler dispatch: Tiến trình được cấp CPU để bắt đầu thực hiện/ xử lý.
Exit: Tiến trình hoàn thành xử lý và kết thúc.
Interrupt: Tiến trình bị bộ điều phối tiến trình thu hồi CPU, do hết thời gian được quyền
sử dụng CPU, để cấp phát cho tiến trình khác.
I/O or event wait: Tiến trình đang chờ một sự kiện nào đó xảy ra hay đang chờ một thao
vào/ra kết thúc hay tài nguyên mà tiến trình yêu cầu chưa được hệ điều hành đáp ứng.
I/O or event completion: Sự kiện mà tiến trình chờ đã xảy ra, thao tác vào/ra mà tiến
trình đợi đã kết thúc, hay tài nguyên mà tiến trình yêu cầu đã được hệ điều hành đáp ứng,
28
Bộ phận điều phối tiến trình thu hồi CPU từ một tiến trình đang thực hiện trong các
trường hợp sau:
- Tiến trình đang thực hiện hết thời gian (time-out) được quyền sử dụng CPU mà bộ
phận điều phối dành cho nó.
- Có một tiến trình mới phát sinh và tiến trình mới này có độ ưu tiên cao hơn tiến trình
hiện tại.
- Có một tiến trình mới phát sinh và tiến trình này mới cần một khoảng thời gian của
CPU nhỏ hơn nhiều so với khoảng thời gian còn lại mà tiến trình hiện tại cần CPU.
Tại một thời điểm xác định trong hệ thống có thể có nhiều tiến trình đang ở trạng thái
ready hoặc waiting nhưng chỉ có một tiến trình ở trạng thái running. Các tiến trình ở trạng thái
ready và waiting được chứa trong các hàng đợi (queue) riêng.
Có nhiều lý do để một tiến trình đang ở trạng thái running chuyển sang trạng thái
waiting, do đó đa số các hệ điều hành đều thiết kế một hệ thống hàng đợi gồm nhiều hàng đợi,
mỗi hành đợi dùng để chứa những tiến trình đang đợi cùng một sự kiện nào đó.
2.3. Các thao tác điều khiển tiến trình
2.3.1. Khởi tạo tiến trình
Khi tạo mới tiến trình hệ điều hành thực hiện các thao tác sau:
- Hệ điều hành gán PID cho tiến trình mới và đưa tiến trình vào danh sách quản lý của
hệ thống, tức là, dùng một entry trong PCB để chứa các thông tin liên quan đến tiến trình mới
tạo ra này.
- Cấp phát không gian bộ nhớ cho tiến trình. Ở đây hệ điều hành cần phải xác định được
kích thước của tiến trình, bao gồm code, data và stack. Giá trị kích thước này có thể được gán
mặt định dựa theo loại của tiến trình hoặc được gán theo yêu cầu của người sử dụng khi có
một công việc (job) được tạo. Nếu một tiến trình được sinh ra bởi một tiến trình khác, thì tiến
trình cha có thể chuyển kích thước của nó đến hệ điều hành trong yêu cầu tạo tiến trình.
- Khởi tạo các thông tin cần thiết cho khối điều khiển tiến trình như các PID của tiến
trình cha (nếu có), thông tin trạng thái tiến trình, độ ưu tiên của tiến trình, thông tin ngữ cảnh
của CPU (bộ đến chương trình và các thanh ghi khác), vv.
- Cung cấp đầy đủ các tài nguyên cần thiết nhất, trừ CPU, để tiến trình có thể vào trạng
thái ready được hoặc bắt đầu hoạt động được.
- Đưa tiến trình vào một danh sách tiến trình nào đó: ready list, suspend list, waiting list,
vv, sao cho phù hợp với chiến lược điều phối tiến trình hiện tại của bộ phận điều phối tiến
trình của hệ điều hành.
Khi một tiến trình tạo lập một tiến trình con, tiến trình con có thể được cấp phát tài
nguyên bởi chính hệ điều hành, hoặc được tiến trình cha cho thừa hưởng một số tài nguyên
ban đầu của nó.
2.3.2. Kết thúc tiến trình
Khi tiến trình kết thúc xử lý, hoàn thành chỉ thị cuối cùng, hệ điều hành sẽ thực hiện các
thao tác sau đây:
29
- Thu hồi tài nguyên đã cấp phát cho tiến trình.
- Loại bỏ tiến trình ra khỏi danh sách quản lý của hệ thống.
- Huỷ bỏ khối điều khiển tiến trình.
Hầu hết các hệ điều hành đều không cho phép tiến trình con hoạt động khi tiến trình cha
đã kết thúc. Trong những trường hợp như thế hệ điều hành sẽ chủ động việc kết thúc tiến trình
con khi tiến trình cha vừa kết thúc.
2.3.3. Chuyển ngữ cảnh (context switch)
Muốn chuyển quyền sử dụng CPU giữa các tiến trình thì phải thay đổi tiến trình của các
tiến trình. Khi một tiến trình đang ở trạng thái running bị chuyển sang trạng thái khác (ready,
waiting,…) thì hệ điều hành phải tạo ra sự thay đổi trong môi trường làm việc của nó, quá
trình này được gọi là chuyển ngữ cảnh. Sau đây là các bước mà hệ điều hành phải thực hiện
đầy đủ khi thay đổi trạng thái tiến trình:
Hình 2.6. Sơ đồ chuyển ngữ cảnh.
- Lưu (save) ngữ cảnh của CPU, bao gồm thanh ghi bộ đếm chương trình (PC: program
counter) và các thanh ghi khác.
- Cập nhật PCB của tiến trình, sao cho phù hợp với trạng thái mới của tiến trình, bao
gồm trạng thái mới của tiến trình, các thông tin tính toán, vv.
- Di chuyển PCB của tiến trình đến một hàng đợi thích hợp, đế đáp ứng được các yêu
cầu của công tác điều phối tiến trình.
- Chọn một tiến trình khác để cho phép nó thực hiện.
- Cập nhật PCB của tiến trình vừa được chọn thực hiện ở trên, chủ yếu là thay đổi trạng
thái của tiến trình đến trạng thái running.
30
- Cập nhật các thông tin liên quan đến quản lý bộ nhớ. Bước này phụ thuộc vào các yêu
cầu chuyển đổi địa chỉ bộ nhớ đang được sử dụng.
- Khôi phục (Restore) lại ngữ cảnh của CPU và thay đổi giá trị của bộ đếm chương trình
và các thanh ghi khác sao cho phù hợp với tiến trình được chọn ở trên, để tiến trình này có thể
bắt đầu hoạt động được.
Như vậy, khi hệ điều hành chuyển một tiến trình từ trạng thái running (đang chạy) sang
một trạng thái nào đó (tạm dừng) thì hệ điều hành phải lưu trữ các thông tin cần thiết, nhất là
Program Count, để sau này hệ điều hành có thể cho tiến trình tiếp tục hoạt động trở (tái kích
hoạt) lại được. Đồng thời hệ điều hành phải chọn một tiến trình nào đó đang ở trạng thái ready
để cho tiến trình này chạy (chuyển tiến trình sang trạng thái running). Tại đây, trong các thao
tác phải thực hiện, hệ điều hành phải thực hiện việc thay đổi giá trị của PC, thay đổi ngữ cảnh
CPU, để PC chỉ đến địa chỉ của chỉ thị đầu tiên của tiến trình running mới này trong bộ nhớ.
Đây cũng chính là bản chất của việc thực hiện các tiến trình trong các hệ thống một CPU.
2.4. Giao tiếp giữa các tiến trình
2.4.1. Cộng tác giữa các tiến trình
Các tiến trình thực thi trong hệ điều hành có thể là những tiến trình độc lập hay những
tiến trình cộng tác. Một tiến trình là độc lập (independent) nếu nó không thể ảnh hưởng hay bị
ảnh hưởng bởi các tiến trình khác thực thi trong hệ thống.
Rõ ràng, bất kỳ một tiến trình không chia sẻ bất cứ dữ liệu (tạm thời hay cố định) với
tiến trình khác là độc lập. Ngược lại, một tiến trình là cộng tác (cooperating) nếu nó có thể
ảnh hưởng hay bị ảnh hưởng bởi các tiến trình khác trong hệ thống. Hay nói cách khác, bất cứ
tiến trình chia sẻ dữ liệu với tiến trình khác là tiến trình cộng tác. Chúng ta có thể cung cấp
một môi trường cho phép cộng tác tiến trình với nhiều lý do:
- Chia sẻ thông tin: vì nhiều người dùng có thể quan tâm cùng phần thông tin (thí dụ,
tập tin chia sẻ), chúng phải cung cấp một môi trường cho phép truy xuất đồng hành tới những
loại tài nguyên này.
- Gia tăng tốc độ tính toán: nếu chúng ta muốn một tác vụ chạy nhanh hơn, chúng ta
phải chia nó thành những tác vụ nhỏ hơn, mỗi tác vụ sẽ thực thi song song với các tác vụ
khác. Việc tăng tốc như thế có thể đạt được chỉ nếu máy tính có nhiều thành phần đa xử lý
(như các CPU hay các kênh I/O).
- Tính module hóa: chúng ta muốn xây dựng hệ thống trong một kiểu mẫu dạng module,
chia các chức năng hệ thống thành những tiến trình hay luồng như đã thảo luận ở chương
trước.
- Tính tiện dụng: Thậm chí một người dùng đơn có thể có nhiều tác vụ thực hiện tại
cùng thời điểm. Thí dụ, một người dùng có thể đang soạn thảo, in, và biên dịch cùng một lúc.
2.4.2. Giao tiếp giữa các tiến trình
Thực thi đồng hành của các tiến trình cộng tác yêu cầu các cơ chế cho phép các tiến
trình giao tiếp với các tiến trình khác và đồng bộ hóa các hoạt động của chúng. Đó chính là cơ
chế giao tiếp giữa các tiến trình (Interprocess communication - IPC).
Sau đây chúng ta sẽ tìm hai mô hình giao tiếp giữa các tiến trình là chia sẻ bộ nhớ và
31
truyền thông điệp.
Hình 2.7. Mô hình giao tiếp giữa các tiến trình.
(a) truyền thông điệp. (b) chia sẻ bộ nhớ
2.4.3. Mô hình giao tiếp qua chia sẻ bộ nhớ
Để minh họa khái niệm của các quá trình cộng tác, chúng ta xem xét bài toán người sản
xuất - người tiêu thụ, là mô hình chung cho các tiến trình cộng tác. Tiến trình người sản xuất
cung cấp thông tin được tiêu thụ bởi tiến trình người tiêu thụ. Thí dụ, một chương trình in sản
xuất các ký tự được tiêu thụ bởi trình điều khiển máy in. Một trình biên dịch có thể sản xuất
mã hợp ngữ được tiêu thụ bởi trình hợp ngữ. Sau đó, trình hợp ngữ có sản xuất module đối
tượng, được tiêu thụ bởi bộ nạp.
Để cho phép người sản xuất và người tiêu thụ chạy đồng hành, chúng ta phải có sẵn một
vùng đệm chứa các sản phẩm có thể được điền vào bởi người sản xuất và được lấy đi bởi
người tiêu thụ. Người sản xuất có thể sản xuất một sản phẩm trong khi người tiêu thụ đang
tiêu thụ một sản phẩm khác. Người sản xuất và người tiêu thụ phải được đồng bộ để mà người
tiêu thụ không cố gắng tiêu thụ một sản phẩm mà chưa được sản xuất. Trong trường hợp này,
người tiêu thụ phải chờ cho tới khi các sản phẩm mới được tạo ra.
Nếu độ lớn vùng đệm không bị giới hạn (unbounded-buffer), người tiêu thụ có thể phải
chờ các sản phẩm mới nhưng người sản xuất có thể luôn tạo ra sản phẩm mới. Nhưng với
vùng đệm có kích thước giới hạn (bounded-buffer), người tiêu thụ phải chờ nếu vùng đệm
rỗng, và người sản xuất phải chờ nếu vùng đệm đầy.
Vùng đệm có thể được cung cấp bởi hệ điều hành thông qua việc sử dụng bộ nhớ được
chia sẻ. Chúng ta xem xét một giải pháp chia sẻ bộ nhớ đối với vấn đề vùng đệm bị giới hạn
(bounded-buffer). Tiến trình người sản xuất và người tiêu thụ chia sẻ các biến sau:
#define BUFFER_SIZE 10
typedef struct{
…
} item;
32
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
Vùng đệm được chia sẻ được cài đặt như một mảng vòng với hai con trỏ logic: in và
out. Biến in chỉ tới vị trí trống kế tiếp trong vùng đệm; out chỉ tới vị trí đầy đầu tiên trong
vùng đệm. Vùng đệm là rỗng khi in==out; vùng đệm là đầy khi ((in + 1)%BUFFER_SIZE)
==out.
Mã cho tiến trình người sản xuất và người tiêu thụ được trình bày dưới đây. Tiến trình
người sản xuất có một biến nextProduced trong đó sản phẩm mới được tạo ra và được lưu
trữ:
while (1) {
/*produce an item in nextProduced*/
while (((in + 1)%BUFFER_SIZE) ==out)
; /*do nothing*/
buffer[in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
}
Tiến trình người tiêu thụ có biến cục bộ nextConsumed trong đó sản phẩm được tiêu
thụ:
while (1){
while (in==out)
; //nothing
nextConsumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
/*consume the item in nextConsumed*/
}
2.4.3. Mô hình giao tiếp qua truyền thông điệp
Giao tiếp giữa các tiến trình thông qua việc gửi thông điệp. Để hỗ trợ cơ chế liên lạc
bằng thông điệp, hệ điều hành cung cấp các hàm IPC chuẩn, cơ bản là hai hàm:
Send(message), gửi một thông điệp và Receive(message), nhận một thông điệp.
a. Đặt tên
Các tiến trình muốn giao tiếp phải có cách tham chiếu với nhau. Chúng có thể dùng giao
tiếp trực tiếp hay gián tiếp.
Với giao tiếp trực tiếp, mỗi tiến trình muốn giao tiếp phải đặt tên rõ ràng người gửi và
người nhận của giao tiếp. Trong cơ chế này, các hàm cơ sở send và receive được định nghĩa
như sau:
33
Send(P, message): gửi một thông điệp tới tiến trình P
Receive(Q, message): nhận một thông điệp từ tiến trình Q
Một liên kết giao tiếp trong cơ chế này có những thuộc tính sau:
- Một liên kết được thiết lập tự động giữa mỗi cặp tiến trình muốn giao tiếp.
- Các tiến trình cần biết định danh của nhau khi giao tiếp.
- Một liên kết được nối kết với chính xác hai tiến trình.
- Chính xác một liên kết tồn tại giữa mỗi cặp tiến trình.
Với giao tiếp gián tiếp, một thông điệp được gửi tới và nhận từ các hộp thư (mailboxes),
hay cổng (ports). Một hộp thư có thể được hiển thị trừu tượng như một đối tượng trong đó các
thông điệp có thể được đặt bởi các tiến trình và sau đó các thông điệp này có thể được xóa đi.
Mỗi hộp thư có một định danh duy nhất. Trong cơ chế này, một tiến trình có thể giao tiếp với
một vài tiến trình khác bằng một số hộp thư khác nhau. Hai tiến trình có thể giao tiếp chỉ nếu
chúng chia sẻ cùng một hộp thư. Hàm cơ sở send và receive được định nghĩa như sau:
Send(A, message): gửi một thông điệp tới hộp thư A.
Receive(A, message): nhận một thông điệp từ hộp thư A.
Trong cơ chế này, một liên kết giao tiếp có các thuộc tính sau:
- Một liên kết được thiết lập giữa một cặp tiến trình chỉ nếu cả hai thành viên của cặp có
một hộp thư được chia sẻ.
- Một liên kết có thể được nối kết với nhiều hơn hai tiến trình.
- Số các liên kết khác nhau có thể tồn tại giữa mỗi cặp tiến trình giao tiếp với mỗi liên
kết tương ứng với một hộp thư.
b. Đồng bộ hóa
Giao tiếp giữa hai quá trình xảy ra bởi lời gọi hàm cơ sở send và receive. Có các tùy
chọn thiết kế khác nhau cho việc cài đặt mỗi hàm cơ sở. Truyền thông điệp có thể là nghẽn
(block) hay không nghẽn (nonblocking)-cũng được xem như đồng bộ và bất đồng bộ.
Hàm send nghẽn: quá trình gửi bị nghẽn cho đến khi thông điệp được nhận bởi quá trình
nhận hay bởi hộp thư.
Hàm send không nghẽn: quá trình gửi gửi thông điệp và thực hiện tiếp hoạt động.
Hàm receive nghẽn: người nhận nghẽn cho đến khi thông điệp sẵn sàng.
Hàm receive không nghẽn: người nhận nhận lại một thông điệp hợp lệ hay rỗng.
Sự kết hợp khác nhau giữa send và receive là có thể. Khi cả hai hàm send và receive là
nghẽn chúng ta có sự thống nhất giữa người gửi và người nhận.
c. Tạo vùng đệm
Dù giao tiếp có thể là trực tiếp hay gián tiếp, các thông điệp được chuyển đổi bởi
các quá trình giao tiếp thường trú trong một hàng đợt tạm thời. Về cơ bản, một hàng đợi
như thế có thể được cài đặt trong ba cách:
34
- Khả năng chứa là 0 (zero capacity): hàng đợi có chiều dài tối đa là 0; do đó liên kết
không thể có bất kỳ thông điệp nào chờ trong nó. Trong trường hợp này, người gửi phải
nghẽn cho tới khi người nhận nhận thông điệp.
- Khả năng chứa có giới hạn (bounded capacity): hàng đợi có chiều dài giới hạn n; do
đó, nhiều nhất n thông điệp có thể thường trú trong nó. Nếu hàng đợi không đầy khi một
thông điệp mới được gửi, sau đó nó được đặt trong hàng đợi (thông điệp được chép hay một
con trỏ thông điệp được giữ) và người gửi có thể tiếp tục thực thi không phải chờ. Tuy nhiên,
liên kết có khả năng chứa giới hạn. Nếu một liên kết đầy, người gửi phải nghẽn cho tới khi
không gian là sẳn dùng trong hàng đợi
- Khả năng chứa không giới hạn (unbounded capacity): Hàng đợi có khả năng có chiều
dài không giới hạn; do đó số lượng thông điệp bất kỳ có thể chờ trong nó. Người gửi không
bao giờ nghẽn.
2.5. Luồng (Thread)
Trong các hệ điều hành cũ, mỗi tiến trình có một không gian địa chỉ riêng và chỉ một
dòng chạy chương trình (tuần tự chạy các lệnh từ lệnh đầu tiên). Tuy nhiên trong nhiều trường
hợp công việc cần có nhiều dòng chạy chương trình chạy “song song” trong cùng một không
gian địa chỉ.
2.5.1 Khái niệm luồng
Mô hình tiến trình dựa trên hai điều: gom nhóm tài nguyên và chạy chương trình. Gom
nhóm tài nguyên là gom các tài nguyên có liên quan đến một công việc lại với nhau như
không gian địa chỉ để nạp chương trình xử lý công việc, các tập tin được mở liên quan đến
công việc … Chạy chương trình là chạy các mã để thực hiện công việc, đó là dòng chạy
chương trình. Dòng chạy chương trình có bộ đếm chương trình, thanh ghi, ngăn xếp, … riêng.
Trong một tiến trình có thể có nhiều dòng chạy chương trình đồng thời, mỗi dòng chạy
chương trình được gọi là luồng.
Các luồng của một tiến trình sẽ dùng chung môi trường của tiến trình, tức là dùng chung
không gian địa chỉ, các tập tin được mở,… của tiến trình.
Hình 2.8. Luồng.
Tiến trình đa luồng (multithreaded process) là tiến trình có nhiều luồng.
Hình (a): Có ba tiến trình, mỗi tiến trình chỉ có một dòng chạy chương trình và có
35
không gian địa chỉ riêng. Hình (b) có một tiến trình, trong tiến trình có ba luồng. Ba luồng này
có cùng không gian địa chỉ, tài nguyên của tiến trình.
Trong tiến trình đa luồng, các luồng chạy được luân phiên chạy (giống như sự luân
phiên chạy của các tiến trình). Trong khi tiến trình đang chạy, CPU sẽ lần lượt phục vụ cho
các luồng trong tiến trình này.
Các luồng trong một tiến trình thì không hoàn toàn độc lập nhau như giữa các tiến trình.
Các luồng trong một tiến trình dùng chung không gian địa chỉ, nghĩa là các luồng có thể dùng
chung các biến toàn cục, luồng có thể truy xuất đến bất cứ địa chỉ nào trong không gian địa
chỉ của tiến trình, luồng có thể đọc, ghi lên cả ngăn xếp của luồng khác trong cùng tiến trình.
Không có cơ chế bảo vệ giữa các luồng vì không thể làm được, và không cần thiết vì các
luồng trong một tiến trình cùng phối hợp nhau để thực hiện một công việc nào đó (trách
nhiệm thuộc về người lập trình), điều này khác với tiến trình vì các tiến trình là những chương
trình thực hiện các công việc khác nhau (của những người viết khác nhau).
2.5.2. Cài đặt luồng
a. Cài đặt luồng mức người dùng
Có hai hướng chính cài đặt luồng: trong mức người dùng và trong kernel. Khi cài đặt
luồng trong mức người dùng thì kernel không biết gì về luồng, kernel chỉ biết tiến trình (có
một luồng). Thuận lợi khi cài đặt luồng ở mức người dùng là luồng có thể cài đặt luồng trong
những hệ điều hành không hỗ trợ luồng.
Mỗi tiến trình có riêng bảng luồng để lưu trữ thông tin về các luồng trong tiến trình đó.
Bảng luồng tương tự như bảng tiến trình của nhân, lưu giữ bộ đếm chương trình của luồng,
con trỏ ngăn xếp, thanh ghi, trạng thái luồng … Khi luồng chuyển về trạng thái sẵn sàng chạy
hoặc bị treo thì các thông tin của luồng sẽ được lưu lại.
Hình 2.9. Luồng ở mức người dùng.
Khi một luồng thực hiện một lệnh có thể dẫn đến bị treo (như đợi một luồng khác), nó
gọi thủ tục hệ thống đang chạy (run-time), thủ tục này lưu các thanh ghi vào bảng luồng và
bắt đầu cho luồng khác chạy. Trong tiến trình có một hệ thống đang chạy (run-time system).
36
Khi một luồng ngưng chạy như gọi hàm luồng_yeild, hàm này sẽ lưu các thông tin của
luồng vào trong bảng luồng, sau đó gọi bộ lập lịch luồng để cho luồng khác chạy. Vì bộ lập
lịch luồng là thủ tục cục bộ, không phải gọi hàm hệ thống nên thực hiện nhanh. Mỗi tiến trình
có thể có giải thuật lập lịch luồng riêng.
Tuy nhiên cài đặt luồng trong mức người dùng có những điểm hạn chế sau:
Cài đặt các hàm gọi hệ thống blocking (blocking system call) ? Khi một luồng thực hiện
gọi hệ thống (blocking) thì tất cả các luồng trong tiến trình đều bị ngưng. Điều này dẫn đến
phải có các hàm gọi hệ thống không blocking (nonblocking system call), và làm ảnh hưởng
rất nhiều đến các chương trình khác.
Khi một luồng chạy thì các luồng trong tiến trình đó không có cơ hội để chạy trừ khi
luồng đang chạy tự nguyện nhường CPU vì trong một tiến trình không có ngắt thời gian nên
bộ lập lịch luồng không thể chạy được.
b. Cài đặt luồng ở mức nhân
Trong tiến trình không có bảng luồng, kernel sẽ có bảng luồng lưu giữ thông tin của tất
cả các luồng trong hệ thống. Khi một luồng được tạo hay hủy bỏ thì gọi hàm hệ thống, do vậy
thời gian thực hiện chậm.
Khi một luồng thực hiện gọi hệ thống, trong trường hợp blocking thì luồng này sẽ bị
treo, và kernel sẽ cho luồng khác trong cùng tiến trình chạy nếu có, hoặc luồng của tiến trình
khác chạy. Do vậy các luồng không cần thiết phải có những hàm gọi thống không blocking.
Hình 2.10. Luồng ở mức nhân
c. Kết hợp
Kết hợp cách cài đặt luồng trong mức người dùng và mức nhân. Luồng có thể được cài
dặt theo một trong các mô hình sau:
Mô hình many-to-one
Nhiều luồng mức người dùng “chia sẻ” một luồng mức nhân để thực thi. Việc quản lý
luồng được thực hiện thông qua các hàm của một thư viện luồng được gọi ở mức người dùng.
Khi một luồng bị treo thì luồng mức nhân cũng bị treo, do đó mọi luồng khác của tiến
trình cũng sẽ bị treo.
37
Hình 2.11. Mô hình many-to-one.
Có thể được hiện thực đối với hầu hết các hệ điều hành.
Mô hình one-to-one
Hình 2.12. Mô hình one-to-one.
Mỗi luồng ở mức người dùng thực thi thông qua một luồng ở mức nhân riêng của nó.
Mỗi khi một luồng ở mức người được tạo ra thì cũng cần tạo một luồng ở mức nhân tương
ứng.
Hệ điều hành phải có cơ chế cung cấp được nhiều luồng ở mức nhân cho một tiến trình.
Mô hình many-to-many
Hình 2.11. Mô hình many-to-many.
38
Nhiều ở mức người dùng được phân chia thực thi (multiplexed) trên một số luồng ở
mức nhân.
Tránh được một số khuyết điểm của hai mô hình many-to-one và one-to-one
CÂU HỎI ÔN TẬP
1. Phân biệt chương trình với tiến trình.
2. Nêu các trạng thái của tiến trình.
3. Xác định các thành phần chính của khối điều kiển tiến trình.
4. Tại sao phải giao tiếp giữa các tiến trình. Nêu các cơ chế giao tiếp giữa các tiến
trình.
5. Trình bày các mô hình luồng.
TÀI LIỆU THAM KHẢO
1. Hồ Đắc Phương. Giáo trình nguyên lý hệ điều hành. Nhà xuất bản Giáo dục, 2009.
Chương 4.
2. A.Silberschatz, P. B. Galvin, G. Gagne. Operating System Concepts, 8th Edition.
John Wiley & Sons, 2009. Chương 3,4.
3. William Stallings. Operating Systems: Internals and Design Principles (6th
Edition). Pearson Prentice Hall, 2009. Chương 3,4.
39
CHƯƠNG 3. LẬP LỊCH CPU
3.1. Các khái niệm cơ bản
3.1.1. Chu kỳ CPU-I/O
Sự thành công của việc lập lịch CPU phụ thuộc vào thuộc tính được xem xét sau đây
của quá trình. Việc thực thi quá trình chứa một chu kỳ (cycle) thực thi CPU và chờ đợi vào/ra.
Các quá trình chuyển đổi giữa hai trạng thái này. Sự thực thi quá trình bắt đầu với một chu kỳ
CPU (CPU burst), theo sau bởi một chu kỳ vào/ra (I/O burst), sau đó một chu kỳ CPU khác,
sau đó lại tới một chu kỳ vào/ra khác khác, Sau cùng, chu kỳ CPU cuối cùng sẽ kết thúc với
một yêu cầu hệ thống để kết thúc việc thực thi, hơn là với một chu kỳ vào/ra khác, được mô tả
như hình 3.1. Một chương trình hướng vào/ra (I/O-bound) thường có nhiều chu kỳ CPU ngắn.
Một chương trình hướng xử lý (CPU-bound) có thể có một nhiều chu kỳ CPU dài. Sự phân bổ
này có thể giúp chúng ta chọn giải thuật lập lịch CPU hợp lý.
Hình 3.1. Sơ đồ thực hiện tiến trình.
3.1.2. Phân loại các hoạt động lập lịch
Lập lịch dài hạn (long-term scheduling): xác định tiến trình nào được chấp nhận vào hệ
40
thống. Quyết định độ-đa-lập-trình (degree of multiprogramming) Nếu càng nhiều tiến trình
được đưa vào hệ thống thì: khả năng các process bị block có xu hướng giảm; sử dụng CPU
hiệu quả hơn; mỗi process được phân chia khoảng thời gian sử dụng CPU thấp hơn.
Thường có xu hướng đưa vào một tập lẫn lộn các tiến trình CPU-bound và tiến trình
I/O-bound.
Hình 3.2. Các hoạt động lập lịch.
Lập lịch trung hạn (medium-term scheduling): xác định process nào được đưa vào
(swap in), đưa ra khỏi (swap out) bộ nhớ chính. Phụ thuộc vào yêu cầu quản lý việc đa-lập-
trình (multiprogramming). Cho phép bộ lập lịch dài hạn chấp nhận nhiều process hơn số
lượng process mà có tổng kích thước được chứa vừa trong bộ nhớ chính. Nhưng nếu có quá
nhiều process thì sẽ làm tăng việc truy xuất đĩa, do đó cần phải lựa chọn độ-đa-lập-trình cho
phù hợp. Được thực hiện bởi phần mềm quản lý bộ nhớ.
Lập lịch ngắn hạn (short-term scheduling): xác định process nào được thực thi tiếp theo
nên còn được gọi là lập lịch CPU. Được kích hoạt khi có một sự kiện có thể dẫn đến khả năng
chọn một process để thực thi:ngắt thời gian, ngắt ngoại vi, lời gọi hệ thống…
3.1.3. Hai thành phần của chiến lược lập lịch
a. Hàm lựa chọn (selection function)
Xác định process nào trong ready queue sẽ được thực thi tiếp theo. Thường theo các tiêu
chí như: tổng thời gian đợi trong hệ thống, thời gian đã được phục vụ, tổng thời gian thực thi
của tiến trình.
b. Chế độ quyết định (decision mode)
Chọn thời điểm hàm lựa chọn lập lịch thực thi. Có hai chế độ:
- Nonpreemptive (độc quyền): Một tiến trình sẽ ở trạng thái running cho đến khi nó bị
block hoặc nó kết thúc.
- Preemptive (không độc quyền): tiến trình đang thực thi có thể bị ngắt và chuyển về
41
trạng thái sẵn sàng nhằm tránh trường hợp một tiến trình độc chiếm CPU.
Hàm lập lịch có thể được thực thi khi có tiến trình:
(1) chuyển từ trạng thái đang thực thi sang chờ.
(2) chuyển từ trạng thái đang thực thi sang sẵn sàng.
(3) chuyển từ trạng thái chờ, mới sang sẵn sàng.
(4) kết thúc thực thi.
Lập lịch nonpreemptive: chỉ thực thi hàm lập lịch trong trường hợp 1 và 4
Lập lịch preemptive: ngoài trường hợp 1 và 4 còn thực thi thêm hàm lập lịch trong
trường hợp 2 hoặc 3 (hoặc cả hai).
3.1.4. Bộ điều phối (dispatcher)
Bộ điều phối sẽ chuyển quyền điều khiển CPU về cho tiến trình được chọn bởi bộ lập
lịch ngắn hạn. Chức năng này thực hiện:
- Chuyển ngữ cảnh (sử dụng thông tin ngữ cảnh trong PCB).
- Chuyển về chế độ người dùng.
- Nhảy đến vị trí thích hợp trong chương trình ứng dụng để khởi động lại chương trình
(chính là program counter trong PCB).
Thời gian để bộ điều phối dừng một tiến trình này và bắt đầu chạy một tiến trình khác
được gọi là thời gian trễ cho việc điều phối (dispatch latency).
3.2. Tiêu chí lập lịch
3.2.1. Tiêu chuẩn lập lịch
Để so sánh và đánh giá các giải thuật lập lịch có thể dựa trên các tiêu chuẩn khác nhau.
Các tiêu chuẩn bao gồm:
Hiệu suất sử dụng CPU (CPU utilization): thời gian vô ích của CPU càng ít càng tốt.
Việc sử dụng CPU có thể từ 0 đến 100%.
Thông lượng (throughput): là số lượng tiến trình được hoàn thành trên một đơn vị thời
gian. gọi là thông lượng Đối với các tiến trình dài, tỉ lệ này có thể là 1 tiến trình trên 1 giờ; đối
với các giao dịch ngắn, thông lượng có thể là 10 tiến trình trên giây.
Thời gian hoàn thành (turnaround time): Thời gian để một tiến trình hoàn tất, kể từ lúc
vào hệ thống đến lúc kết thúc. Thời gian hoàn thành là tổng các thời gian chờ đưa tiến trình
vào bộ nhớ, chờ hàng đợi sẳn sàng, thực thi CPU và thực hiện vào/ra.
Thời gian chờ (waiting time): là tổng thời gian tiến trình chờ trong hàng đợi sẵn sàng.
Thời gian đáp ứng (response time): Thời gian từ lúc có yêu cầu của người dùng đến khi
có đáp ứng đầu tiên.
3.2.2. Mục tiêu điều phối:
Bộ điều phối tiến trình của hệ điều hành phải đạt được các mục tiêu sau đây trong công
tác điều phối:
42
Sự công bằng (Fairness): Các tiến trình đều công bằng với nhau trong việc chia sẻ thời
gian xử lý của CPU, không có tiến trình nào phải chờ đợi vô hạn để được cấp CPU.
Tính hiệu quả: Tận dụng được 100% thời gian xử lý của CPU. Trong công tác điều
phối, khi CPU rỗi bộ điều phối sẽ chuyển ngay nó cho tiến trình khác, nếu trong hệ thống có
tiến trình đang ở trạng thái chờ CPU, nên mục tiêu này dễ đạt được. Tuy nhiên, nếu hệ điều
hành đưa vào hệ thống quá nhiều tiến trình thiên hướng vào/ra, thì nguy cơ CPU bị rỗi là có
thể. Do đó, để đạt được mục tiêu này hệ điều hành phải tính toán và quyết định nên đưa vào
hệ thống bao nhiêu tiến trình hướng vào/ra, bao nhiêu tiến trình hướng xử lý, là thích hợp.
Thời gian đáp ứng hợp lý: Một tiến trình đáp ứng yêu cầu của người sử dụng, phải nhận
được thông tin hồi đáp từ yêu cầu của nó thì nó mới có thể trả lời người sử dụng. Do đó, theo
người sử dụng thì bộ phận điều phối phải cực tiểu hoá thời gian đáp ứng của các tiến trình, có
như vậy thì tính tương tác của tiến trình mới tăng lên.
Thời gian lưu lại trong hệ thống: Phải cực tiểu thời gian hoàn thành nhất là các tác vụ
xử lý theo lô.
Thông lượng tối đa: Chính sách điều phối phải cố gắng để cực đại được số lượng tiến
trình hoàn thành trên một đơn vị thời gian. Mục tiêu này ít phụ thuộc vào chính sách điều phối
mà phụ thuộc nhiều vào thời gian thực hiện trung bình của các tiến trình.
Công tác điều phối của hệ điều hành khó có thể thỏa mãn đồng thời tất cả các mục tiêu
trên vì bản thân các mục tiêu này đã có sự mâu thuẫn với nhau. Các hệ điều hành chỉ có thể
dung hòa các mục tiêu này ở một mức độ nào đó.
3.3. Các thuật toán lập lịch
3.3.1. Đến trước phục vụ trước (First Come Pirst Served - FCFS)
Trong thuật toàn này, độ ưu tiên phục vụ tiến trình căn cứ vào thời điểm hình thành tiến
trình. Việc cài đặt thuật toán FCFS được quản lý dễ dàng với hàng đợi FIFO Khi CPU rỗi thì
hệ điều hành sẽ cấp nó cho tiến trình đầu tiên trong hàng đợi sẵn sàng, đây là tiến trình được
chuyển sang trạng thái sẵn sàng sớm nhất, có thể là tiến trình được đưa vào hệ thống sớm
nhất. FIFO được sử dụng trong điều phối độc quyền nên khi tiến trình được cấp CPU nó sẽ sở
hữu CPU cho đến khi kết thúc xử lý hay phải đợi một thao tác vào/ra hoàn thành, khi đó tiến
trình chủ động trả lại CPU cho hệ thống.
Ví dụ: cho các tiến trình sau đến ở thời điểm 0 theo thứ tự P
1
, P
2
, P
3
có thời gian xử lý
như sau:
Tiến trình Thời gian xử lý (ms)
P
1
24
P
2
3
P
3
3
Giản đồ Gantt cho việc lập lịch là:
43
Thời gian đợi cho P
1
= 0, P
2
= 24, P
3
= 27
Thời gian đợi trung bình: (0 + 24 + 27)/3 = 17
Nếu các tiến trình đến theo thứ tự: P
2
, P
3
, P
1
thì giản đồ Gantt cho việc lập lịch là:
Thời gian đợi cho P
1
= 6,
P
2
= 0,
P
3
= 3. Thời gian đợi trung bình là: (6 + 0 + 3)/3 = 3.
Tốt hơn rất nhiều so với trường hợp trước.
Ưu điểm của thuật toán này là thời gian CPU không bị phân phối lại (không bị ngắt) và
chi phí tổ chức thực hiện thấp nhất (vì khỏng phải thay đồỉ thứ tự ưu tiên phục vụ, thứ tự trư
tiên là thứ tự của tiến trình trong hàng đợi).
Nhược điểm của thuật toán là thời gian trung bình chờ phục vụ của các tiến trình là như
nhau không kể tiến trình ngắn hay đài), do đó dẫn tới ba nhược điểm sau:
- Thời gian chờ đời trung bình sẽ tăng vô hạn khi hệ thống tiếp cận tới hạn khả năng
phục vụ của mình.
- Nếu độ phát tán thời gian thực hiện tiến trình tăng thì thời gian chờ đợi trung bình
cũng tăng theo.
- Khi có tiến trình dài, ít bị ngắt thì các tiến trình khác phải chờ đợi lâu hơn.
3.3.2. Công việc ngắn nhất trước (Shortest Job First - SJF)
Thuật toán SJF xác định thứ tự ưu tiên thực hiện tiến trình tựa vào tổng thời gian thực
hiện tiến trình. Tiến trình nào có tổng thời gian thực hiện ngắn sẽ được ưu tiên phục vụ trước.
Ví dụ: cho dãy tiến trình có thời điểm đến và thời gian xử lý như sau:
Tiến trình Thời điểm đến Thời gian xử lý(ms)
P
1
0.0 7
P
2
2.0 4
P
3
4.0 1
P
4
5.0 4
Giản đồ Gantt khi lập lịch theo SJF là:
Thời gian đợi trung bình = (0 + 6 + 3 + 7)/4 = 4
Ưu điểm của thuật toán là thời gian chờ đợi trung bình của các tiến trình ngắn hơn so
với FCFS. SJF nhanh chóng loại bỏ các tiến trình ngắn giảm số lượng các tiến trình trong
hàng đợi.
Nhược điểm chính của thuật toán là chế độ phân phối lại thời gian CPU cũng được áp
44
dụng trong trường hợp ngắt các tiến trình dài đang thực hiện để phục vụ các tiến trình ngắn
hơn mới mất hiện trong hàng đợi. Nếu tiến trình mới xuất hiện có tổng thời gian thực hiện
ngắn nhưng vẫn lớn hơn thời gian cần thiết để thực hiện nốt tiến trình đang thực hiện thì việc
ngắt tiến trình là không hợp lý.
Khó khăn thật sự với thuật toán SJF là làm thế nào để biết chiều dài của yêu cầu CPU
tiếp theo. Một kỹ thuật thường dùng để dự đoán thời gian sử dụng CPU là dùng trung bình
hàm mũ (exponential averaging). Chu kỳ CPU kế tiếp thường được đoán như trung bình số
mũ của chiều dài các chu kỳ CPU trước đó. Gọi t
n
là chiều dài của chu kỳ CPU thứ n và gọi
τ
n+1
giá trị được đoán cho chu kỳ CPU kế tiếp. Thì đối với α, với 0 ≤ α ≤ 1, định nghĩa:
τ
n+1
= α t
n
+ (1 - α) τ
n
Thay thế ta có:
τ
n+1
= αt
n
+ (1- α) α t
n-1
+…+ (1- α)
j
αt
n-j
+…+ (1- α)
n+1
ατ
0
Nếu chọn α = ½ thì có nghĩa là trị đo được t
n
và trị dự đoán τ
n
được xem quan trọng
như nhau.
Hình 3.3. dự đoán thời gian sử dụng CPU.
3.3.3. Công việc còn lại ngắn nhất trước (Shortest Remaining Time First - SRTF)
Tương tự như SJF nhưng trong thuật toán này, độ ưu tiên thực hiện các tiến trình dựa
vào thời gian cần thiết để thực hiện nốt tiền trình (bằng tổng thời gian trừ đi thời gian đã thực
hiện). Như vậy, trong thuật toàn này cần phải thường xuyên cặp nhật thông tin về thời gian đã
thực hiện tiến trình. Đồng thời, chế độ phân bố lại thời CPU cũng phải được áp dụng nếu
không sẽ làm mất tính tưu việt của thuật toán. Đây có thể coi là phiên bản của SJF theo chế độ
không độc quyền.
Ví dụ: cho dãy tiến trình có thời điểm đến và thời gian phục vụ như sau:
Tiến trình Thời điểm đến Thời gian xử lý(ms)
P
1
0.0 7
P
2
2.0 4
P
3
4.0 1
45
P
4
5.0 4
Giản đồ Gantt khi lập lịch theo SRTF là:
Thời gian đợi trung bình = (9 + 1 + 0 + 2)/4 = 3. Tốt hơn giải thuật SJF.
3.3.4. Lập lịch xoay vòng (Round Robin - RR)
Trong thuật toán này, hệ thống quy định một lượng tử thời gian (time quantum) khoảng
từ 10 - 100 mili giây.
Mỗi tiến trình trong hàng đợi lần lượt được phân phối một lượng tử thời gian để thực
hiện. Sau khoảng thời gian đó, nếu tiến trình chưa kết thúc hoặc không rơi vào trạng thái đợi
thì nó được chuyển về cuối hàng đợi.
Hàng đợi các tiến trình được tổ chức theo kiểu vòng tròn và các tiến trình luôn luôn đảm
bảo được phục vụ. Khi có tiến trình mới phát sinh, nó sẽ được đưa vào hàng đợi vòng tròn.
Các tiến trình dù ngắn hay dài đều có độ ưu tiên phục vụ như nhau.
Nhược điềm là do phải thường xuyên phân phối lại CPU nên thời gian chờ đợi trung
bình của RR có thể lớn hơn so với FCFS.
Chú ý: Trong thuật toán, cần chọn giá trị lượng tử thời gian thích hợp. Nếu chọn giá trị
lượng tử thời gian lớn thì việc bố sung tiến trình mới hoặc kích hoạt tiến trình bị ngắt sẽ làm
tăng thời gian chờ đợi trung bình, nhưng ngược lại nếu chọn giá trị lượng tử thời gian nhỏ thì
nó sẽ làm cho các tiến trình phải liên tục chuyển trạng thái đầu đến giảm hệ số hữu ích của
CPU.
Thông thường giá trị lượng tử thời gian được chọn theo công thức q = t/n hoặc q = t/n -
s. Trong đó t là thời gian khống chế trước; n là số tiến trình; s là thời gian chuyển từ tiến trình
này sang tiến trình khác (chuyển ngữ cảnh).
Ví dụ: cho dãy tiến trình có thời điểm đến và thời gian xử lý như sau:
Tiến trình Thời gian xử lý(ms)
P
1
53
P
2
17
P
3
68
P
4
24
Với lượng tử thời gian q = 20 ms
Giản đồ Gantt theo thuật toán RR là:
Ta có thể thấy thuật toán RR thường có thời gian quay vòng cao hơn SJF, nhưng lại có