báo cáo Đanlhdh

32
Xây dựng chương trình producer-consumer với bounded buffer i TRƯỜNG ĐẠI HỌC BÁCH KHOA KHOA CÔNG NGHỆ THÔNG TIN BỘ MÔN MẠNG VÀ TRUYỀN THÔNG Tel. (84-511) 736 949, Fax. (84-511) 842 771 Website: itf.dut.edu.vn , E-mail: [email protected] BÁO CÁO ĐỒ ÁN MÔN HỌC NGUYÊN LÝ HỆ ĐIỀU HÀNH ĐỀ TÀI : XÂY DỰNG CHƯƠNG TRÌNH PRODUCER-CONSUMER VỚI BOUNDED BUFFER SINH VIÊN : Phạm Duy Dy LỚP : 11T4 CBHD : ThS. Nguyễn Văn Nguyên

Upload: pham-duy-dy

Post on 03-Dec-2015

222 views

Category:

Documents


2 download

DESCRIPTION

Báo cáo ĐANLHDH

TRANSCRIPT

Page 1: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer i

TRƯỜNG ĐẠI HỌC BÁCH KHOA

KHOA CÔNG NGHỆ THÔNG TINBỘ MÔN MẠNG VÀ TRUYỀN THÔNG

Tel. (84-511) 736 949, Fax. (84-511) 842 771Website: itf.dut.edu.vn, E-mail: [email protected]

BÁO CÁO ĐỒ ÁN MÔN HỌCNGUYÊN LÝ HỆ ĐIỀU HÀNH

ĐỀ TÀI :

XÂY DỰNG CHƯƠNG TRÌNH PRODUCER-CONSUMER VỚI BOUNDED BUFFER

SINH VIÊN : Phạm Duy DyLỚP : 11T4CBHD : ThS. Nguyễn Văn Nguyên

Đà Nẵng, 08/2015

Page 2: Báo cáo ĐANLHDH

MỤC LỤC

MỞ ĐẦU

CHƯƠNG 1. CƠ SỞ LÝ THUYẾT.............................................................51.1 Tổng quan về tiến trình............................................................................................5

1.1.1 Khái niệm......................................................................................................51.1.2 Môi trường thực hiện....................................................................................51.1.3 Đặc điểm của tiến trình.................................................................................61.1.4 Ngữ cảnh của Tiến trình...............................................................................71.1.5 Trạng thái của tiến trình................................................................................7

1.2 Kiểm soát tiến trình.................................................................................................81.2.1 Tạo Tiến trình...............................................................................................81.2.2 Dừng một tiến trình......................................................................................91.2.3 Giao tiếp giữa các tiến trình........................................................................10

1.3 Cơ chế semophore.................................................................................................121.3.1 Giới thiệu....................................................................................................121.3.2 Phân loại.....................................................................................................131.3.3 Khởi tạo Semaphore...................................................................................131.3.4 Điều khiển Semaphore................................................................................141.3.5 Thao tác trên Semaphore............................................................................151.3.6 Các hàm trong Semaphore..........................................................................16

CHƯƠNG 2. BÀI TOÁN............................................................................172.1 Yêu cầu..................................................................................................................17

2.2 Các giải pháp.........................................................................................................172.2.1 Môi trường phát triển..................................................................................17 Hệ điều hành: Ubuntu v14.04............................................................................17 Ngôn ngữ lập trình C trong linux.......................................................................17 Trình biên dịch GCC..........................................................................................172.2.2 Chương trình nguồn....................................................................................17

2.3 Demo......................................................................................................................22

KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN

TÀI LIỆU THAM KHẢO

Page 3: Báo cáo ĐANLHDH

MỞ ĐẦU1. GIỚI THIỆU

Chắc chắn chúng ta đều nhận thấy được rằng, chính công nghiệp điện toán đã

làm thay đổi cả thế giới về mọi mặt trong tất cả các lĩnh vực như đời sống, văn

hóa, chính trị, xã hội… Và bây giờ chúng ta chắc sẽ không hình dung nổi cuộc

sống của chúng ta sẽ như thế nào nếu không có sự xuất hiện của công nghiệp điện

toán. Những chiếc máy tính ngày càng nhỏ đi về kích thước, xử lý số liệu ngày

càng mạnh lên và đặc biệt là giá cả ngày càng hạ. Vậy làm thế nào để những chiếc

máy tính bằng phần cứng đó hoạt động phục vụ cho mọi nhu cầu công việc, giải trí

