- Đọc/Ghi ngày, tháng, năm
b) Sử dụng cấu trúc Semaphore:
Semaphore là cấu trúc được Dijkstra đề xuất vào 1965, semaphore s là một biến cĩ các thuộc tính sau:
- Một giá trị nguyên dương e
- Một hàng đợi f lưu danh sách các tiến trình đang chờ trên semaphore s - Cĩ hai thao tác được định nghĩa trên semaphore s:
Down(s): e=e-1. Nếu e < 0 thì tiến trình phải chờ trong f, ngược lại tiến trình tiếp tục. Up(s): e=e+1. Nếu e<=0 thì chọn một tiến trình trong f cho tiếp tục thực hiện (đánh thức). Gọi P là tiến trình thực hiện thao tác Down(s) hay Up(s):
Down(s)
{ e = e - 1; if (e < 0)
{ status(P)= blocked; //chuyển P sang trạng thái bị khố (chờ) enter(P,f); //cho P vào hàng đợi f
} } Up(s) { e = e + 1; if (e<= 0 ) {
exit(Q,f); //lấy một tt Q ra khỏi hàng đợi f theo một thuật tốn nào đĩ (FIFO,…) status (Q) = ready; //chuyển Q sang trạng thái sẵn sàng
enter(Q,ready-list); //đưa Q vào danh sách sẵn sàng của hệ thống }
Nhận xét:
- Hệđiều hành cần cài đặt các thao tác Down, Up là độc quyền. Để cài đặt sự độc quyền cĩ thể dùng kỹ thuật cấm ngắt (1 cpu) hoặc các giải pháp phần mềm, hoặc lệnh TSL (nhiều cpu). Nếu dùng giải pháp phần mềm thì giải pháp semaphore vẫn là giải pháp "busy and waiting" nhưng tách vịng lặp chờ ra khỏi chương trình.
- Hàng đợi cĩ thể cài đặt là một con trỏ trỏ tới danh sách các khối PCB của các tiến trình đang chờ trên s, khi đĩ semaphore cĩ dạng:
class semaphore { int e; PCB * f; //ds riêng của semaphore public: down(); up(); };
|e| = số tiến trình đang chờ trên f.
Cĩ thể dùng semaphore để giải quyết bài tốn miền găng hay đồng bộ các tiến trình.
* Giải quyết bài tốn miền găng bằng Semaphores:
Dùng một semaphore s, e được khởi gán là 1. Tất cả các tiến trình áp dụng cùng cấu trúc chương trình sau: semaphore s=1; //nghĩa là e của s=1 while (1) { Down(s); critical-section (); Up(s); Noncritical-section (); }
* Giải quyết bài tốn đồng bộ bằng Semaphores:
Ví dụ cĩ hai tiến trình đồng hành P1 và P2, P1 thực hiện cơng việc 1, P2 thực hiện cơng việc 2. Giả sử muốn cv1 làm trước rồi mới làm cv2, ta cĩ thể cho hai tiến trình dùng chung một semaphore s, khởi gán e(s)= 0:
semaphore s=0; //dùng chung cho hai tiến trình P1:
{
job1();
Up(s); //đánh thức P2 }
P2:
{
Down(s); // chờ P1 đánh thức job2();
}
Nhận xét: Nhờ lệnh down, up là độc quyền, semaphore đã giải quyết được vấn đề tín hiệu "đánh thức" bị thất lạc. Tuy nhiên sử dụng semaphore cũng khơng đơn giản, chương trình dễ bị lỗi mà khơng dựđốn được. Ta xét một số tình huống gây ra lỗi sau:
- Nếu đặt Down và Up sai vị trí hoặc thiếu thì cĩ thể bị sai. Ví dụ nếu P1 để Up() lên trước lệnh job1() thì nếu P1 thực hiện trước P2, cĩ thể job1 chưa thực hiện xong mà job2 được thực hiện. Xét ví dụ khác
e(s)=1; while (1) {
Down(s); critical-section (); Noncritical-section (); }
Tiến trình quên gọi Up(s), và kết quả là khi ra khỏi miền găng nĩ sẽ khơng cho tiến trình khác vào miền găng!
- Sử dụng semaphore cĩ thể gây ra tình trạng tắc nghẽn. Ví dụ cĩ hai tiến trình P1, P2 sử dụng chung hai semaphore s1=s2=1
P1: { down(s1); down(s2); …. up(s1); up(s2); } P2: { down(s2); down(s1); …. up(s2); up(s1); }
Nếu thứ tự thực hiện như sau: P1: down(s1), P2: down(s2) , P1: down(s2), P2: down(s1) khi đĩ s1=s2=-1 nên P1,P2 đều chờ mãi
- Sử dụng semaphore cĩ thể gây ra tình trạng đĩi CPU khi giải thuật chọn tiến trình đang đợi là giải thuật LIFO.
Semaphore xây dựng như trên gọi là semaphore đếm (counting semaphore) giá trị e khơng giới hạn. Semaphore nhị phân (binary semaphore) cĩ e=0,1 dễ cài đặt hơn vì được sự hỗ trợ của phần cứng. Semaphore đếm cĩ thể cài đặt bằng semaphore nhị phân như sau:
//các biến dùng chung
binary semaphore s1=1, s2=0;
int c=giá trị ban đầu e của semaphore đếm; down() //down of counting semaphore {
down(s1); //down of binary semaphore c--;
if (c<0) down(s2); //down of binary semaphore up(s1); //up of binary semaphore
}
up() //up of counting semaphore {
down(s1); //down of binary semaphore c++;
if (c<=0) up(s2); //up of binary semaphore up(s1);
}
down(s1); up(s1); đểđảm bảo độc quyền truy xuất miền găng .