Tải bản đầy đủ (.pdf) (19 trang)

Báo cáo hệ điều hành UIT LAB5

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (703.29 KB, 19 trang )

IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
Section 5.5
1. Hiện thực hóa mơ hình trong ví dụ 5.3.1.2, tuy nhiên thay bằng điều kiện sau:
sells <= products <= sells + [4 số cuối của MSSV]
Trả lời:
-

Mã số sinh viên: 22521676 => 4 số cuối của MSSV: 1676. Vậy thay điều kiện
trên bằng 1676
Source code:

#include <stdio.h>
#include
#include <semaphore.h>

int sells = 0, products = 0;
sem_t sema;
sem_t semb;

void *ProcessA(void *arg)
{
while (1)
{
sem_wait(&sema);
sells++;
printf("sells = %d\n", sells);
sem_post(&semb);
}
}

void *ProcessB(void *arg)


GVHD: Lê Hoài Nghĩa

1


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
{
while (1)
{
sem_wait(&semb);
products++;
printf("products = %d\n", products);
sem_post(&sema);
}
}

int main()
{
sem_init(&sema, 0, 0);
sem_init(&semb, 0, 1676);
pthread_t processA;
pthread_t processB;
pthread_create(&processA, NULL, &ProcessA, NULL);
pthread_create(&processB, NULL, &ProcessB, NULL);
while (1)
{
}
}
-


Kết quả:

GVHD: Lê Hoài Nghĩa

2


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình

Hình 5.5.1: Kết quả thực hiện code

2. Cho một mảng a được khai báo như một mảng số nguyên có thể chứa n phần tử, a
được khai báo như một biến tồn cục. Viết chương trình bao gồm 2 thread chạy
song song:
• Một thread làm nhiệm vụ sinh ra một số nguyên ngẫu nhiên sau đó bỏ vào a.
Sau đó đếm và xuất ra số phần tử của a có được ngay sau khi thêm vào.
• Thread còn lại lấy ra một phần tử trong a (phần tử bất kỳ, phụ thuộc vào
người lập trình). Sau đó đếm và xuất ra số phần tử của a có được ngay sau
khi lấy ra, nếu khơng có phần tử nào trong a thì xuất ra màn hình “Nothing
in array a”.
Chạy thử và tìm ra lỗi khi chạy chương trình trên khi chưa được đồng bộ. Thực
hiện đồng bộ hóa với semaphore.

GVHD: Lê Hoài Nghĩa

3


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
Trả lời:

Code khi chưa đồng bộ:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include
#include <semaphore.h>
#define MAX_SIZE 100000
static int n;
int a[MAX_SIZE];
int size = 0;
sem_t add_sem, del_sem;
void* add(void* arg) {
while(1) {
if(sizeint r = rand() % 100;
a[size++] = r;
printf("[ADD] Added %d. Size now: %d\n", r, size);
}
}
}
void* delete(void* arg) {
while(1) {
if(size<=n){
if(size == 0) {
printf("[DEL] Nothing in array a\n");
} else {
int r = a[--size];
printf("[DEL] Deleted %d. Size now: %d\n", r, size);
}

}
}
}
int main() {
pthread_t t1, t2;
printf("\nEnter n: ");
scanf("%d",&n);
pthread_create(&t1, NULL, add, NULL);
pthread_create(&t2, NULL, delete, NULL);
GVHD: Lê Hoài Nghĩa

4


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
while(1);
}
Kết quả khi chạy code:

Hình 5.5.2.1: Kết quả chạy code khi chưa đồng bộ semaphore.
-

Nhận Xét:
• Vấn đề lỗi khi chạy chương trình trên trước khi được đồng bộ hóa có thể xuất hiện
trong việc thêm và xóa các phần tử trong mảng a khi hai luồng chạy song song. Trong
trường hợp chưa được đồng bộ, có thể xảy ra một số tình huống xung đột về việc
thêm và xóa phần tử trong mảng.
• Ví dụ, khi mảng a đã đầy (size đạt MAX_SIZE), luồng thêm (add) sẽ khơng thể thêm
phần tử mới, và nó sẽ chờ semaphore del_sem để tiếp tục. Tuy nhiên, trong khi luồng
thêm đang chờ, luồng xóa (delete) có thể lấy ra một phần tử khỏi mảng, giảm size và

tăng semaphore add_sem. Khi luồng thêm được phép tiếp tục, nó khơng kiểm tra lại

GVHD: Lê Hoài Nghĩa

5


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình

-

xem mảng có đầy hay khơng và thêm một phần tử mới vào mảng, khiến mảng có thể
vượt q MAX_SIZE.
• Điều này dẫn đến việc khơng đảm bảo an toàn khi thao tác với mảng và size, và có
thể gây ra lỗi hoặc dữ liệu khơng đồng nhất.
Kết luận:
• Vì chưa có semaphore nên chương trình chưa được đồng bộ (Lỗi logic của semaphore
để xử lý lỗi logic). Bây giờ kích thước của mảng A khi chưa được vào chương trình.