của chúng ta, chính là nhờ vào hệ điều hành. Hệ điều hành UNIX ra đời vào những

năm đầu thập niên 60 của thế kỉ XX vẫn được dùng nhiều trên thị trường, đặc biệt

là trong lĩnh vực giáo dục. Ngày nay, với những sự tiện dụng và đặc biệt là mã

nguồn mở của nó mà giúp người dùng dể dàng sử dụng và tinh chỉnh hệ thống theo

ý thích của mình. Một số hệ điều hành thuộc tương tự UNIX như LINUX ngày

càng được sử dụng rộng rãi. Vì thế mà việc tìm hiểu về cơ chế hoạt động, cách làm

việc của hệ điều hành này là không thể thiếu đối với những ai học trong ngành

công nghệ thông tin. Vì thế mà chúng em được nhận đề tài liên quan đến hệ điều

hành LINUX, đó là chủ đề tìm hiểu cơ chế đồng bộ và giao tiếp giữa các tiến trình

thông qua Semaphore để giải quyết bài toán sản xuất – tiêu thụ(Producer –

consume) với bounded buffer.

Nội dung đề tài: Xây dựng chương trình Producer – Consumer với bounded

buffer

- Giới thiệu tiến trình trong Unix, cách tạo tiến trình.

- Giới thiệu sơ lược về semaphore, giao tiếp đồng bộvới semaphore.

- Các hàm xử lý semaphore.

- Tạo tiến trình Producer-Consumer.

- Sử dụng semaphore để truy xuất tài nguyên.

- Xây dựng chương trình và kết quả demo.

Page 4: Báo cáo ĐANLHDH

Em xin chân thành cảm ơn sự hướng dẫn tận tình của thầy NGUYỄN VĂN

NGUYÊN, cảm ơn sự trao đổi, góp ý của các bạn trong lớp đã giúp em hoàn thành

đề tài này.

Với sự hạn chế về mặt kiến thức nên báo cáo của em vẫn còn nhiều thiếu sót và

hạn chế, vậy em rất mong nhận được sự góp ý thêm của quý thầy cô và các bạn.

Đà Nẵng, ngày 10 tháng 8 năm 2015

Sinh viên thực hiện

Phạm Duy Dy

2. BỐ CỤC ĐỒ ÁN

Chương 1: Các cơ sở lý thuyết liên quan.

Chương 2: Phân tích các chức năng của đề tài. Thiết kế xây dựng nên các

chức năng và chương trình.

Chương 3: Thực thi chương trình, đánh giá kết quả nhận được.

Page 5: Báo cáo ĐANLHDH

Chương 1. CƠ SỞ LÝ THUYẾT

1.1 Tổng quan về tiến trình

1.1.1 Khái niệm

Unix là hệ đa xử lí, tức khả năng thực thi nhiều tác vụ cùng một lúc. Một chương trình máy tính là một chuỗi các chỉ lệnh (intructions, hay còn gọi là lệnh máy) mà theo đó máy tính phải thực hiện. Mặt khác tài nguyên máy tính (CPU, bộ nhớ, tệp, các thiết bị...) là hữu hạn và khi các chương trình chạy thì các chương trình đều có nhu cầu trên các tài nguyên đó. Để đáp ứng nhu cầu tài nguyên, cần có một sách lược chạy trình thật hiệu quả để đảm bảo tính đa nhiệm, nhiều người dùng. Cách phổ biến nhất là cấp tài nguyên cho mỗi chương trình trong một lượng thời gian nhất định, sao cho các chương trình đều có cơ hội thực hiện như nhau và trong thời gian thực hiện chương trình, cần kiểm soát việc thực hiện đó chặt chẻ. Để làm điều đó, ta đưa ra một khái niệm gọi là tiến trình (process).

Vậy tiến trình là một thực thể điều khiển đoạn mã lệnh có riêng một không gian địa chỉ, có ngăn xếp riêng, có bảng chứa các mô tả tập tin đang mở và đặc biệt là có một định danh PID (Process Identifier) duy nhất trong toàn bộ hệ thống vào thời điểm tiến trình đang chạy..

1.1.2 Môi trường thực hiện

Việc thực hiện một TT trên Unix được chia ra làm hai mức: user (người dùng) và kernel (nhân của hệ thống). Khi một TT của user thực hiện một chức năng của nhân (thông qua gọi hệ thống GHT), chế độ thực hiện của TT sẽ chuyển từ chế độ người dùng (user mode) sang chế độ nhân của hệ thống (kernel mode): Hệ thống sẽ thực hiện và phục vụ các yêu cầu của user, trả lại kết quả. Ngay cả khi user tạo ra các yêu cầu không tường minh, thì hệ hống vẫn thực hiện các kết toán có liên quan tới TT của user, thao tác các ngắt, lập biểu các TT, quản lí bộ nhớ...

