Cờ hiệu (semaphore)

Một phần của tài liệu Bài giảng Hệ Điều Hành_HVCNBCVT_PGS.TS Từ Minh Phương (Trang 54)

Một giải phỏp loại trừ tương hỗ khỏc khụng phụ thuộc vào sự hỗ trợ của phần cứng (dưới dạng cỏc lệnh kiểm tra và xỏc lập trỡnh bày ở trờn), ủồng thời tương ủối dễ sử dụng là

cờ hiệu hay ủốn hiệu (semaphore) do Dijkstra ủề xuất.

Cờ hiệu S là một biến nguyờn ủược khởi tạo một giỏ trị ban ủầu nào ủú, bằng khả năng phục vụ ủồng thời của tài nguyờn. Trừ thao tỏc khởi tạo, giỏ trị của cờ hiệu S chỉ cú thể thay

ủổi nhờ gọi hai thao tỏc là WaitSignal. Cỏc tài liệu trước ủõy sử dụng ký hiệu P - viết tắt cho từ Ộkiểm traỢ trong tiếng đức - cho thao tỏc Wait, và V - viết tắt của từ ỘtăngỢ trong tiếng

đức - cho thao tỏc Signal. Hai thao tỏc này cú ý nghĩa như sau:

- Wait(S): Giảm S ủi một ủơn vị. Nếu giỏ trị của S õm sau khi giảm thỡ tiến trỡnh gọi thao tỏc P(S) sẽ bị phong tỏa (blocked). Nếu giỏ trị của S khụng õm, tiến trỡnh sẽủược thực hiện tiếp.

- Signal(S): Tăng S lờn một ủơn vị. Nếu giỏ trị S nhỏ hơn hoặc bằng 0 sau khi tăng thỡ một trong cỏc tiến trỡnh ủang bị phong tỏa (nếu cú) sẽủược giải phúng và cú thể thực hiện tiếp.

điểm ủầu tiờn cần lưu ý là hai thao tỏc Wait và Signal là những thao tỏc nguyờn tử, khụng bị phõn chia. Trong thời gian tiến trỡnh thực hiện thao tỏc như vậy ủể thay ủổi giỏ trị cờ

hiệu, thao tỏc sẽ khụng bị ngắt giữa chừng.

Khi tiến trỡnh bị phong tỏa, tiến trỡnh sẽ chuyển sang trạng thỏi chờủợi cho ủến khi hết bị phong tỏa mới ủược phộp thực hiện tiếp. Cỏc tiến trỡnh bị phong tỏa ủược xếp vào hàng ủợi của cờ hiệu.

Xõy dựng cờ hiệu

Cờ hiệu cú thểủược xõy dựng dưới dạng một cấu trỳc trờn ngụn ngữ C với hai thao tỏc Wait và Signal như sau:

struct semaphore { int value;

process *queue;//danh sỏch chứa cỏc tiến trỡnh bị phong tỏa

};

