TỔNG QUAN ĐỀ TÀI 1.1. Bối cảnh đề tài: Nhận thấy việc giao tiếp giữa các tiến trình trên linux có thể giúp ta hiểu rõ hơn cơ chế xử lý, và trao đổi dữ liệu giữa các tiến trình trong hệ điều hành linux. Trong đó việc trao đổi dữ liệu, giao tiếp qua đường ống pipe theo cơ chế FIFO tương đối dễ hiểu và cũng phần nào giúp em hiểu được nguyên lý tổ chức hệ điều hành nên chúng em quyết định chọn đề tài này. 1.2. Mục tiêu đề tài: Hiểu hơn về việc giao tiếp giữa các tiến trình trong hệ điều hành linux đặc biệt là bằng đường ống pipe. Nghiên cứu đề tài này sẽ có thể cho ta hiểu được thế nào là pipe và cơ chế hoạt động của nó. 1.3. Hướng giải quyết: Đầu tiên phải tìm hiểu cách giao tiếp giữa các tiến trình, là thế nào 2 tiến trình giao tiếp với nhau thông qua đường ống pipe. Khi giao tiếp bằng pipe này thì vấn đề gì sẽ nảy sinh ra ( lỗi, hạn chế ). Từ đó tìm ra cách giải quyết và mô phỏng cách sử dụng pipe thông qua bài toán. Khi một pipe được thiết lập giữa hai tiến trình, một tiến trình sẽ ghi dữ liệu vào pipe còn tiến trình kia đọc dữ liệu từ pipe. Điều trước hết ta phải tạo hai tiến trình cha và con, làm sao để hai tiến trình này giao tiếp với nhau ta phải tạo một đường ống pipe. Bây giờ chúng ta đi tìm hiểu và tạo các tiến trình để cho nó giao tiếp với nhau. 1.4. Môi trường áp dụng: Chương trình mô phỏng sẽ được viết bằng ngôn ngữ C chạy trên nền hệ điều hành linux để có thể thấy được cơ chế giao tiếp tiến trình thông qua đường ống pipe. Cộng đồng mã nguồn mở GNU cung cấp khá nhiều công cụ biên dịch C/C++ trên hệ điều hành Linux như: CodeBlock Trình biên dịc C/C++ GCC Trình biên dịch C G++ Trình biên dịch C++ gdb Trình gỡ lỗi GNU make Trình quản lý mã nguồn và trợ giúp biên dịch GNU emacs Trình soạn thảo văn bản (hỗ trợ cho việc sửa mã nguồn khi lập trình) Bash Hệ vỏ Shell hỗ trợ các dòng lệnh của hệ điều hành Bison Bộ phân tích tương thích với yacc của UNIX Trong đồ án môn học này, em sử dụng trình biên dịch CodeBlock chạy trên nền hệ điều hành linux Unbuntu 14.04 để áp dụng. CƠ SỞ LÝ THUYẾT 1.5. Giới thiệu về hệ điều hành Linux 1.5.1. Lịch sử ra đời hệ điều hành Linux. Vào năm 1991 tại Phần Lan, Linux B. Torvalds lúc đó là sinh viên ở trường Đại học tổng hợp Hensinki đã dùng một máy tính cá nhân có trang bị bộ xử lí 386 để nghiên cứu cách làm việc của nó. Do hệ điều hành MS-DOS không khai thác đầy đủ các đặc tính của bộ xử lí 386, Linux đã sử dụng một hệ điều hành thương mại khác là Minix. Hệ điều hành Minix là hệ điều hành Unix cỡ nhỏ. Do đối mặt với các hạn chế của hệ điều hành này, Linux bắt đầu viết lại một số một số của phần mềm để thêm chức năng và các điểm đặc trưng. Sau đó, ông thông báo kết quả của mình miễn phí bằng Internet dưới tên gọi Linux – chữ viết tắt của Linus và Unix. Phiên bản đầu tiên của Linux là 0.01 được tung ra vào tháng 8/1991. Các phiên bản đầu tiên có rất nhiều hạn chế. Tuy nhiên, sự kiện các mã nguồn được truyền bá rộng rãi đã giúp phát triển hệ điều hành rất nhanh. Nhiều năm qua, số lượng các công ty khai thác đã không ngừng tăng lên. Ngày nay, Linux được phát triển bởi nhiều người rải rác khắp nơi trên thế giới. Mặc dù 5 phiên bản đầu tiên của Linux tương đối không ổn định, nhưng phiên bản đầu tiên được tuyên bố là ổn định (1.0 ) đã được công bố vào khoảng tháng 3/1994. Số phiên bản đi kèm với kernel có một ý nghĩa đặc trưng bởi vì nó liên quan đến chu kì phát triển. Thực tế, quá trình phát triển Linux diễn ra theo một chuỗi hai giai đoạn: Giai đoạn phát triển: ở đây kernel không có độ tin cậy cao và tiến trình là bổ sung chức năng cho nó, tối ưu hóa nó và thử nghiệm các ý tưởng mới. Giai đoạn này đem lại sự gia tăng số lượng các phiên bản đánh số lẻ, chẳng hạn như 1.1, 1.3… Đây là thời điểm mà lượng công việc tối đa được thực hiện trên kernel. Giai đoạn ổn định: mục đích là tạo ra một kernel càng ổn định càng tốt. Trong trường hợp này, chỉ cho phép thực hiện các hiệu chỉnh, sửa đổi nhỏ. Số phiên bản của các kernel được gọi là ổn định là các số chẵn, chẳng hạn 1.0 , 1.2 và mới nhất là 2.2. Ngày nay, Linux hoàn toàn là một hệ điều hành Unix. Nó ổn định và liên tục phát triển. Nó không chỉ có khả năng phát triển trên các thiết bị ngoại vi mới nhất trên thị trường ( bộ nhớ flash quang , đĩa quang … ) mà hiệu năng của nó còn có thể so sánh với một số hệ điều hành Unix thương mại và thậm chí còn có một số điểm ưu việt hơn. Sau cùng, mặc dù Linux đã có một khoảng thời gian bị giới hạn trong môi trường các trường đại học, bây giờ nó đang được tiếp nhận ở các hãng công nghiệp. . 1.5.2. Các chức năng của hệ điều hành Linux Hệ điều hành Linux có rất nhiều chức năng và chúng khai thác khả năng của các hệ Unix hiện đại theo các cách sau : Đa xử lí: các bộ đa xử lí có thể thực hiện nhiều chương trình đồng thời bất kể sử dụng một hay nhiều bộ xử lí. Đa nền: Cho phép nhiều người sử dụng : giống
Trang 1LỜI NÓI ĐẦU
Nguyên lý hệ điều hành là 1 môn học bổ ích giúp sinh viên chúng em hiểu biếtđược cơ cấu tổ chức, cũng như việc quản lý, điều phối các tiến trình của hệ thống máytính Qua đó hiểu biết phần nào về phần mềm cơ bản nhất của máy tính là hệ điều hành.Việc nghiên cứu, hoàn thành đồ án nguyên lý hệ điều hành cũng giúp chúng em đượchiểu rõ hơn nữa về hệ điều hành Linux, 1 hệ điều hành có nhiều tính năng vượt trội và cótriển vọng trong tương lai
Chúng em xin chân thành cảm ơn sự hướng dẫn của thầy Mai Văn Hà, đã tận tìnhchỉ dẫn giúp chúng em hoàn thành được đề tài đồ án này
TỔNG QUAN ĐỀ TÀI
1.1.Bối cảnh đề tài:
Nhận thấy việc giao tiếp giữa các tiến trình trên linux có thể giúp ta hiểu rõ hơn cơ chế
xử lý, và trao đổi dữ liệu giữa các tiến trình trong hệ điều hành linux Trong đó việc traođổi dữ liệu, giao tiếp qua đường ống pipe theo cơ chế FIFO tương đối dễ hiểu và cũngphần nào giúp em hiểu được nguyên lý tổ chức hệ điều hành nên chúng em quyết địnhchọn đề tài này
1.2.Mục tiêu đề tài:
Hiểu hơn về việc giao tiếp giữa các tiến trình trong hệ điều hành linux đặc biệt là bằngđường ống pipe Nghiên cứu đề tài này sẽ có thể cho ta hiểu được thế nào là pipe và cơchế hoạt động của nó
1.3.Hướng giải quyết:
Đầu tiên phải tìm hiểu cách giao tiếp giữa các tiến trình, là thế nào 2 tiến trình giao tiếpvới nhau thông qua đường ống pipe Khi giao tiếp bằng pipe này thì vấn đề gì sẽ nảy sinh
ra ( lỗi, hạn chế ) Từ đó tìm ra cách giải quyết và mô phỏng cách sử dụng pipe thông quabài toán
Khi một pipe được thiết lập giữa hai tiến trình, một tiến trình sẽ ghi dữ liệu vào pipe còntiến trình kia đọc dữ liệu từ pipe
Trang 2Điều trước hết ta phải tạo hai tiến trình cha và con, làm sao để hai tiến trình này giao tiếpvới nhau ta phải tạo một đường ống pipe Bây giờ chúng ta đi tìm hiểu và tạo các tiếntrình để cho nó giao tiếp với nhau.
1.4.Môi trường áp dụng:
Chương trình mô phỏng sẽ được viết bằng ngôn ngữ C chạy trên nền hệ điều hành linux để có thể thấy được cơ chế giao tiếp tiến trình thông qua đường ống pipe
Cộng đồng mã nguồn mở GNU cung cấp khá nhiều công cụ biên dịch C/C++ trên
hệ điều hành Linux như:
GNU make Trình quản lý mã nguồn và trợ giúp biên dịch
GNU emacs Trình soạn thảo văn bản (hỗ trợ cho việc sửa mã nguồn khi lập trình)
Trong đồ án môn học này, em sử dụng trình biên dịch CodeBlock chạy trên nền hệ điều hành linux Unbuntu 14.04 để áp dụng
SVTH: Hoàng Thị Mai Liên Trang 2
Trang 3CƠ SỞ LÝ THUYẾT
1.5.Giới thiệu về hệ điều hành Linux
1.5.1 Lịch sử ra đời hệ điều hành Linux.
Vào năm 1991 tại Phần Lan, Linux B Torvalds lúc đó là sinh viên ở trường Đại học tổnghợp Hensinki đã dùng một máy tính cá nhân có trang bị bộ xử lí 386 để nghiên cứu cách làm việc của nó Do hệ điều hành MS-DOS không khai thác đầy đủ các đặc tính của bộ xử lí 386, Linux đã sử dụng một hệ điều hành thương mại khác là Minix Hệ điều hành Minix là hệ điều hành Unix cỡ nhỏ
Do đối mặt với các hạn chế của hệ điều hành này, Linux bắt đầu viết lại một số một số của phần mềm để thêm chức năng và các điểm đặc trưng Sau đó, ông thông báo kết quả của mình miễn phí bằng Internet dưới tên gọi Linux – chữ viết tắt của Linus và Unix Phiên bản đầu tiên của Linux là 0.01 được tung ra vào tháng 8/1991
Các phiên bản đầu tiên có rất nhiều hạn chế Tuy nhiên, sự kiện các mã nguồn được truyền bá rộng rãi đã giúp phát triển hệ điều hành rất nhanh Nhiều năm qua, số lượng các công
ty khai thác đã không ngừng tăng lên Ngày nay, Linux được phát triển bởi nhiều người rải rác khắp nơi trên thế giới
Mặc dù 5 phiên bản đầu tiên của Linux tương đối không ổn định, nhưng phiên bản đầu tiên được tuyên bố là ổn định (1.0 ) đã được công bố vào khoảng tháng 3/1994 Số phiên bản đi kèm với kernel có một ý nghĩa đặc trưng bởi vì nó liên quan đến chu kì phát triển Thực tế, quá trình phát triển Linux diễn ra theo một chuỗi hai giai đoạn:
Giai đoạn phát triển: ở đây kernel không có độ tin cậy cao và tiến trình là bổ sung chức
năng cho nó, tối ưu hóa nó và thử nghiệm các ý tưởng mới Giai đoạn này đem lại sự gia tăng sốlượng các phiên bản đánh số lẻ, chẳng hạn như 1.1, 1.3… Đây là thời điểm mà lượng công việc tối đa được thực hiện trên kernel
Giai đoạn ổn định: mục đích là tạo ra một kernel càng ổn định càng tốt Trong trường
hợp này, chỉ cho phép thực hiện các hiệu chỉnh, sửa đổi nhỏ Số phiên bản của các kernel được gọi là ổn định là các số chẵn, chẳng hạn 1.0 , 1.2 và mới nhất là 2.2
Ngày nay, Linux hoàn toàn là một hệ điều hành Unix Nó ổn định và liên tục phát triển
Nó không chỉ có khả năng phát triển trên các thiết bị ngoại vi mới nhất trên thị trường ( bộ nhớflash quang , đĩa quang … ) mà hiệu năng của nó còn có thể so sánh với một số hệ điều hànhUnix thương mại và thậm chí còn có một số điểm ưu việt hơn Sau cùng, mặc dù Linux đã cómột khoảng thời gian bị giới hạn trong môi trường các trường đại học, bây giờ nó đang được tiếpnhận ở các hãng công nghiệp
1.5.2. Các chức năng của hệ điều hành Linux
Hệ điều hành Linux có rất nhiều chức năng và chúng khai thác khả năng của các hệ Unix hiện đại theo các cách sau :
Đa xử lí: các bộ đa xử lí có thể thực hiện nhiều chương trình đồng thời bất kể sử dụng một hay nhiều bộ xử lí
Trang 4Đa nền: Cho phép nhiều người sử dụng : giống như tất cả các hệ Unix, Linux cho phép nhiều người sử dụng cùng làm việc trên một máy ở cùng thời điểm.
Hỗ trợ truyền thông giao xử lí ( Pipes , IPC , Sockets )
Quản lí các thông điệp điều khiển khác nhau
Hệ thống quản lí thiết bị đầu cuối tuân thủ theo tiêu chuẩn POSIX Linux cũng giả các thiết bị đầu cuối cũng như điều khiển quá trình
Hỗ trợ một dải rộng các thiết bị ngoại vi, chẳng hạn như các card âm thanh, giao diện đồ hoa, mạng, giao diện hệ máy tính nhỏ …
Buffer cache : vùng bộ nhớ được dành để làm vùng đệm cho các đầu vào và đầu ra từ cácquá trình khác nhau
Hệ thống quản lí bộ nhớ trang yêu cầu Một trang sẽ không được nạp chừng nào nó không thực sự cần thiết ở bộ nhớ
Các thư viện động và dùng chung : Các thư viện động chỉ được tải khi chúng thật sự cần thiết và mã của chúng được dùng chung nếu nhiều ứng dụng đang dùng chúng
Các hệ thống file có thể quản lí tốt và đồng đều các phân hoạch file Linux được sử dụng bởi filesystem làm các phân hoạch có các định dạng khác ( MS-DOS , ISO9660, vv )
Thiết bị của TCP/IP và các giao thức mạng khác
1.5.3 Hạn chế của hệ điều hành Linux:
Hiện nay hệ điều hành Linux vẫn còn rất hạn chế đối với người sử dụng, nó vẫn chưađược sử dụng một cách rộng rãi Một phần là do có quá ít phần mền tương thích với nó
1.6.Tiến trình trong Linux:
Tiến trình là một chương trình đang xử lý, sở hữu một con trỏ lệnh, tập các thanh ghi và các biến Để hoàn thành tác vụ của mình, một tiến trình có thể cần đến một số tài nguyên – như CPU, bộ nhớ chính, các tập tin và thiết bị nhập/xuất
Cần phân biệt 2 khái niệm chương trình và tiến trình Một chương trình là một thực thể thụ
động, chứa đựng các chỉ thị điều khiển máy tính để tiến hành một tác vụ nào đó Khi cho thực hiện các chỉ thị này, chương trình chuyển thành tiến trình, là một thực thể hoạt động, với con trỏ lệnh xác định chỉ thị kế tiếp sẽ thi hành, kèm theo tập các tài nguyên phục vụ cho hoạt động của tiến trình
Về mặt ý niệm, có thể xem như mỗi tiến trình sỡ hữu một bộ xử lý ảo cho riêng nó,nhưng trong thực tế, chỉ có một bộ xử lý thật sự được chuyển đổi qua lại giữa các tiến trình Sự
chuyển đổi nhanh chóng này được gọi là sự đa chương (multiprogramming) Hệ điều hành chịu
trách nhiệm sử dụng một thuật toán điều phối để quyết định thời điểm cần dừng hoạt động củatiến trình đang xử lý để phục vụ một tiến trình khác, và lựa chọn tiến trình tiếp theo sẽ được phục
vụ 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 (scheduler)
SVTH: Hoàng Thị Mai Liên Trang 4
Trang 51.6.1 Các trạng thái của tiến trình
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ờicủa tiến trình tại thời điểm đó Trong quá trình tồn tại, một tiến trình thay đổi trạng thái
do nhiều nguyên nhân như : phải chờ một sự kiện nào đó xảy ra, hay đợi một thao tácnhập/xuất hoàn tất, buộc phải dừng hoạt động do đã hết thời gian xử lý …
Tại một thời điểm, một tiến trình có thể nhận trong một các trạng thái sau đây :
In execution ( thực hiện ) : Tiến trình đang được bộ xử lý thực hiện.
Ready ( sẵn sàng ): Tiến trình có thể được thực hiện nhưng một tiến trình khác lại
Stop (ngừng) : Tiến trình đã bị treo bởi một tiến trình ngoài
Các thay đổi trạng thái của một tiến trình được trình bày ở sơ đồ trạng thái sau :
Hình 1 : Sơ đồ trạng thái của một tiến trình.
Tại một thời điểm, chỉ có một tiến trình có thể nhận trạng thái Thực hiện trên một
bộ xử lý bất kỳ Trong khi đó, nhiều tiến trình có thể ở trạng thái khác
Các cung chuyển tiếp trong sơ đồ trạng thái biễu diễn những sự chuyển trạng thái có thể xảy ra trong các điều kiện sau :
- Tiến trình mới tạo được đưa vào hệ thống
- Bộ điều phối cấp phát cho tiến trình một khoảng thời gian sử dụng CPU
Nhập / Xuất
Kết thúc
Nhập / Xuất
Kết thúcTín hiệu
Tín hiệu
Trang 6- Tiến trình yêu cầu một tài nguyên nhưng chưa được đáp ứng vì tài nguyên chưa sẵnsàng để cấp phát tại thời điểm đó; hoặc tiến trình phải chờ một sự kiện hay thao tác nhập/xuất.
- Bộ điều phối chọn một tiến trình khác để cho xử lý
- Tài nguyên mà tiến trình yêu cầu trở nên sẵn sàng để cấp phát ; hay sự kiện hoặc thaotác nhập/xuất tiến trình đang đợi hoàn tất
1.6.2 Cấu trúc tiến trình
Một tiến trình được đặc trưng bởi nhiều thuộc tính do hệ thống duy trì như:
Trạng thái của nó
Định danh của nó
Các giá trị của các thanh ghi, bao gồm cả bộ đếm chương trình
Mã định danh người sử dụng có tên mà tiến trình đang thực hiện
Thông tin được kernel sử dụng để thiết lập lịch biểu của các tiến trình (thứ tự
Tính tương thích thông tin tổng kết các tài nguyên do tiến trình sử dụng
Ví dụ, khi có hai người dùng (user), một mang tên là A, một mang tên là B cùng đăng nhập và chạy chương trình C ( chương trình có nhiệm vụ tìm một chuỗi ký tự trong file) đồng thời, thực
tế hệ điều hành sẽ quản lý và nạp mã của chương trình C vào hai vùng nhớ khác nhau và gọi mỗi
phân vùng như vậy là tiến trình Hình cho thấy cách phân chia chương trình C thành hai tiến
trình cho hai người dùng khác nhau sử dụng
Ở hình minh họa, người dùng A chạy chương trình C tìm chuỗi B trong tệp findgirl.txt
$CB findA.txt
Trong khi người dùng B chạy C và tìm chuỗi A trong tệp findboy.txt
$CA findB.txt
Chúng ta nên nhớ rằng hai người dùng A và B có thể ở hai máy tính khác nhau đăng nhập vào
máy chủ Linux và gọi C chạy đồng thời Hình 2 là hiện trạng không gian bộ nhớ hệ điều hành
Linux khi chạy chương trình C phục vụ người dùng.
SVTH: Hoàng Thị Mai Liên Trang 6
Trang 7Nếu dùng lệnh ps, hệ thống sẽ liệt kê cho bạn thông tin về các tiến trình mà hệ điều hành đang kiểm soát Ví dụ với tham số -af , chúng ta có thể thấy các thông tin do ps liệt kê như sau
root 2345 1234 0 Apr11 pts/0 00:00:00 [bash]
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 mới
Tiến trình với PID = 1 là tiến trình init, tiến trình init được gọi và chạy ngay khi khởi động hệ điều hành, init là tiến trình quản lý và tạo ra mọi tiến trình con khác.
Chúng ta thấy lệnh ps –af hiển thị hai tiến trình A và B với các số PID lần lượt là 111 và 222.
Mã lệnh thực thi của lệnh C chứa trong tệp chương trình có sẵn trong đĩa được hệ điều hành nạp
vào bộ nhớ Như đã thấy trên hình , mỗi tiến trình được hệ điều hành phân chia rõ ràng : vùng chứa mã lệnh (code), vùng chứa dữ liệu (data) Mã lệnh thường là giống nhau và có thể sử dụng chung Linux quản lý cho phép tiến trình của cùng một chương trình có thể sử dụng chung mã lệnh của nhau
Thư viện cũng vậy Trừ những thư viện đặc thù còn thì các thư viện chuẩn sẽ được hệ điều hành cho phép chia sẻ và dùng chung bởi mọi tiến trình trong hệ thống Việc chia sẻ thư viện, kích thước chương trình sẽ giảm đi đáng kể
Trang 8Trừ mã lệnh và thư viện còn hầu như dữ liệu không thể chia sẻ bởi các tiến trình Mỗi tiến trình
sở hữu phân đoạn dữ liệu riêng ( như ta thấy ở ví dụ trên) Mỗi tiến trình cũng được hệ thống dành riêng cho một bảng mô tả file Bảng này chứa các số mô tả áp đặt cho các file đang được
mở Và mỗi tiến trình có riêng ngăn xếp stack để lưu biến cục bộ và các giá trị trả về sau lời gọi hàm Tiến trình cũng được dành cho khoảng không gian dành riêng để lưu các biến môi trường Cuối cùng mỗi tiến trình hoạt động trong không gian địa chỉ ảo độc lập do hệ thống cấp phát Chúng ta không cần quan tâm đến bộ nhớ vật lý Linux và UNIX có cách quản lý phân trang cộng với hoán chuyển giữa bộ nhớ vật lý và bộ nhớ ảo thông qua phân vùng swap
Bảng thông tin tiến trình
Hệ điều hành lưu dữ một cấu trúc danh sách bên trong hệ thống gọi là bảng tiến trình(process table).Bảng tiến trình quản lý tất cả các Pid của hệ thống cùng với các thông tin chi tiết về các tiến trình đang chạy
Ta có thể xem bảng tiến trình với lệnh ps –af ( với tùy chọn –af để hiển thị chi tiết ) :
root 2345 1234 0 Apr11 pts/0 00:00:00 [bash]
UID : tên người dùng đã gọi tiến trình.
PID : là số định danh mà hệ thống cấp cho tiến trình ( được dùng làm khóa chính và đánh chỉ mục để truy xuất thông tin).
PPID : parent PID là số định danh của tiến trình cha.
STIME : Thời điểm tiến trình được đưa vào sử dụng.
TTY : Là màn hình terminal ảo nơi gọi thực thi tiến trình
TIME : là thời gian chiếm dụng CPU của tiến trình
CMD : là toàn bộ dòng lệnh khi tiến trình được triệu gọi
1.6.3. Tạo lập tiến trình
Ta có thể gọi một chương trình khác bên trong chương trình đang chạy bằng hàm system() Hay nói cách khác ta có thể tạo ra một tiến trình mới từ một tiến trình đang chạy Hàm system() được khai báo như sau :
Hàm này gọi chuỗi lệnh cmdstr thực thi và chờ lệnh chấm dứt mới quay về nơi gọi hàm
Nó tương đương với việc ta gọi shell thực thi lệnh của hệ thống
SVTH: Hoàng Thị Mai Liên Trang 8
#include <stdlib.h>
int system( const char *cmdstr);
$sh –c cmdstr
Trang 9system() sẽ trả về mã lỗi 127 nếu như không thể khởi động shell để gọi lệnh cmdstr Mã lỗi –1 nếu gặp các lỗi khác Ngược lại mã trả về của system() là mã lỗi do cmdstr sau khi thực thi trả về.
Ví dụ cách sử dụng hàm system() trong chương trình:
1.6.4. Thay thế tiến trình hiện hành với hàm exec
Mỗi tiến trình được hệ điều hành cấp cho một không gian nhớ riêng biệt để tiến trình hoạtđộng Nếu tiến trình A gọi một chương trình ngoài B (bằng hàm system() chẳng hạn) hệ điều hành thường thực hiện các thao tác như : cấp phát không gian bộ nhớ cho tiến trình mới, điều chỉnh lại danh sách các tiến trình, nạp mã lệnh của chương trình B trên đĩa vào không gian nhớ vừa cấp phát cho tiến trình Đưa tiến trình vào danh sách cần điều phối của hệ điều hành Những công việc trên đòi hỏi thời gian và chiếm tài nguyên của hệ thống
Nếu tiến trình A đang chạy và ta muốn tiến trình B khởi động chạy trong không gian bộ nhớ đã cấp sẵn cho tiến trình A : Linux cung cấp cho ta tập hợp các hàm exec() thực hiện chức năng kháthú vị này Hàm exec() sẽ thay thế toàn bộ ảnh của tiến trình A (bao gồm mã lệnh, dữ liệu, bảng
mô tả file) thành ảnh của tiến trình B hoàn toàn khác Chỉ có số định danh PID của tiến trình A làcòn giữ lại Hàm thay thế ảnh của tiến trình bao gồm tập các hàm sau :
Đa số các hàm này đều yêu cầu bạn chỉ đối số path hoặc file là đường dẫn đến chương trình cần thực
printf(“Running ps with system\n”);
extrn char **environ;
int execl(const char *path, const char *arg,…);
int execlp(const char *file, const char *arg…);
int execle(const char *path, const char *arg,…,char *const envp[]); int exect(const char *path, const char *argv[]);
int execv(const char *path, const char *argv[]);
int execvp(const char *path, const char *argv[]);
Trang 10Khi chạy chương trình lệnh ps được thực thi, chương trình exec của ta không chờ ps
chấm dứt và cũng khôngbao giờ in ra chuỗi Done
Lý do là toàn bộ mã lệnh
và không gian của tiếntrình exec sau khi gọihàm execlp đã bị thay thếbởi tiến trình mới ps
1.6.5. Nhân bản tiến trình với hàm fork()
Tiến trình hiện hành có thể tạo ra một tiến trình con nhờ sử dụng lệnh gọi hệ thống fork().Nguyên bản của nó là làm nhân bản tiến trình hiện hành> Được khai báo như sau :
Hình 3: Cơ chế phân chia tiến trình của fork()
Trường hợp không tách được, fork() sẽ trả về trị -1 Kiểu pid_t được khai báo và định danh trongunistd.h là kiểu nguyên(int)
SVTH: Hoàng Thị Mai Liên Trang 10
Khởi tạo tiến trình chính
Gọi fork()
Trả về PID của tiến trình
Mã lệnh kế tiếp của tiến trình đầu(cha)
Mã lệnh thực thi tiến trình mới (con)
Trang 11Cả hai tiến trình trên hoạt động đồng thời và có thể đang xen nhau.
Ví dụ cho cách sử dụng hàm fork() để nhân đôi tiến trình:
1.6.6.
Kiểm soát và đợi tiến trình con
Khi fork() tách tiến trình chính thành hai tiến trình cha và con, trên thực tế cả tiến trình cha
lẫn tiến trình con đều hoạt động độc lập Để có trật tự hơn, ta muốn khi tiến trình cha kết thúc thì tiến trình con cũng phải hoàn tất, hay hơn nữa là tiến trình con phải được thực hiện xong rồi mới đến tiến trình cha Hàm wait() sẽ giúp ta thực hiện công việc này Wait() được khai báo như
sau :
Hàm wait() được gọi trong tiến trình cha sẽ dừng lại chờ tiến trình con kết thúc trước khi thực hiện tiếp các lệnh điều khiển trong tiến trình cha wait()làm cho sự liên lạc giữa tiến trình cha và tiến trình con trở nên tuần tự Khi tiến trình con kết thúc hàm sẽ trả về số PID tương ứng của tiến trình con Nếu ta truyền thêm đối số stat_loc khác Null cho hàm thì wait() cũng sẽ trả về trạng thái mà tiến trình con kết thúc trong biến stat_loc Ta có thể sử dụng các macro khai báo sẵn trong sys/wait.h để diễn dịch ý nghĩa trạng thái như sau :
WIFEXITED(stat_loc) Trả về trị khác 0 nếu tiến trình con kết
thúc bình thườngWEXITSTATUS(stat_loc) Trả về mã lỗi của tiến trình con
WIFSIGNALED(stat_loc) Trả về trị khác 0 nếu tiến trình con kết
thúc bởi một tín hiệu gửi đến nhưngkhông đón bắt và xử lý được
WTERMSIG(stat_loc) Cho biết số tín hiệu đã hủy tiến trình
conWIFSTOPPED(stat_loc) Trả về trị khác 0 nếu tiến trình con đã
Trang 12WSTOPSIG(stat_loc) Trả về số hiệu của signal
1.6.7. Đón xử lý tín hiệu khi tiến trình con kết thúc
Tiến trình cha có thể đón bắt tình huống hay tín hiệu SIGCHILD khi tiến trình conchấm dứt và gán cho nó một tác vụ thự thi nào đó Bằng cách này, tiến trình cha có thểkhông cần phải gọi wait() để chờ tiến trình con kết thúc mới có thể thực hiện được tác vụtiếp theo Khi tạo tiến trình con xong, tiến trình cha tiếp tục công việc của mình Khi tiếntrình con chấm dứt và gửi tín hiệu đến tiến trình cha, hàm hay bộ xử lý tín hiệu của tiếntrình cha sẽ được triệu gọi thực thi
Hàm catch_child() được dùng để xử lý tín hiệu(còn gọi là bộ xử lý tín hiệu) Đểgán một hàm xử lý tín hiệu cho một tín hiệu cụ thể nào đó, ta gọ hàm hệ thống signal()
1.6.8. Bỏ rơi tiến trình con
Như ta đa biết, lệnh fork() tách đôi một tiến trình thành tiến trình cha và tiến trìnhcon Cả hai tiến trình cùng hoạt động độc lập và hầu như không phụ thuộc lẫn nhau Ta
có thể dùng hàm wait() để lấy thông tin và kiểm tra trạng thái kêt thúc của tiến trình con.Tuy nhiên nếu tiến trình cha kết thúc trước, tiến trình con vẫn còn hoạt động, đến khi kếtthúc, nó sẽ không còn điểm trở về để liên hệ với tiến trình cha đa sinh ra nó trước đó.Những tiến trình con như vậy gọi là zombie( dỡ dang) Hệ điều hành khi đó sẽ gán PPIDcủa tiến trình con về 1 Có nghĩa là nếu tiến trình cha của tiến trình con mất đi thì tiếntrình con sẽ được chuyển giao cho tiến trình thủy tổ cao nhất, đó là tiến trình init
Ta nên kiểm soát tiến trình con của mình do fork() sinh ra Cố gắng đừng để tiếntrình con ở trạng thái bị bỏ rơi Mặc dù tiến trình init có cách thu lượm các tiến trình con
bị bỏ rơi, nhưng bản thân của tiến trình cũng chiếm một phần thời gian và tài nguyên của
hệ thống
1.7.Giao tiếp tiến trình
1.7.1. Tổng quan về giao tiếp giữa các tiến trình
Trong hệ thống các tiến trình không phải chạy hoàn toàn độc lập với nhau, nhưng
nó có thể giao tiếp với nhau, 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ụ: Nhiều tiến trình cùng tham gia thực hiện một công
việc Hình thức này sẽ đẩy nhanh khả năng xử lý công việc trong hệ thống
SVTH: Hoàng Thị Mai Liên Trang 12
Trang 131.7.2 Các cơ chế giao tiếp tiến trình trong Linux
Hệ điều hành Linux cung cấp cho người dùng một số cơ chế giao tiếp giữa các tiến trình như là:
- Giao tiếp bằng tín hiệu (signals handing)
- Giao tiếp bằng đường ống (pipe)
- Giao tiếp bằng hàng đợi thông điệp (message queue)
- Giao tiếp bằng sử dụng vùng nhớ chia sẻ (share memory)
- Giao tiếp bằng đồng bộ tín hiệu semephore
- Giao tiếp trao đổi thông qua socket
1.7.3. Các vấn đề nảy sinh trong giao tiếp giữa các tiến trình
Do mỗi tiến trình sở hữu một không gian địa chỉ riêng biệt, nên các tiến trình không thểliên lạc trực tiếp dễ dàng mà phải nhờ vào các cơ chế do hệ điều hành cung cấp Khi cungcấp cơ chế liên lạc cho các tiến trình, hệ điều hành thường phải tìm giải pháp cho các vấn
đề chính yếu sau:
- Giao tiếp tường minh hay tiềm ẩn (explicit naming/ implicit naming): tiến trình có cần
phải biết tiến trình nào đang trao đổi hay chia sẻ thông tin với nó? Mỗi liên kết được gọi
là tường minh khi được thiết lập rõ ràng, trực tiếp giữa các tiến trình và là tiềm ẩn khi cáctiến trình liên lạc với nhay thông qua một quy ước ngầm nào đó
- Giao tiếp theo chế độ đồng bộ hay không đồng bộ (blocking/ nonblocking): Khi tiếntrình trao đổi thông tin với một tiến trình khác, các tiến trình có cần phải đợi cho thao tácliên lạc hoàn tất rồi mới tiếp tục các xử lý khác? Các tiến trình liên lạc theo cơ chế đồng
bộ sẽ chờ nhau hoàn tất việc liên lạc, còn các tiến trình liên lạc theo cơ chế không đồng
bộ thì không
- Giao tiếp giữa các tiến trình trong hệ thống tập trung và hệ thống phân tán: Cơ chế liênlạc giữa các tiến trình trong cùng một máy tính có sự khác biệt so với việc liên lạc giữacác tiến trình giữa những máy tính khác nhau
1.8.Cơ chế giao tiếp bằng đường ống (pipe)
1.8.1. Định nghĩa
Các pipe (ống dẫn) tạo thành phương tiện truyền thông giữa những tiến trình Sựtruyền dữ liệu giữa các tiến trình được thực hiện thông qua một lệnh truyền : dữ liệu đãghi tại một đầu của kênh được đọc ở đầu bên kia
Ống dẫn truyền thông
Trang 14Hình 4: Truyền thông giữa hai tiến trình bằng pipe.
Các pipe không cung cấp một truyền thông theo cấu trúc Thao tác đọc độc lập với
thao tác ghi và do đó tại cấp độ của các lệnh gọi hệ thống, người ta không thể biết được
kích cỡ, người gởi và người nhận dữ liệu chứa trong pipe Mặt khác, một ưu điểm của
phương pháp truyền thông này là khả năng sử dụng dữ liệu đã ghi nhiều lần để được đọc
chỉ một lần
Ta có thể thấy rõ trong việc chuyển dữ liệu trong Linux, ví dụ :
$ps –ax | grep ‘runner’
Lệnh trên trước hết thực hiện lệnh ps –ax để liệt kê chi tiết danh sách của tất cả
những tiến trình đang chạy trong hệ thống Kết quả tập dữ liệu mà ps –ax trả về sau đó sẽ
được chuyển cho lệnh grep thông qua đường ống ( | ) Lệnh grep tiếp nhận dữ liệu và
thực hiện tìm chuỗi runner trong dữ liệu do ps gửi sang Kết quả của tổ hợp lệnh trên là
chỉ có thông tin về tiến trình mang tên runner đang chạy được in ra chi tiết
1.8.2. Đường ống pipe giao tiếp trao đổi dữ liệu một chiều
Hệ thống cung cấp cho ta hàm pipe() để tạo đường ống có khả năng đọc/ghi Sau
khi đường ống được tạo ra, ta có thể dùng nó để giao tiếp giữa hai tiến trình (thường là
tiến trình cha và tiến trình con sinh ra bởi hàm fork()) Mỗi đường ống khi tạo ra được
kèm theo một số mô tả để ta có thể truy xuất
Hàm này yêu cầu đối số là mảng filedes bao gồm hai phần tử nguyên dùng để lưu
lại số mô tả cho đường ống trả về sau lời gọihàm Nếu hàm thành công ta có thể dùng hai số
mô tả này để thực hiện thao tác đọc/ghi trênđường ống Phần tử thứ nhất của mảng đượcdùng để đọc, trong khi phần tử thứ hai của mảng được dùng để ghi Ví dụ về cách gọi
hàm pipe() tạo đường ống.
Nếu pipe() thực hiện thành công, đường ống sẽ được tạo ra, pipes[0] là số mô tả
đường ống phía đầu đọc trong khi pipes[1] là số mô tả đường ống phía đầu ghi Nếu
không tạo được đường ống mã lỗi trả về sẽ là –1
Trong ví dụ dưới đây, sau khi tạo ra đường ống, ta gọi hàm fork() để tạo ra tiến
trình con Như ta đã biết, khi nhân đôi tiến trình, bộ mô tả file của tiến trình cha và tiến
trình con có khả năng kế thừa nhau cho nên pipes[] mở bởi tiến trình cha cũng được nhân
bản và sao chép sang tiến trình con Tiến trình cha sẽ đọc dữ liệu nhập vào từ phía người
SVTH: Hoàng Thị Mai Liên Trang 14
#include <unistd.h>
int pipe(int filedes[2])
/* Trước hết định nghĩa mảng bao gồm hai phần tử để chứa số mô tả đường ống */ int pipes[2]
/* Thực hiện tạo đường ống pipe */
int rc = pipe (pipes);
if (rc == -1) { /* tạo đường ống bị lỗi*/
perror(“pipe”);
exit(1);
}