Kernel mode là một chế độ đặc quyền, trong đó không có giới hạn nào đối với kernel: kernel sử dụng tất cả các lệnh của CPU, các thanh ghi của CPU, kiểm soát bộ nhớ, liên lạc trực tiếp với các thiết bị ngoại vi. Kernel tiếp nhận và xử lí các yêu cầu của các TT của user, sau đó gởi kết quả đến các TT đó.

User mode được hiểu là chế độ thực hiện bình thường của một tiến trình. Trong chế độ này, có nhiều hạn chế áp đặt lên TT: TT chỉ truy nhập được các lệnh và dữ liệu của nó, không thể truy nhập lệnh, dữ liệu của kernel và của các TT khác, một số các thanh ghi của CPU là cấm. Ví dụ: không gian địa chỉ ảo của một TT được chia ra thành miền chỉ truy nhập được trong chế độ kernel, miền khác ở chế độ user, hay TT không thểtương tác với máy vật lí, một sốlệnh của CPU không được

Page 6: Báo cáo ĐANLHDH

sử dụng, có thể bị ngắt trong bất kì lúc nào. Một TT trong user mode khi muốn truy nhập tài nguyên, phải thực hiện qua gọi hệ thống (GHT).

Gọi hệ thống (GHT hay gọi thực hiện chức năng hệ thống cung cấp) là quá trình chuyển thông số (yêu cầu qua tên hay số của các dịch vụ hệ thống) mà TT yêu cầu cho kernel thực hiện. Trong Unix, việc đó được làm qua một bẫy hệ thống (trap), sau đó kernel sẽ thực hiện nhu cầu của TT, đôi khi còn nói là: kernel thực hiện TT trên danh nghĩa của TT, trong môi trường của TT. Kernel không phải là tập tách biệt của TT chạy song song với TT người dùng, mà là một phần của mỗi TT người dùng. Văn cảnh trình bày nói “kernel cung cấp tài nguyên” hay “kernel thực hiện ... “ có nghĩa là TT đang chạy trong kernel mode cấp tài nguyên hay TT thực hiện ... Bản chất của GHT để thực hiện các dịch vụ của kernel và mã thực thi các dịch vụ đó đã là một phần trong mã của TT người dùng, chỉ khác là mã đó chỉ chạy trong kernel mode mà thôi.

Ví dụ: shell đọc đầu vào từ thiết bị đầu cuối bằng một GHT, lúc này kernel thực hiện nhân danh TT shell, kiểm soát thao tác của thiết bị đầu cuối và trả lại cho shell kí tự nhận được. Shell sau đó chạy trong user mode, thông dịch xâu kí tự và thực hiện các hành vi nhất định và có thể phát sinh GHT tiếp theo.

1.1.3 Đặc điểm của tiến trình

Như đã nói có rất nhiều TT được thực hiện đồng thời trong hệ thống, nhưng kernel cần lập biểu để đưa vào thực hiện. Mỗi TT chỉ có một TT cha, nhưng có nhiều TT con của nó.

Kernel nhận biết mỗi TT qua số hiệu của TT gọi là số định danh của TT (Procces ID: PID). PID thường là số nguyên dương có giá trị từ 2 đến 32768 ( số1 được dành cho tiến trình init ). Khi một tiến trình mới yêu cầu khởi động, HĐH sẽ chọn lấy một số (chưa bị tiến trình đang chạy nào chiếm giữ) trong khoảng số nguyên trên và cấp phát cho tiến trình mới. Khi tiến trình chấm dứt hệ thống sẽ thu hồi lại số PID đó để cấp phát cho tiến trình khác trong lần sau.

Cũng như User, nó cũng có thể nằm trong nhóm. Vì thế để phân biệt, chúng ta nhận biết thông qua số hiệu nhóm của tiến trình gọi là pgrp. Một số hàm trong C cho phép chúng ta có thể tương tác với các tiến trình:

int getpid( ) : Trả về giá trịint là số hiệu pid của tiến trình hiện tại.

int getppid( ): Trả về giá trị int là pid của tiến trình cha của tiến trình hiện tại.

int getpgrp( ): Trả về giá trị int là số hiệu của nhóm tiến trình .

Page 7: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 7