void Wait(semaphore& S) {

S.value--;

if (S.value < 0) {

Thờm tiến trỡnh gọi Wait vào S.queue

block(); //phong tỏa tiến trỡnh

} void Signal(semaphore& S) { S.value++; if (S.value <= 0) { Lấy một tiến trỡnh P từ S.queue wakeup(P); } } Hỡnh 2.12. định nghĩa cờ hiệu trờn C

Mỗi cờ hiệu cú một giỏ trị và một danh sỏch queue chứa tiến trỡnh bị phong tỏa. Thao tỏc block() trong Wait phong tỏa tiến trỡnh gọi Wait và thao tỏc wakeup() trong Signal khụi phục tiến trỡnh phong tỏa về trạng thỏi sẵn sàng. Hai thao tỏc block và wakeup ủược thực hiện nhờ những lời gọi hệ thống của hệủiều hành.

Danh sỏch tiến trỡnh bị phong tỏa queue cú thể xõy dựng bằng những cỏch khỏc nhau, chẳng hạn dưới dạng danh sỏch kết nối cỏc PCB của tiến trỡnh. Việc chọn một tiến trỡnh từ

danh sỏch khi thực hiện Signal cú thể thực hiện theo nguyờn tắc FIFO hoặc theo những thứ tự

khỏc. Cần lưu ý rằng việc sử dụng FIFO sẽủảm bảo ủiều kiện chờủợi cú giới hạn, tức là tiến trỡnh chỉ phải chờủợi một thời gian giới hạn trước khi vào ủoạn nguy hiểm.

Cỏch sử dụng cờ hiệu

Cờ hiệu ủược tiến trỡnh sử dụng ủể gửi tớn hiệu trước khi vào ủoạn nguy hiểm và sau khi ra khỏi ủoạn nguy hiểm. đầu tiờn, cờ hiệu ủược khởi tạo một giỏ trị dương hoặc bằng khụng. Mỗi cờ hiệu với giỏ trịủầu dương thường dựng ủể kiểm soỏt việc truy cập một tài nguyờn với khả năng phục vụ ủồng thời một số lượng hữu hạn tiến trỡnh. Vớ dụ, tại mỗi thời ủiểm tài nguyờn như mỏy in chỉ cho phộp một tiến trỡnh ghi thụng tin, và cờ hiệu dựng cho mỏy in

ủược khởi tạo bằng 1.

Khi tiến trỡnh cần truy cập tài nguyờn, tiến trỡnh thực hiện thao tỏc Wait của cờ hiệu tương ứng. Nếu giỏ trị cờ hiệu õm sau khi giảm cú nghĩa là tài nguyờn ủược sử dụng hết khả

năng và tại thời ủiểm ủú khụng phục vụ thờm ủược nữa. Do vậy, tiến trỡnh thực hiện Wait sẽ

bị phong tỏa cho ủến khi tài nguyờn ủược giải phúng. Nếu tiến trỡnh khỏc thực hiện Wait trờn cờ hiệu, giỏ trị cờ hiệu sẽ giảm tiếp. Giỏ trị tuyệt ủối của cờ hiệu õm tương ứng với số tiến trỡnh bị phong tỏa.

Sau khi dựng xong tài nguyờn, tiến trỡnh thực hiện thao tỏc Signal trờn cựng cờ hiệu. Thao tỏc này tăng giỏ trị cờ hiệu và cho phộp một tiến trỡnh ủang phong tỏa ủược thực hiện tiếp.

Nhờ việc phong tỏa cỏc tiến trỡnh chưa ủược vào ủoạn nguy hiểm, việc sử dụng cờ hiệu trỏnh cho tiến trỡnh khụng phải chờủợi tớch cực và do vậy tiết kiệm ủược thời gian sử dụng CPU.

Loại trừ tương hỗ ủược thực hiện bằng cỏch sử dụng cờ hiệu như thể hiện trờn hỡnh 2.13.

const int n; //n là số lượng tiến trỡnh

semaphore S = 1;

void P(int i){ //tiến trỡnh P(i)

for(;;){ //lặp vụ hạn Wait(S); <đoạn nguy hiểm> Signal(S); <Phần cũn lại của tiến trỡnh> } } void main(){ //tắt tiến trỡnh chớnh, chạy ủồng thời n tiến trỡnh StartProcess(P(1)); ... StartProcess(P(n)); } Hỡnh 2.13. Loại trừ tương hỗ sử dụng cờ hiệu 2.4.6. Một số bài toỏn ủồng bộ

để tiện minh họa cho việc sử dụng giải phỏp ủồng bộ, trong phần này sẽ trỡnh bày một số bài toỏn ủồng bộ kinh ủiển. đõy là những bài toỏn hoặc cú ứng dụng trờn thực tế hoặc khụng cú ứng dụng nhưng rất thuận tiện trong việc mụ tả vấn ủề xảy ra giữa cỏc quỏ trỡnh

ủồng thời và do vậy thường ủược sử dụng ủể minh họa hoặc kiểm tra giải phỏp ủồng bộ húa.

a. Bài toỏn triết gia ăn cơm

Tỡnh huống trong bài toỏn như sau. Cú năm triết gia ngồi trờn ghế quanh một bàn trũn, giữa bàn là thức ăn, xung quanh bàn cú năm chiếc ủũa sao cho bờn phải mỗi người cú một ủũa và bờn trỏi cú một ủũa (2.14).

Hỡnh 2.14. Bài toỏn cỏc triết gia ăn cơm

Cụng việc của mỗi triết gia là suy nghĩ. Khi người nào ủú cần ăn, người ủú dừng suy nghĩ, nhặt hai chiếc ủũa nằm gần hai phớa và ăn. Triết gia cú thể nhặt hai chiếc ủũa theo thứ tự

bất kỳ nhưng bắt buộc phải nhặt từng chiếc một với ủiều kiện ủũa khụng nằm trong tay người khỏc. Sau khi cầm ủược cả hai ủũa, triết gia bắt ủầu ăn và khụng ủặt ủũa xuống trong thời gian ăn. Sau khi ăn xong, triết gia ủặt hai ủũa xuống bàn và suy nghĩ tiếp.

Cú thể coi năm triết gia như năm tiến trỡnh ủồng thời với tài nguyờn nguy hiểm là ủũa và ủoạn nguy hiểm là ủoạn dựng ủũa ủểăn.

Cờ hiệu cho phộp giải quyết bài toỏn này như sau. Mỗi ủũa ủược biểu diễn bằng một cờ

hiệu. Thao tỏc nhặt ủũa sẽ gọi Wait ủối với cờ hiệu tương ứng và thao tỏc ủặt ủũa xuống bàn gọi Signal. Toàn bộ giải phỏp sử dụng cờ hiệu cho bài toỏn triết gia ăn cơm thể hiện trờn 2.15.

semaphore chopstick[5] = {1,1,1,1,1,1};

void Philosopher(int i){ //tiến trỡnh P(i)

for(;;){ //lặp vụ hạn Wait(chopstick[i]); //lấy ủũa bờn trỏi Wait(chopstick[(i+1)%5]); //lấy ủũa bờn phải <Ăn cơm> Signal(chopstick[(i+1)%5]); Signal(chopstick[i]); <Suy nghĩ> } } void main(){ // chạy ủồng thời 5 tiến trỡnh StartProcess(Philosopher(1)); ... StartProcess(Philosopher (5)); }

Hỡnh 2.15. Bài toỏn triết gia ăn cơm sử dụng cờ hiệu

Lưu ý rằng giải phỏp trờn 2.15 cho phộp thực hiện loại trừ tương hỗ, tức là trỏnh trường hợp hai triết gia cựng nhặt một ủũa. Tuy nhiờn, giải phỏp này cú thể gõy bế tắc nếu cả năm người cựng nhặt ủược ủũa bờn trỏi và khụng thể tiếp tục vỡ ủũa bờn phải ủó bị người bờn phải nhặt mất. Cỏch giải quyết tỡnh trạng này sẽủược ủề cập trong phần về bế tắc.

b. Bài toỏn người sản xuất, người tiờu dựng với bộủệm hạn chế

Bài toỏn ủược mụ tả như sau. Cú một người sản xuất ra sản phẩm gỡ ủú và xếp sản phẩm làm ra vào một chỗ chứa gọi là bộ ủệm, mỗi lần một sản phẩm. Một người tiờu dựng lấy sản phẩm từ bộ ủệm, mỗi lần một sản phẩm, ủể sử dụng. Dung lượng của bộ ủệm là hạn chế và chỉ chứa ủược tối ủa N sản phẩm. đõy là bài toỏn cú nhiều phiờn bản tương tự trờn thực tế, chẳng hạn thiết bị vào ra như bàn phớm cú thể nhận ký tự gừ từ bàn phớm, ủặt vào bộủệm, và tiến trỡnh lấy ký tự từ bộ ủệm ra ủể xử lý. Trong trường hợp tổng quỏt cú thể cú nhiều người sản xuất cựng làm ra và xếp sản phẩm vào bộủệm.

- Người sản xuất và người tiờu dựng khụng ủược sử dụng bộủệm cựng một lỳc. đõy là yờu cầu loại trừ tương hỗ.

- Khi bộủệm rỗng, người tiờu dựng khụng nờn cố lấy sản phẩm.

- Khi bộủệm ủầy, người sản xuất khụng ủược thờm sản phẩm vào bộủệm.

Ba yờu cầu trờn cú thể giải quyết bằng cờ hiệu. Yờu cầu thứ nhất ủược giải quyết bằng cỏch sử dụng một cờ hiệu lock khởi tạo bằng 1. Yờu cầu thứ hai và thứ ba ủược giải quyết lần lượt bằng hai cờ hiệu empty và full. Cờ hiệu empty ủược khởi tạo bằng 0 và full

ủược khởi tạo bằng kớch thước bộủệm N. Giải phỏp cho bài toỏn ủược thể hiện trờn 2.16:

const int N; //kớch thước bộ ủệm

semaphore lock = 1; semaphore empty = 0; semaphore full = N;

void producer () { //tiến trỡnh người sản xuất

for (;;) { <sản xuất > wait (full); wait (lock); <thờm một sản phẩm vào bộ ủệm> signal (lock); signal (empty); } }

void consumer () { //tiến trỡnh người tiờu dựng

for (;;) { wait (empty); wait (lock); <lấy một sản phẩm từ bộ ủệm> signal (lock); signal (full); <tiờu dựng> } } void main(){ StartProcess(producer); StartProcess(consumer); }

Hỡnh 2.16: Giải phỏp cho bài toỏn người sản xuất người tiờu dựng sử dụng cờ hiệu

2.4.7. Monitor

Trong một phần trước ta ủó xem xột về cơ chếủồng bộ sử dụng cờ hiệu. Mặc dự cú một số ưu ủiểm như khụng phải chờủợi tớch cực và dễ sử dụng hơn giải thuật Peterson song ủồng

bộ húa bằng cờ hiệu cú thể gõy ra lỗi nếu khụng ủược sử dụng ủỳng cỏch. Sử dụng cờ hiệu khụng ủỳng sẽ gõy ra cỏc lỗi sau:

- Quờn khụng gọi thao tỏc wait hoặc signal hoặc cả hai. - đảo ngược thứ tự wait và signal: gõy ra bế tắc.

- Thay vỡ dựng thao tỏc wait lại dựng signal hoặc ngược lại, tức là gọi wait hai lần hoặc signal hai lần: trong trường hợp thứ nhất khụng ủảm bảo loại trừ tương hỗ, trong trường hợp thứ hai gõy ủúi.

Một ủiều quan trọng là những lỗi xuất hiện như vậy khú debug do lỗi chỉ xẩy ra ở một số trường hợp khi tiến trỡnh thực hiện xen kẽ nhau theo một thứ tự nhất ủịnh.

để hạn chế phần nào cỏc vấn ủề vừa nờu khi sử dụng cờ hiệu, một giải phỏp khỏc của nhúm giải phỏp dựa trờn hỗ trợ của hệ ủiều hành và ngụn ngữ bậc cao là giải phỏp sử dụng

monitor (một số tài liệu tại Việt nam dịch là phũng ủợi do ủặc ủiểm của monitor).

Khỏi niệm monitor

Monitor ủược ủịnh nghĩa dưới dạng một kiểu dữ liệu trừu tượng của ngụn ngữ lập trỡnh bậc cao, chẳng hạn như một class của C++ hoặc Java. Mỗi monitor gồm một dữ liệu riờng, hàm khởi tạo, và một số hàm hoặc phương thức ủể truy cập dữ liệu với cỏc ủặc ủiểm sau:

1) Tiến trỡnh hoặc dũng chỉ cú thể truy cập dữ liệu của monitor thụng qua cỏc hàm hoặc phương thức của monitor, khụng thể truy cập dữ liệu trực tiếp. Tiến trỡnh thực hiện trong monitor bằng cỏch gọi hàm do monitor cung cấp.

2) Tại mỗi thời ủiểm, chỉ một tiến trỡnh ủược thực hiện trong monitor. Tiến trỡnh khỏc gọi hàm của monitor sẽ bị phong tỏa, xếp vào hàng ủợi của monitor ủể chờ cho ủến khi monitor ủược giải phúng.

đặc ủiểm thứ nhất rất quen thuộc ủối với ủối tượng trong ngụn ngữ lập trỡnh hướng ủối tượng và làm cho monitor cú nhiều ủiểm tương tự class.

đặc ủiểm thứ hai cho phộp ủảm bảo loại trừ tương hỗ ủối với ủoạn nguy hiểm. Người lập trỡnh chỉ cần ủặt tài nguyờn nguy hiểm vào trong monitor, vớ dụủặt biến dựng chung thành dữ liệu của monitor, và khụng cần lập trỡnh cơ chế loại trừ tương hỗ một cỏch tường minh bằng cỏch sử dụng cỏc bước trước và sau ủoạn nguy hiểm như trong những phương phỏp ở

trờn. đặc ủiểm này giỳp trỏnh lỗi xảy như khi dựng cờ hiệu.

Biến ủiều kiện

Monitor ủược ủịnh nghĩa như trờn cho phộp giải quyết vấn ủề loại trừ tương hỗ. Tuy nhiờn, cú thể xẩy ra vấn ủề khỏc về ủồng bộ, chẳng hạn khi một tiến trỡnh ủang thực hiện trong monitor và phải dừng lại (bị phong tỏa) ủể ủợi một sự kiện hay một ủiều kiện nào ủú

ủược thỏa món. Trong trường hợp như vậy, tiến trỡnh cần trả lại monitor ủể tiến trỡnh khỏc cú thể sử dụng. Tiến trỡnh chờ ủợi sẽ ủược khụi phục lại từ ủiểm dừng sau khi ủiều kiện ủang chờủợi ủược thỏa món.

để giải quyết tỡnh huống vừa nờu, ta cú thể sử dụng cỏc biến ủiều kiện. đõy là những biến ủược khai bỏo và sử dụng trong monitor với hai thao tỏc là cwait và csignal (tiền tố ỘcỢ

ủược thờm vào ủể phõn biệt với cờ hiệu) như sau:

- x.cwait (): tiến trỡnh ủang ở trong monitor và gọi cwait bị phong tỏa cho tới khi ủiều kiện x xẩy ra. Tiến trỡnh bị xếp vào hàng ủợi của biến ủiều kiện x. Monitor ủược giải phúng và một tiến trỡnh khỏc sẽủược vào monitor.

- x.csignal (): tiến trỡnh gọi csignal ủể thụng bỏo ủiều kiện x ủó thỏa món. Nếu cú tiến trỡnh ủang bị phong tỏa và nằm trong hàng ủợi của x do gọi x.cwait() trước ủú thỡ một tiến trỡnh như vậy sẽ ủược giải phúng. Nếu khụng cú tiến trỡnh bị phong tỏa thỡ thao tỏc csignal sẽ khụng cú tỏc dụng gỡ cả.

Cần lưu ý ủiểm khỏc nhau của csignal với signal của cờ hiệu: signal luụn giải phúng một tiến trỡnh cũn csignal thỡ cú thể khụng, nếu khụng cú tiến trỡnh chờủợi ủiều kiện.

Cấu trỳc monitor với cỏc biến ủiều kiện ủược thể hiện trờn 2.17.

Hỡnh 2.17. Monitor

để minh họa cho cỏch sử dụng monitor, chỳng ta sẽ xem xột một giải phỏp cho bài toỏn người sản xuất người tiờu dựng với bộ ủệm hạn chế, trong ủú cơ chếủồng bộủược thực hiện bằng monitor (hỡnh 2.18). Trong giải phỏp này, bộ ủệm buffer ủược khai bỏo như dữ liệu cục bộ của monitor cú thể truy cập bằng hai thao tỏc append (thờm vào) và take (lấy ra). Monitor sử dụng hai biến ủiều kiện notFull và notEmpty ủể trỏnh việc thờm sản phẩm vào bộủệm ủầy hoặc lấy sản phẩm khỏi bộ ủệm rỗng.

monitor BoundedBuffer {

product buffer[N]; //bộ ủệm chứa N sản phẩm kiểu product

int count; //số lượng sản phẩm hiện thời trong bộ ủệm

Dữ liệu cục bộ cỏc biến ủiều kiện

Một phần của tài liệu Bài giảng Hệ Điều Hành_HVCNBCVT_PGS.TS Từ Minh Phương (Trang 54)

Tải bản đầy đủ (PDF)

(140 trang)