Code Sau khi được đồng bộ:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include
#include <semaphore.h>
#define MAX_SIZE 100000
int n;
int i=0;
int a[MAX_SIZE];

int size = 0;
sem_t add_sem, del_sem;
void* add() {
while(1) {
if(size < n) {
int r = rand() % (n-1);
a[size++] = r;
printf("[ADD] Added %d. Size now: %d\n", r, size);
} else {
printf("Array full. Cannot add.\n");
}
int time_sleep = rand()%2 + 1;
sleep(time_sleep);
sem_post(&add_sem);
}
}
void* delete(void* arg) {
int j,b;
GVHD: Lê Hoài Nghĩa

6


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
while(1) {
sem_wait(&add_sem);
if(size <= n) {
if(size != 0){
int r = a[--size];
printf("[DEL] Deleted %d. Size now: %d\n", r, size);

}
else printf("[DEL] Nothing in array a\n");
}
}
int time_sleep = rand()%2 + 1;
sleep(time_sleep);
}
int main() {
pthread_t t1, t2;
sem_init(&add_sem, 1, 0); // Initialize add semaphore with MAX_SIZE permits
sem_init(&del_sem, 0, 0);
// Initialize delete semaphore with 0 permits
printf("\nEnter n: ");
scanf("%d",&n);
pthread_create(&t1, NULL, add, NULL);
pthread_create(&t2, NULL, delete, NULL);
while(1);
}

GVHD: Lê Hoài Nghĩa

7


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
Kết quả sau khi chạy code trên:

Hình 5.5.2.2: Kết quả chạy code sau khi đồng bộ semaphore.
-


Nhận xét:
• Semaphore được sử dụng đúng cách: Sử dụng hai semaphore add_sem và del_sem
để kiểm soát quyền truy cập vào việc thêm và xóa phần tử trong mảng a.
• Kiểm sốt thêm và xóa trong các luồng: Sử dụng sem_wait và sem_post để đảm bảo
rằng chỉ có một luồng được phép thực hiện thêm vào mảng tại một thời điểm, và chỉ
có một luồng được phép thực hiện xóa khỏi mảng tại một thời điểm.
• Sử dụng sleep để mơ phỏng thời gian chờ: Sử dụng hàm sleep để mô phỏng thời gian
chờ giữa các hành động thêm và xóa. Tuy nhiên, điều này có thể tạo ra một số vấn đề
khi chương trình chờ lâu hoặc có thể bị treo.
• Kiểm tra điều kiện trước khi xóa: Trước khi xóa phần tử khỏi mảng, luồng xóa kiểm
tra xem mảng có phần tử để xóa khơng để tránh lỗi khi mảng rỗng.

GVHD: Lê Hoài Nghĩa

8


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
-

Kết luận:
• Đồng bộ hóa bằng semaphore: Việc sử dụng semaphore là một cách hiệu quả để đảm
bảo chỉ có một luồng được phép thực hiện thao tác thêm hoặc xóa vào mảng tại một
thời điểm.
• Vẫn có thể cần cải thiện: Mặc dù đã có sự cải thiện đáng kể so với phiên bản không
đồng bộ, nhưng vẫn có thể cần cải thiện để xử lý các trường hợp đặc biệt như xử lý
thêm khi mảng đã đầy.
• Quản lý thời gian chờ: Việc sử dụng sleep để tạo ra khoảng thời gian chờ có thể cần
được điều chỉnh hoặc thay đổi để đáp ứng các u cầu cụ thể hơn.
• Tóm lại, mã code đã có sự cải thiện đáng kể trong việc đồng bộ hóa thêm và xóa phần

tử trong mảng sử dụng semaphore, nhưng vẫn có thể cần điều chỉnh để cải thiện hiệu
suất hoặc xử lý các trường hợp đặc biệt khác.

3. Cho 2 process A và B chạy song song như sau:
int x=0;
PROCESS A

PROCESS B

processA()

processB()

{

{
while(1){

while(1){

x = x + 1;

x = x + 1;

if (x == 20)

if (x == 20)

x = 0;


x = 0;

print(x);

print(x);

}
}

}
}

Hiện thực mơ hình trên C trong hệ điều hành Linux và nhận xét kết quả.
Trả lời:
-

-

Đây là bài toán liên quan đến vùng tranh chấp.
o Dữ liệu chia sẻ: biến toàn cục x.
o Vùng tranh chấp: các câu lệnh x = x + 1; if (x == 20) x = 0; print(x);
Do sử dụng vùng tranh chấp nhưng chưa được đồng bộ, có thể xảy ra các trường hợp
sau:
o Giá trị x lớn hơn 20. Khi x = 20 ở processA, processA bị dừng do CPU để
chuyển sang cho processB thực hiện, lúc này processB thực hiện x=x+1 => x
có giá trị = 21, dẫn đến việc có thể không =20 được => không thể gán x = 0.