int setpgrp( ): Trả về giá trị int là số hiệu của tiến trình mới được tạo ra.

1.1.4 Ngữ cảnh của Tiến trình

Bối cảnh (context) của TT là tập hợp các thông tin mô tả trạng thái của TT đó. Bối cảnh được định nghĩa bởi mã lệnh, các giá trị của các biến và các cấu trúc tổng thể của user, giá trị của các thanh ghi của CPU mà TT đang dùng, các giá trị trong proccess table và nội dung các stack (ngăn xếp) trong user và kernel mode. Phần mã, cũng như các cấu trúc dữ liệu tổng thể của kernel tuy có chia sẻ cho TT nhưng không thuộc bối cảnh của TT.

Khi thực hiện một TT, ta nói hệ thống được thực hiện trong bối cảnh của TT đó. Khi kernel quyết định cho chạy một TT khác, kernel sẽ chuyển đổi bối cảnh (switch context) sao cho hệ thống sẽ thực hiện trong bối cảnh của TT mới. Kernel cho phép chuyển bối cảnh chỉ dưới những điều kiện nhất định. Khi chuyển bối cảnh, kernel bảo vệ các thông tin cần thiết để khi trở lại TT trước đó, kernel có thể tiếp tục thực hiện TT đó. Tương tự như vậy cũng xảy ra cho quá trình chuyển từ user mode sang kernel mode và ngược lại. Tuy nhiên chuyển từ user mode sang kernel mode của một TT là sự thay đổi chế độ thực hiện, chứ không phải chuyển đổi bối cảnh. Bối cảnh thực hiện của TT không đổi, vì không có sự chuyển đổi thực hiện TT khác.

1.1.5 Trạng thái của tiến trình

Cuộc đời của TT có thể phân chia ra các trạng thái, mỗi trạng thái có các đặc tính mô tả về TT. Tiến trình có một số các trạng thái sau đây:

1. TT đang chạy trong user mode.

2. TT đang chạy trong kernel mode.

3. TT không được thực hiện (không chạy) nhưng sẵn sàng chạy khi bộ lập biểu chọn để thực hiện. Có rất nhiều TT trong trạng thái này, nhưng scheduler chỉ chọn một TT.

4. TT ngủ (sleeping): TT không thể thực hiện tiếp vì những lí do khác nhau, ví dụ: đang đợi...

Phạm Duy Dy, 11T4 7

Page 8: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 8

1.2 Kiểm soát tiến trình

1.2.1 Tạo Tiến trình

Tạo ra các TT là một điểm mạnh trong Unix. Vậy tạo ra các TT để làm gì? Người lập trình luôn quan tâm tới khả năng thực hiện nhiều tác vụ đồng thời trong khi phát triển một ứng dụng, trong khi đó cũng muốn sử dụng lại các tiện ích, các hàm đã có để nâng cao năng lực, hiệu quả tính toán của máy tính. Tạo TT là giải pháp đáp ứng yêu cầu trên, bởi vì một TT được tạo ra sẽ chạy song song với TT đã tạo ra nó. Đó cũng chính là sức mạnh đa nhiệm mà Unix có. Ta hãy theo dõi quá trình sau:

Khi thực hiện một lệnh máy, còn gọi là command, một qui trình được thực hiện bao gồm: Giao diện người – máy, shell, nhận lệnh (command) user đưa vào, sau đó shell tạo ra một TT (con) để thực hiện command đó. Quá trình này xảy ra qua hai bước:

1. Shell tạo ra một TT con để chạy command, tức tạo ra một bối cảnh sao cho lệnh có thể thực hiện được. Quá trình này thực sự là việc sao chép bối cảnh của TT bố(shell) cho TT mới (TT con). Như vậy khi thực hiện command thì command này sử dụng các môi trường của TT bố đã sao chép cho nó. Tuy nhiên không phải tất cả môi trường của TT bố được sao chép cho TT con, mà chỉ có các phần tổng thể được sao chép, và phần này bao gồm:

- Môi trường xuất (exported environment): UID, GID của TT bố, các đầu vào/ra chuẩn (stdin, stdout), các tệp đã mở, thư mục root, thư mục hiện hành, và các thông tin hệ thống khác, danh sách các biến tổng thể (global variables) và một vài biến trong môi trường riêng của TT bố cũng có thể xuất cho TT con.

- Mã chương trình.

Phạm Duy Dy, 11T4 8

Page 9: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 9

2. Thực hiện command trong bối cảnh của TT mới được tạo (sẽ do scheduler sắp xếp). TT con sẽ trả lại các kết quả khi kết thúc thực hiện command qua exit(). Sự tạo lập một tiến trình mới thực hiện bằng lệnh gọi hệ thống fork. Fork() cho phép một tiến trình lập một bản sao của nó, trừ bộ định dạng tiến trình. Tiến trình gốc tự nhân bản chính nó được gọi là tiến trình bốvà bản sao tạo ra được gọi là tiến trình con.

Cú pháp như sau:

#include<sys/types.h>

#include<unistd.h>

pid = fork();

Khi thực hiện xong, hai TT nói trên có hai bản sao user level context như nhau (exported environment ở mô hình trên), và với các giá trị trả lại pid khác nhau:

Trong bản thân mã của TT bố, pid là số định danh của TT con;

Trong bản thân mã của TT con, pid = 0 thông báo kết quảtạo TT là tốt;

1.2.2 Dừng một tiến trình

Lệnh kill của Shell có thể dùng để chấm dứt hoạt động của một tiến trình. ví dụ như khi muốn dừng tiến trình 234 ta dùng lệnh: kill 234

C cũng có lệnh kill như sau:

int kill(pid, sig);

int pid; là dấu hiệu nhận biết của một tiến trình.

int sig; hằng tín hiệu giao tiếp tiến trình.

Phạm Duy Dy, 11T4 9

Page 10: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 10

1.2.3 Giao tiếp giữa các tiến trình

Trong các hệ thống đa chương, một tiến trình không đơn độc trong hệ thống mà có thể ảnh hưởng đến các tiến trình khác, hoặc bị các tiến trình khác tác động. Nói các khác, các tiến trình là thực thể độc lập nhưng chúng cũng có nhu cầu liên lạc với nhau để:

Chia sẻ thông tin: Nhiều tiến trình có thể cùng quan tâm đến những dữ liệu nào đó, do vậy hệ điều hành cần cung cấp một môi trường cho phép sự truy cập đồng thời đến các dữ liệu chung.

Hợp tác hoàn thành tác vụ: Đôi khi để đạt được một xử lí nhanh chóng, người dùng phân chia một tác vụ thành các công việc nhỏ để có thể tiến hành song song. Thường thì các công việc nhỏ này cần hợp tác với nhau để hoàn thành tác vụ ban đầu . Ví dụ như dữ liệu kiết xuất của tiến trình này là dữ liệu nhập vào của tiến trình kia. Trong trường hợp đó, hệ điều hành cần cung cấp các cơ chế để các tiến trình có thể trao đổi thông tin với nhau.

HĐH Linux và UNIX cung cấp cho bạn một số cơ chế giao tiếp giữa các tiến trình gọi là IPC (Inter Process Communication) bao gồm:

Trao đổi bằng tín hiệu (signals handling)

Trao đổi qua cơchế đường ống (pipe)

Trao đổi thông qua hàng đợi thông điệp (message queue)

Trao đổi bằng phân đoạn nhớchung (shared memory segment)

Giao tiếp đồng bộvới semaphore

Giao tiếp trao đổi thông qua socket

Việc giao tiếp giữa các tiến trình được thực hiện thông qua các tín hiệu chuẫn của hệ thống. Tín hiệu là một sựngắt quãng logic được gửi đến các tiến trình bởi hệ thống để thông báo cho chúng về những sự việc không bình thường trong môi trường hoạt động của chúng (như lỗi bộ nhớ, lỗi vào ra). Nó cũng cho phép các tiến trình liên lạc với nhau. Một tín hiệu (trừ SIGKILL) có thể được xem xét theo ba cách khác nhau:

1. Tiến trình có thể được bỏ qua: Ví dụ chương trình có thể bỏ qua sự ngắt quãng của người sử dụng hệ thống (đó là sự bỏ qua khi một tiến trình đang được sử dụng ở phần nền.

2. Tiến trình có thể được thực hiện: Trong trường hợp này, khi nhận được 1 tín hiệu, việc thực hiện 1 tiến trình được chuyển về một quy trình do người sử dụng xác định trước, sau đó trở lại nơi nó bị ngắt.

3. Lỗi có thể được tiến trình trả về sau khi nhận được tín hiệu này.

Dưới đây là một số tín hiệu thường gặp:

Phạm Duy Dy, 11T4 10

Page 11: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 11

Phạm Duy Dy, 11T4 11

Page 12: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 12

1.3 Cơ chế semophore

1.3.1 Giới thiệu

Semaphore là một biến được bảo vệ( hay là một kiểu dữ liệu trừu tượng),

tạo thành một phương pháp để hạn chếtruy nhập tới tài nguyên dùng chung

trong môi trường đa lập trình( multiprogramming). Đây là một phát minh của

Edsger Dijkstra và được sử dụng lần đầu tiên trong hệ điều hành THE. Giá trị

của semaphore được khởi tạo bằng số các tài nguyên tương đương được chia sẻ

cài đặt điều khiển. Trong trường hợp đặc biệt, khi mà chỉ có một tài nguyên

tương đương được chia sẻ, semaphore được gọi là semaphore nhị phân. Trong

trường hợp khái quát, semaphore thường được gọi là biến đếm semaphore.

Semaphore chỉ có thể được truy nhập bằng cách sử dụng các toán tử sau:

P(Semaphore S)

{

if S > 0 then S := S – 1 else wait on S) ;

}