GVHD: Lê Hoài Nghĩa

9



IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình

-

o Giá trị của x có thể tăng khơng đúng từ 0 -> 20. Do phép tốn + được thực
hiện bởi 3 thanh ghi load, add và store. Giả sử sau phép load, x = 1 được lưu
trong thanh ghi thì processA kết thúc phiên làm việc, processB tiến hành tăng
x lên 15 thì processB bị dừng, chuyển sang processA thực hiện thì lúc này x
lại bằng 2 => lỗi.
Code C:

#include <semaphore.h>
#include <stdio.h>
#include
int x = 0;
void *processA(void* mess)
{
while (1)
{
x = x + 1;
if (x == 20)
x = 0;
printf("ProcessA - x = %d\n", x);
}
}
void *processB(void* mess)
{
while (1)

{
x = x + 1;
if (x == 20)
x = 0;
printf("ProcessB - x = %d\n", x);
}
}
int main()
{
pthread_t pA, pB;
pthread_create(&pA, NULL, &processA, NULL);
pthread_create(&pB, NULL, &processB, NULL);
while (1) {}
return 0;
}
-

Chúng ta sẽ thực hiện lại bằng việc viết hai hàm tiến trình như đề bài và cho 2 tiến
trình chạy song song với nhau bằng cách tạo pthread.

GVHD: Lê Hoài Nghĩa

10


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
-

Kết quả chạy code


Hình 3.5.3.1: Kết quả chạy code

Hình 5.5.3.2: Kết quả chạy code
-

Nhận xét kết quả:
o Sau khi thực hiện chương trình câu 3, chúng ta có nhận xét rằng chương trình
này có lỗi vùng tranh chấp. Nghĩa là khi tiến trình A thực thi vùng tranh chấp
của mình thì tiến trình B cũng thực thi vùng tranh chấp của mình, dẫn đến
xung đột xảy ra lỗi như hình 3.1 và 3.2 ở trên, vì cả 2 hàm processA và

GVHD: Lê Hoài Nghĩa

11


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình
processB đều cùng thực hiện những câu lệnh như nhau, nên nếu đúng thì kết
quả xuất ra phải logic, liên tục lẫn nhau.
o Ở hình 3.1, sau khi x = 7 ở process A thì tiếp theo tại process B x phải bằng 8,
nhưng B lại bằng 1.
o Tương tự ở hình 3.2, sau khi process B x = 13 thì tiếp theo ở ProcessA x phải
bằng 10, nhưng x lại bằng 17.
 Đây chính là lỗi vùng tranh chấp mà hai tiến trình đã tạo nên.
-

Kết luận: khơng đồng bộ tiến trình ở vùng tranh chấp có thể khiến kết quả bài tốn bị
sai, bất hợp lý.

4. Đồng bộ với mutex để sửa lỗi bất hợp lý trong kết quả của mơ hình Bài 3.

Trả lời:
-

Ý tưởng:
o Để sửa lỗi bất hợp lý trong kết quả của mơ hình Bài 3, ta cần sử dụng mutex để
đồng bộ hóa truy cập vào biến x giữa hai process A và B.
o Khởi tạo Mutex: Trước khi bắt đầu sử dụng, ta cần khởi tạo một biến mutex.
o Sử dụng Mutex trong Process A và B:
▪ Trước khi thay đổi giá trị của x, ta sử dụng pthread_mutex_lock để đảm
bảo rằng chỉ có một process được phép thay đổi giá trị.
▪ Sau khi thay đổi x, ta sử dụng pthread_mutex_unlock để giải phóng
mutex.
o Hủy Mutex: Khi khơng cần sử dụng nữa, hủy mutex để giải phóng tài nguyên.
o Với cơ chế này, ta đảm bảo rằng chỉ có một process được phép thay đổi giá trị của
biến x tại một thời điểm, từ đó tránh được race condition và đảm bảo tính nhất
quán của dữ liệu.

-

Hiện thực code:

GVHD: Lê Hoài Nghĩa

12


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình

Hình 5.5.4.1. Chương trình C.
-


Kết quả:

GVHD: Lê Hoài Nghĩa

13


IT007.O16 – Lab05: Làm việc với tiểu trình và đồng bộ hóa tiểu trình

Hình 5.5.4.2. Kết quả chương trình.
-

Nhận Xét:
o Race Condition đã được giải quyết:
Các giá trị của x hiển thị đều là duy nhất và không bị trùng lặp giữa Process A và
Process B. Điều này cho thấy việc sử dụng mutex đã giải quyết vấn đề race
condition thành công.
o Chu kỳ lặp đúng đắn:
Các process đều thực hiện chu kỳ lặp của mình mà khơng gặp v

×