V(Semaphore S)

{

if (Có quá trình đang đợi trên S)

then(kích khởi 1 trong các quá trình đó )

S := S + 1;

}

Init(Semaphore S, Integer V)

{ S := V; }

Phạm Duy Dy, 11T4 12

Page 13: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 13

Giá trịcủa một semaphore là số đơn vị tài nguyên đang ở trạng thái rỗi.

(Nếu chỉ có một tài nguyên, người ta sẽ sử dụng một "semaphore nhị phân" với

các giá trị0 và 1.) Toán tử P đợi (busy waiting) hoặc đang ngủ cho đến khi một

tài nguyên hết bận, khi đó, nó lập tức chiếm quyền sử dụng tài nguyên đó. Vlà

toán tử đảo ngược; nó thả một tài nguyên sau khi tiến trình đã sử dụng xong tài

nguyên đó. Init chỉ được dùng để khởi tạo semaphore trước tất cả các yêu cầu sử

dụng nào. Các toán tử P và V phải có tính chất nguyên tố, nghĩa là không có tiến

trình nào có thể chặn giữa quá trình thực hiện một trong các toán tửnày để chạy

một toán tử khác trên cùng một semaphore đó.

Để tránh tình trạng busy-waiting, một semaphore có thể có một cấu trúc

hàng đợi gồm các tiến trình. Nếu một tiến trình thực hiện một thao tác P đối với

một semaphore có giá trị0, tiến trình này được đưa vào hàng đợi của semaphore.

Khi một tiến trình khác dùng toán tử V để tăng giá trị của semaphore, và có tiến

trình nằm trong hàng đợi, thì một tiến trình trong đó được lấy ra khỏi hàng đợi

và tiếp tục chạy.

1.3.2 Phân loại

Có hai loại Semaphore:

Semaphore nhị phân chỉ có 2 giá trị là 0 và 1. Nếu Semaphore có hai giá trị hay là semaphore nhị phân thì nó có vai trò như Mutex trong tương tác các luồng. Semaphore nhị phân thường được khởi tạo tại giá trị 1. Khi tài nguyên đang được sử dụng, luồng truy nhập gọi P(S) để giảm giá trị của nó về 0, và khôi phục giá trị 1 bằng toán tử V khi tài nguyên sẵn sàng được được thả ra.

Semaphore khác có các giá trị nhiều hơn 2.

1.3.3 Khởi tạo Semaphore

Để khởi tạo cho một Semaphore ban đầu chúng ta sử dụng hàm semget().

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

Phạm Duy Dy, 11T4 13

Page 14: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 14

int semget(key_t key, int nsems, int semflg);

key: giá trịkey cho IPC object, nếu key=IPC_PRIVATE thì semaphore

tạo ra chỉ được sử dụng trong nội bộ process.

nsems: sốlượng semaphore trong semaphore set, thông thường chỉcần

dùng 1 semaphore.

semflag: IPC_CREAT, IPC_EXCL và có thể OR với giá trị ấn định quyền truy cập (tương tự quyền hạn trên một file).

Ví dụ:

sset1_id=semget(IPC_PRIVATE,1,IPC_CREAT|IPC_EXCL|0600);

sset2_id=semget(12345,1,IPC_CREAT|IPC_EXCL|0666);

Đểtạo ra một tập semaphore duy nhất với mỗi lần gọi thường sử dụng 2 cách:

- Truyền vào thông số IPC_PRIVATE, hệ thống tự giải quyết.

- Sử dụng hàm ftok()

key_t ftok(const char *path, int id)

Hàm này trả về key duy nhất với 2 thông số truyền vào khác nhau. “path” là đường dẫn đến một file bất kỳ có quyền read( sử dụng file nào đó chỉ có user owner có quyền read), “id” là một ký tự nhận dạng nào đó. Trường hợp truyền vào cùng một key thì nó sẽ trả về cùng một semaphore ( có id giống nhau).

1.3.4 Điều khiển Semaphore

Để điều khiển các Semaphore chúng ta sử dụng hàm semctl().

Semctl ( ): Là hàm lấy hoặc thay đổi thuộc tính của các đối tượng semaphore. Hàm có cấu trúc như sau:

Int Semctl(int Semid, int Semnum, int command, union semun);

Trong đó :

- Semid: Nó gọi hợp lệ một sốhiệu của Semaphore. Tức là giá trị được trả lại thông qua hàm semget().

- Semnum: Giá trị của Semnum lựa chọn một giá trị bên trong một mảng bởi các chỉ số của nó (bắt đầu từ 0).

- Command: Lời gọi Commandlà một trong những cờ điều khiển sau :

Phạm Duy Dy, 11T4 14

Page 15: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 15

Các thao tác thông thường

IPC_SET: thiết lập quyền truy cập

IPC_STAT: lấy thông tin thuộc tính

IPC_RMID: xoá semaphore set

Các thao tác trên từng semaphore riêng lẻ

GETVAL: lấy thông tin thuộc tính

SETVAL: thay đổi thuộc tính

GETPID: lấy PID của process vừa truy cập semaphore

GETNCNT: lấy số process đang đợi semval tăng lên

GETZCNT: lấy số process đang đợi semval về0

Các thao tác trên toàn semaphore set

SETALL: thay đổi thuộc tính

GETALL: lấy thông tin thuộc tính

- union Semun là tùy chọn, nó phụ thuộc vào các thao tác yêu cầu.

union semun

{

int val; /* Value for SETVAL */

struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */

ushort *array; /* Array for GETALL,SETALL */

} ;

1.3.5 Thao tác trên Semaphore

Hàm Semop( ) thực hiện những thao tác trên tập hợp Semaphore nhằm thay đổi trạng thái hay nội dung của nó. Nó là hàm có cấu trúc như sau:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);

semid: semaphore set ID do hàm semget() trảvề

nsops: số semaphore trong tập các semaphore (độdài mảng sema)

Phạm Duy Dy, 11T4 15

Page 16: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 16

sops: là cấu trúc sembuf định ra các thao tác cho từng semaphore trong tập semaphore.

Một cấu trúc sembuf cụ thể là một thao tác của Semaphore nó được định nghĩa bên trong:

<sys/sem.h>.

struct sembuf

{

short sem_num; /*chỉ sốSemaphore cần edit*/

short sem_op; /* thao tác được thực hiện */

short sem_flg; /* Cờ điều khiển, nếu có*/

};

sem_num: chỉ số của semaphore trong semaphore set, chỉ số này bắt đầu từ 0

sem_op: là sốnguyên

>0: tăng giá trịsemaphore

<0: giảm giá trịsemaphore

sem_flg:

IPC_NOWAIT: non-blocking mode

SEM_UNDO: undo operation

1.3.6 Các hàm trong Semaphore

Sem_open(): Được sữ dụng để tạo, để mở hay hiệu chỉnh một Semaphore đã tạo sẵn trước đó.

Sem_init(sem_t *sem, int pshared, unsigned int value): Khởi tạo một cấu trúc Semaphore.

Sem_Close() : Dùng để đóng một semaphore được mở hay là tạo ra.

Sem_unlink() : Dùng để kết thúc liên lạc của Semaphore. Và xóa bỏ Semaphore khi tiến trình kết thúc thực thi.

Sem_destroy(sem_t * sem) : Dùng để hủy 1 Semaphore.

Sem_getvalue(): Dùng để copy giá trịcủa Semaphore thành 1 số nguyên cụ thể.

Phạm Duy Dy, 11T4 16

Page 17: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 17

Sem_wait(sem_t * sem): Thực hiện giảm value của semaphore xuống

Sem_port() : Thực hiện việc tăng dần các giá trị của Semaphore.

Phạm Duy Dy, 11T4 17

Page 18: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 18

Chương 2. Bài Toán

2.1 Yêu cầu

Xây dựng chương trình producer-consumer với bounded buffer sử dụng

semaphore. Bài toán là mô hình cho các process hợp tác, quá trình sản xuất

(producer process) tạo ra các thông tin để các quá trình tiêu thụ(consumer producer)

sử dụng. Sự trao đổi thông tin thực hiện qua buffer. Producer và consumer phải

được đồng bộ hoạt động.

2.2 Các giải pháp

Ta sử dụng 1 tiến trình chính với thread chính làm Producer. Tạo 1 thread làm Consumer. Product_Value ban đầu là 2, tối đa MAX_VALUE là 3. Semaphore có value ban đầu là 2, tức là lúc này yêu cầu sử dụng tài nguyên không vượt quá 2. Mỗi khi Producer sản xuất 1 product thì Consumer lại tiêu thụ 2 product, kết quả product giảm dần đến 0, lúc này Consumer không thể tiêu thụ được nữa do product đã hết mà phải chờ Producer sản xuất ra.

2.2.1 Môi trường phát triển

Hệ điều hành: Ubuntu v14.04.

Ngôn ngữ lập trình C trong linux.

Trình biên dịch GCC.

2.2.2 Chương trình nguồn

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

#include <errno.h>

#include <unistd.h>

#include <stdio.h>

#include <signal.h>

#include <stdlib.h>

Phạm Duy Dy, 11T4 18

Page 19: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 19

#define MAX_PRODUCT 3

int product_val = 2;

int sem;

typedef union

{

int val;

struct semid_ds *buf;

ushort *array;

}semun ;

int seminit()

{

int i, semid;

semun carg;

if (semid=semget(IPC_PRIVATE,1,0666|IPC_EXCL)==-1)return(-1);

carg.val=2;

if (semctl(semid,0,SETVAL,carg)==-1) return(-1);

return semid;

}

void P(int sem)

{

struct sembuf pbuf;

pbuf.sem_num=0;

pbuf.sem_op=-1;

pbuf.sem_flg=SEM_UNDO;

if (semop(sem,&pbuf,1)==-1)

{

perror("semop");

exit(1);

}

}

void V(int sem)

Phạm Duy Dy, 11T4 19

Page 20: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 20

{

struct sembuf vbuf;

vbuf.sem_num=0;

vbuf.sem_op=1;

vbuf.sem_flg=SEM_UNDO;

if (semop(sem,&vbuf,1)==-1)

{

perror("semop");

exit(1);

}

}

int semrel(int semid)

{

return semctl(semid,0,IPC_RMID,0);

}

void Consumer(int sem)

{

while(1)

{

P(sem);

product_val--;

printf("Consumer product_value = %d\n", product_val);

sleep(1);

}

}

void Producer(int sem)

{

while(1)

{

if( product_val < MAX_PRODUCT)

{

Phạm Duy Dy, 11T4 20

Page 21: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 21

V(sem);

product_val++;

printf("Producer product_val = %d\n\n", product_val);

sleep(2);

}

else

{

while(product_val >= MAX_PRODUCT)

sleep(1);

}

}

}

int *do_thread(void *data)

{

while(1)

{

Consumer(sem);

}

pthread_exit(NULL);

}

int main()

{

int res, i;

pthread_t a_thread;

void* thread_result;

sem=seminit();

res=pthread_create(&a_thread, NULL, (void*)do_thread, NULL);

if(res!=0)

{

perror("Thread create error");

exit(EXIT_FAILURE);

Phạm Duy Dy, 11T4 21

Page 22: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 22

}

Producer(sem);

printf("All done\n");

exit(EXIT_SUCCESS);

}

Phạm Duy Dy, 11T4 22

Page 23: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 23

2.3 Demo

Phạm Duy Dy, 11T4 23

Page 24: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 24

KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN

Trong chương trình này đã cung cấp cho ta hiểu thêm về cơ chế đồng bộcác tiến trình trong HĐH Unix(Linux) . Điều đó giải quyết được các tranh chấp các tiến trình khi truy cập các tài nguyên của hệ thống. Vấn đề mà tưởng chừng như rất khó khăn trong việc nối kết các chương trình hay các hàm trong chương trình chính của chúng ta.

Phạm Duy Dy, 11T4 24

Page 25: Báo cáo ĐANLHDH

Xây dựng chương trình producer-consumer với bounded buffer 25

TÀI LIỆU THAM KHẢO

[1] TS Tô Tuấn, Viện CNTT: Công nghệ linux (slide)

[2] Huỳnh Thúc Cước, Viện CNTT: Kiến trúc Unix/Linux

[3] Hà Quang Thụy, Nguyễn Chí Thành, ĐH quốc gia HN: Hệ điều hành Unix/Linux

[4] Tài liệu từ Internet

Phạm Duy Dy, 11T4 25