Hàng chờ kích thước zero (zero capacity queue) được sử dụng ở đâu?

21 CHƯƠNG 2. QUẢN LÝ TIẾN TRÌNH 2.1. Khái niệm tiến trình 2.1.1. Tiến trình và các loại tiến trình a. Tiến trình (process): Tiến trình là một khái niệm cơ bản trong hệ điều hành. Tiến trình là một chương trình đang chạy. Mỗi tiến trình có không gian địa chỉ riêng (address space), đó là vùng bộ nhớ dành riêng cho tiến trình, tiến trình có thể đọc và ghi trong vùng bộ nhớ này. Vùng bộ nhớ này chứa chương trình, dữ liệu của chương trình và stack. Trong hệ thống chia xẻ thời gian (timesharing system), hệ điều hành định kỳ ngưng tạm thời (treo, suspend) một tiến trình đang chạy và cho một tiến trình khác chạy. Khi tiến trình được chạy lại, tiến trình được khôi phục lại trạng thái giống như trước khi bị ngưng, do vậy tất cả các thông tin về tiến trình sẽ được cất giữ trước khi bị treo. Các thông tin này được lưu trong bảng điều hành (operating system table) của hệ điều hành, mỗi mục trong bảng điều hành chứa thông tin của một tiến trình. Hàm gọi hệ thống liên quan đến tiến trình về cơ bản gồm có tạo tiến trình và chấm dứt tiến trình. Một tiến trình có thể tạo những tiến trình khác, các tiến trình này được gọi là tiến trình con (child process), và ta có một cây tiến trình. Hình 2.1. Cây tiến trình. Mỗi người được quyền sử dụng hệ thống sẽ có một số hiệu nhận diện UID (User Identification), chỉ có người quản trị hệ thống mới được quyền gán UID cho người sử dụng. Tiến trình khi chạy mang UID của người sử dụng chạy nó. Tiến trình con sẽ có UID giống như tiến trình cha. Người sử dụng có thể thuộc một nhóm nào đó và cũng có số hiệu nhận diện nhóm GID (Group Identification). Trong hệ thống có một UID quan trọng, được gọi là superuser, có quyền cao nhất. Chỉ có người quản trị hệ thống mới có mật khẩu để trở thành superuser. Superuser có thể vi phạm bất cứ sự bảo vệ an toàn hệ thống. b. Các loại tiến trình: Các tiến trình trong hệ thống có thể chia thành hai loại: tiến trình tuần tự và tiến trình song song. Tiến trình tuần tự là các tiến trình mà điểm khởi tạo của nó là điểm kết thúc của tiến trình trước đó. Tiến trình song song là các tiến trình mà điểm khởi tạo của tiến trình này mằn ở thân của các tiến trình khác, tức là có thể khởi tạo một tiến trình mới khi các tiến trình trước đó chưa kết thúc. Tiến trình song song được chia thành nhiều loại: 22 Tiến trình song song độc lập: là các tiến trình hoạt động song song nhưng không có quan hệ thông tin với nhau, trong trường hợp này hệ điều hành phải thiết lập cơ chế bảo vệ dữ liệu của các tiến trình, và cấp phát tài nguyên cho các tiến trình một cách hợp lý. Tiến trình song song có quan hệ thông tin: trong quá trình hoạt động các tiến trình thường trao đổi thông tin với nhau, trong một số trường hợp tiến trình gửi thông báo cần phải nhận được tín hiệu từ tiến trình nhận để tiếp tục, điều này dễ dẫn đến bế tắc khi tiến trình nhận tín hiệu không ở trong trạng thái nhận hay tiến trình gửi không ở trong trạng thái nhận thông báo trả lời. Tiến trình song song phân cấp: Trong qua trình hoạt động một tiến trình có thể khởi tạo các tiến trình khác hoạt động song song với nó, tiến trình khởi tạo được gọi là tiến trình cha, tiến trình được tạo gọi là tiến trình con. Trong mô hình này hệ điều hành phải giải quyết vấn đề cấp phát tài nguyên cho các tiến trình con. Tiến trình con nhận tài nguyên ở đâu, từ tiến trình cha hay từ hệ thống. Để giải quyết vấn đề này hệ điều hành đưa ra 2 mô hình quản lý tài nguyên: Thứ nhất, mô hình tập trung, trong mô hình này hệ điều hành chịu trách nhiệm phân phối tài nguyên cho tất cả các tiến trình trong hệ thống. Thứ hai, mô hình phân tán, trong mô hình này hệ điều hành cho phép tiến trình con nhận tài nguyên từ tiến trình cha, tức là tiến trình khởi tạo có nhiệm vụ nhận tài nguyên từ hệ điều hành để cấp phát cho các tiến trình mà nó tạo ra, và nó có nhiệm vụ thu hồi lại tài nguyên đã cấp phát trả về cho hệ điều hành trước khi kết thúc. Tiến trình song song đồng mức: là các tiến trình hoạt động song song sử dụng chung tài nguyên theo nguyên tắc lần lượt, mỗi tiến trình sau một khoảng thời gian chiếm giữ tài nguyên phải tự động trả lại tài nguyên cho tiến trình kia. Các tiến trình tuần tự chỉ xuất hiện trong các hệ điều hành đơn nhiệm đa chương, như hệ điều hành MS_DOS, loại tiến trình này tồn tại nhiều hạn chế, điển hình nhất là không khai thác tối đa thời gian xử lý của CPU. Các tiến trình song song xuất hiện trong các hệ điều hành đa nhiệm đa chương, trên cả hệ thống một CPU và nhiều CPU. Nhưng sự song song thực, chỉ có ở các hệ thống nhiều CPU, trong hệ thống này mỗi CPU chịu trách nhiệm thực hiện một tiến trình. Sự song song trên các hệ thống một CPU là sự song song giả, các tiến trình song song trên hệ thống này thực chất là các tiến trình thay nhau sử dụng CPU, tiến trình này đang chạy thì có thể dừng lại để nhường CPU cho tiến trình khác chạy và sẽ tiếp tục lại sau đó khi có được CPU. Đây là trường hợp mà ở trên ta cho rằng: điểm khởi tạo của tiến trình này nằm ở thân của tiến trình khác. Hình vẽ sau đây minh họa sự khác nhau, về mặt thực hiện, giữa các tiến trình song song/ đồng thời trong hệ thống một CPU với các tiến trình song song/ đồng thời trong hệ thống nhiều CPU. Trong giáo trình này chúng ta chỉ khảo sát sự hoạt động của các tiến trình song song (hay đồng thời) trên các hệ thống một CPU. Đối với người sử dụng thì trong hệ thống chỉ có hai nhóm tiến trình. Thứ nhất, là các tiến trình của hệ điều hành. Thứ hai, là các tiến trình của chương trình người sử dụng. Các tiến trình của hệ điều hành hoạt động trong chế độ đặc quyền, nhờ đó mà nó có thể truy xuất vào các vùng dữ liệu được bảo vệ của hệ thống. Trong khi đó các tiến trình của chương trình người sử dụng hoạt động trong chế độ không đặc quyền, nên nó không thể truy xuất vào hệ 23 thống, nhờ đó mà hệ điều hành được bảo vệ. Các tiến trình của chương trình người sử dụng có thể truy xuất vào hệ thống thông qua các tiến trình của hệ điều hành bằng cách thực hiện một lời gọi hệ thống. 2.1.2. Mô hình tiến trình Đa số các hệ điều hành đều muốn đưa sự đa chương, đa nhiệm vào hệ thống. Tức là, trong hệ thống có thể có nhiều chương trình hoạt động đồng thời (concurrence) với nhau. Về nguyên tắc, để thực hiện được điều này thì hệ thống phải có nhiều CPU, mỗi CPU có nhiệm vụ thực hiện một chương trình, nhưng mong muốn của hệ điều hành cũng như người sử dụng là thực hiện sự đa chương trên các hệ thống chỉ có một CPU, và trên thực tế đã xuất hiện nhiều hệ điều hành thực hiện được điều này, hệ điều hành windows9x, windowsNT/2000 chạy trên máy tính cá nhân là một ví dụ. Để thực hiện được điều này hệ điều hành đã sử dụng mô hình tiến trình để tạo ra sự song song giả hay tạo ra các CPU logic từ CPU vật lý. Các CPU logic có thể hoạt động song song với nhau, mỗi CPU logic chịu trách nhiệm thực hiện một tiến trình. Trong mô hình tiến trình hệ điều hành chia chương trình thành nhiều tiến trình, khởi tạo và đưa vào hệ thống nhiều tiến trình của một chương trình hoặc của nhiều chương trình khác nhau, cấp phát đầy đủ tài nguyên (trừ CPU) cho tiến trình và đưa các tiến trình sang trạng thái sẵn sàng. Hệ điều hành bắt đầu cấp CPU cho một tiến trình trong số các tiến trình ở trạng thái sẵn sàng để tiến trình này hoạt động, sau một khoảng thời gian nào đó hệ điều hành thu hồi CPU của tiến trình này để cấp cho một tiến trình sẵn sàng khác, sau đó hệ điều hành lại thu hồi CPU từ tiến trình mà nó vừa cấp để cấp cho tiến trình khác, có thể là tiến trình mà trước đây bị hệ điều hành thu hồi CPU khi nó chưa kết thúc, và cứ như thế cho đến khi tất cả các tiến trình mà hệ điều hành khởi tạo đều hoạt động và kết thúc được. Điều đáng chú ý trong mô hình tiến trình này là khoảng thời gian chuyển CPU từ tiến trình này sang tiến trình khác hay khoảng thời gian giữa hai lần được cấp phát CPU của một tiến trình là rất nhỏ nên các tiến trình có cảm giác luôn được sở hữu CPU (logic) hay hệ thống có cảm giác các tiến trình/ chương trình hoạt động song song nhau. Hiện tượng này được gọi là sự song song giả. Giả sử trong hệ thống có 3 tiến trình sẵn sàng P1, P2, P3 thì quá trình chuyển CPU giữa 3 tiến trình này có thể minh họa như sau: P1 P2 P3 t a P1 P2 P3 t b Hình 2.2: Sự thực hiện đồng thời của các tiến trình trong hệ thống một CPU (a) và hệ thống nhiều CPU (b). 24 Thời điểm Trạng thái các tiến trình t1 P1: được cấp CPU t2 P1: bị thu hồi CPU (khi chưa kết thúc) P3: được cấp CPU t3 P3: bị thu hồi CPU (khi chưa kết thúc) P1: được cấp CPU t4 P1: kết thúc và trả lại CPU P2: được cấp CPU t5 P2: kết thúc và trả lại CPU P3: được cấp CPU t6 P3: kết thúc và trả lại CPU Hình 2.3 minh họa quá trình thực hiện của 3 tiến trình P1, P2, P3 ở trên: Chúng ta đều biết, chức năng cở bản của CPU là thực hiện các chỉ thị máy (machine instrustion) thường trú trong bộ nhớ chính, các chỉ thị này được cung cấp từ một chương trình, chương trình bao gồm một dãy tuần tự các chỉ thị. Và theo trên, tiến trình là một bộ phận của chương trình, nó cũng sở hữu một tập lệnh trong bộ nhớ chính, một con trỏ lệnh,… Nên xét về bản chất, thì việc chuyển CPU từ tiến trình này sang tiến trình khác thực chất là việc điều khển CPU để nó thực hiện xen kẽ các chỉ thị bên trong tiến trình. Điều này có thể thực hiện dễ dàng bằng cách thay đổi hợp lý giá trị của con trỏ lệnh, đó chính là cặp thanh ghi CS:IP trong các CPU thuộc kiến trúc Intel, để con trỏ lệnh chỉ đến các chỉ thị cần thực hiện trong các tiến trình. Để thấy rõ hơn điều này ta hãy xem ví dụ sau đây: Giả sử hệ thống cần thực hiện đồng thời 3 tiến trình P1, P2, P3, bắt đầu từ tiến trình P1. Các chỉ thị của các tiến trình này được nạp vào bộ nhớ tại các địa chỉ như sau: Tiến trình P1: Tiến trình P2: Tiến trình P3: a + 0 b + 0 c + 0 a + 1 b + 2 c + 1 a + 3 b + 3 c + 4 a + 5 c + 6 Trong đó: a: là địa chỉ bắt đầu của chương trình của tiến trình P1. b: là địa chỉ bắt đầu của chương trình của tiến trình P2. c: là địa chỉ bắt đầu của chương trình của tiến trình P3. Thì giá trị của con trỏ lệnh, chính xác là giá trị cặp thanh ghi CS:IP, lần lượt là: a + 0, b + 0, c + 0, a + 1, b + 2, c + 1, a + 3, b + 3, c + 4, a + 5, c + 6. Tức là, CPU thực hiện xen kẽ các chỉ thị của 3 tiến trình P1, P2, P3 từ lệnh đầu tiên đến lệnh cuối cùng, cho đến khi tất cả các chỉ thị của 3 tiến trình đều được thực hiện. Nhưng khoảng thời gian từ khi con trỏ lệnh = a + 0 đến khi = a + 1, hay từ khi = b + 0 đến khi = b + 2, … là rất nhỏ, nên hệ thống có “cảm giác” 3 tiến trình P1, P2, P3 hoạt động đồng thời với nhau. P1 P2 P3 t Hình 2.3: Sự hoạt động “song song” của các tiến trình P1, P2, P3. t1 t2 t3 t4 t5 t6 25 Ví dụ trên đây cho ta thấy bản chất của việc thực hiện song song (hay đồng thời) các tiến trình trên các hệ thống một CPU. Rõ ràng với mô hình tiến trình hệ thống có được 2 điều lợi: Tiết kiệm được bộ nhớ: vì không phải nạp tất cả chương trình vào bộ nhớ mà chỉ nạp các tiến trình cần thiết nhất, sau đó tùy theo yêu cầu mà có thể nạp tiếp các tiến trình khác. Cho phép các chương trình hoạt động song song nên tốc độ xử lý của toàn hệ thống tăng lên và khai thác tối đa thời gian xử lý của CPU. Việc chọn thời điểm dừng của tiến trình đang hoạt động (đang chiến giữ CPU) để thu hồi CPU chuyển cho tiến trình khác hay việc chọn tiến trình tiếp theo nào trong số các tiến trình đang ở trạng thái sẵn sàng để cấp CPU là những vấn đề khá phức tạp đòi hỏi hệ điều hành phải có một cơ chế điều phối thích hợp thì mới có thể tạo ra được hiệu ứng song song giả và sử dụng tối ưu thời gian xử lý của CPU. 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 (dispatcher) tiến trình. 2.1.3. Khối quản lý tiến trình (Proccess Control Block – PCB) Để quản lý các tiến trình và tài nguyên trong hệ thống, hệ điều hành phải có các thông tin về trạng thái hiện thời của mỗi tiến trình và tài nguyên. Trong trường hợp này hệ điều hành xây dựng và duy trì các bảng thông tin về mỗi đối tượng (memory, devices, file, process) mà nó quản lý, đó là các bảng: memory table cho đối tượng bộ nhớ, I/O table cho đối tượng thiết bị vào/ra, file table cho đối tượng tập tin, process table cho đối tượng tiến trình. Memory table được sử dụng để theo dõi cả bộ nhớ thực lẫn bộ nhớ ảo, nó phải bao gồm các thông tin sau: Không gian bộ nhớ chính dành cho tiến trình. Không gian bộ nhớ phụ dành cho tiến trình. Các thuộc tính bảo vệ bộ nhớ chính và bộ nhớ ảo. Các thông tin cần thiết để quản lý bộ nhớ ảo. Ở đây chúng tôi điểm qua một vài thông tin về memory table, là để lưu ý với các bạn rằng: nhiệm vụ quản lý tiến trình và quản lý bộ nhớ của hệ điều hành có quan hệ chéo với nhau, bộ phận quản lý tiến trình cần phải có các thông tin về bộ nhớ để điều khiển sự hoạt động của tiến trình, ngược lại bộ phận quản lý bộ nhớ phải có các thông tin về tiến trình để tổ chức nạp tiến trình vào bộ nhớ, … Điều này cũng đúng với các bộ phận quản lý vào/ ra và quản lý tập tin. Trong phần trình bày sau đây chúng tôi chỉ đề cập đến Process Table của hệ điều hành. Để quản lý và điều khiển được một tiến trình, thì hệ điều hành phải biết được vị trí nạp tiến trình trong bộ nhớ chính, phải biết được các thuộc tính của tiến trình cần thiết cho việc quản lý tiến trình của nó: Định vị của tiến trình (process location): định vị của tiến trình phụ thuộc vào chiến lược quản lý bộ nhớ đang sử dụng. Trong trường hợp đơn giản nhất, tiến trình, hay chính xác hơn là hình ảnh tiến trình, được lưu giữa tại các khối nhớ liên tục trên bộ nhớ phụ (thường là đĩa), để tiến trình thực hiện được thì tiến trình phải được nạp vào bộ nhớ chính. Do đó, hệ điều hành cần phải biết định vị của mỗi tiến trình trên đĩa và cho mỗi tiến trình đó trên bộ nhớ chính. Trong một số chiến lược quản lý bộ nhớ, hệ điều hành chỉ cần nạp một phần tiến trình vào bộ nhớ chính, phần còn lại vẫn nằm trên đĩa. Hay tiến trình đang ở trên bộ nhớ chính thì có một phần bị swap-out ra lại đĩa, phần còn lại vẫn còn nằm ở bộ nhớ chính. Trong các trường hợp này hệ điều hành phải theo dõi tiến trình để biết phần nào của tiến trình là đang ở trong bộ nhớ chính, phần nào của tiến trình là còn ở trên đĩa. Đa số các hệ điều hành hiện nay đều sử dụng chiến lược quản lý bộ nhớ mà trong đó 26 không gian địa chỉ của tiến trình là một tập các block, các block này có thể không liên tiếp nhau. Tùy theo chiến lược bộ nhớ sử dụng mà các block này có thể có chiều dài cố định (chiến lược phân phân trang bộ nhớ) hay thay đổi (chiến lược phân đoạn bộ nhớ) hay kết hợp cả hai. Hệ điều hành cho phép không nạp tất cả các trang (page) và/hoặc các đoạn (segment) của tiến trình vào bộ nhớ. Do đó, process table phải được duy trì bởi hệ điều hành và phải cho biết vị trí của mỗi trang/ đoạn tiến trình trên hệ thống. Những điều trên đây sẽ được làm rõ ở phần chiến lược cấp phát bộ nhớ trong chương Quản lý bộ nhớ của giáo trình này. Các thuộc tính của tiến trình: Trong các hệ thống đa chương, thông tin về mỗi tiến trình là rất cần cho công tác quản lý tiến trình của hệ điều hành, các thông tin này có thể thường trú trong khối quản lý tiến trình. Các hệ điều hành khác nhau sẽ có cách tổ chức PCB khác nhau, ở đây chúng ta khảo sát một trường hợp chung nhất. Các thông tin trong PCB có thể được chia thành ba nhóm chính: Hình 2.4. Ví dụ một PCB. Định danh tiến trình (process number, process identification - PID): mỗi tiến trình được gán một định danh duy nhất để phân biệt với các tiến trình khác trong hệ thống. Định danh của tiến trình có thể xuất hiện trong memory table, I/O table. Khi tiến trình này truyền thông với tiến trình khác thì định danh tiến trình được sử dụng để hệ điều hành xác định tiến trình đích. Khi tiến trình cho phép tạo ra tiến trình khác thì định danh được sử dụng để chỉ đến tiến trình cha và tiến trình con của mỗi tiến trình. Tóm lại, các định danh có thể lưu trữ trong PCB bao gồm: định danh của tiến trình này, định danh của tiến trình tạo ra tiến trình này, định danh của người sử dụng. Thông tin trạng thái CPU (CPU state information): bao gồm các thanh ghi User-visible, các thanh ghi trạng thái và điều khiển, các con trỏ stack. Thông tin điều khiển tiến trình (process control information): bao gồm thông tin trạng thái và lập lịch, cấu trúc dữ liệu, truyền thông liên tiến trình, quyền truy cập tiến trình, quản lý bộ nhớ, tài nguyên khởi tạo và tài nguyên sinh ra. PCB là một trong những cấu trúc dữ liệu trung tâm và quan trọng của hệ điều hành. Mỗi PCB chứa tất cả các thông tin về tiến trình mà nó rất cần cho hệ điều hành. Có nhiều modun thành phần trong hệ điều hành có thể read và/hoặc modified PCB như: lập lịch tiến trình, cấp phát tài nguyên cho tiến trình, ngắt tiến trình, v.v… Có thể nói các thiết lập trong PCB định 27 nghĩa trạng thái của hệ điều hành. 2.2. Các trạng thái tiến trình Từ khi được đưa vào hệ thống cho đến khi kết thúc tiến trình tồn tại ở các trạng thái khác nhau. 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ời của tiến trình tại thời điểm đó. Tiến trình có thể ở một trong các trạng thái sau: Tạo mới (new): tiến trình vừa được tạo ra. Sẵn sàng (ready ): Ngay sau khi khởi tạo tiến trình, đưa tiến trình vào hệ thống và cấp phát đầy đủ tài nguyên (trừ CPU) cho tiến trình, hệ điều hành đưa tiến trình vào trạng thái ready. Hay nói cách khác, trạng thái ready là trạng thái của một tiến trình trong hệ thống đang chờ được cấp CPU để bắt đầu thực hiện. Đang thực thi (running): Là trạng thái mà tiến trình đang được sở hữu CPU để hoạt động, hay nói cách khác là các chỉ thị của tiến trình đang được thực hiện/ xử lý bởi CPU. Chờ (waitng): Là trạng thái mà tiến trình đang chờ để được cấp phát thêm tài nguyên, để một sự kiện nào đó xảy ra, hay một quá trình vào/ra kết thúc. Kết thúc (terminated): Hoàn tất việc thực thi. Quá trình chuyển trạng thái của các tiến trình trong được mô tả bởi sơ đồ sau: Hình 2.5. Sơ đồ chuyển trạng thái của tiến trình. Trong đó: Admitted: Tiến trình được khởi tạo, được đưa vào hệ thống, được cấp phát đầy đủ tài nguyên chỉ thiếu CPU. Scheduler dispatch: Tiến trình được cấp CPU để bắt đầu thực hiện/ xử lý. Exit: Tiến trình hoàn thành xử lý và kết thúc. Interrupt: Tiến trình bị bộ điều phối tiến trình thu hồi CPU, do hết thời gian được quyền sử dụng CPU, để cấp phát cho tiến trình khác. I/O or event wait: Tiến trình đang chờ một sự kiện nào đó xảy ra hay đang chờ một thao vào/ra kết thúc hay tài nguyên mà tiến trình yêu cầu chưa được hệ điều hành đáp ứng. I/O or event completion: Sự kiện mà tiến trình chờ đã xảy ra, thao tác vào/ra mà tiến trình đợi đã kết thúc, hay tài nguyên mà tiến trình yêu cầu đã được hệ điều hành đáp ứng, 28 Bộ phận điều phối tiến trình thu hồi CPU từ một tiến trình đang thực hiện trong các trường hợp sau: - Tiến trình đang thực hiện hết thời gian (time-out) được quyền sử dụng CPU mà bộ phận điều phối dành cho nó. - Có một tiến trình mới phát sinh và tiến trình mới này có độ ưu tiên cao hơn tiến trình hiện tại. - Có một tiến trình mới phát sinh và tiến trình này mới cần một khoảng thời gian của CPU nhỏ hơn nhiều so với khoảng thời gian còn lại mà tiến trình hiện tại cần CPU. Tại một thời điểm xác định trong hệ thống có thể có nhiều tiến trình đang ở trạng thái ready hoặc waiting nhưng chỉ có một tiến trình ở trạng thái running. Các tiến trình ở trạng thái ready và waiting được chứa trong các hàng đợi (queue) riêng. Có nhiều lý do để một tiến trình đang ở trạng thái running chuyển sang trạng thái waiting, do đó đa số các hệ điều hành đều thiết kế một hệ thống hàng đợi gồm nhiều hàng đợi, mỗi hành đợi dùng để chứa những tiến trình đang đợi cùng một sự kiện nào đó. 2.3. Các thao tác điều khiển tiến trình 2.3.1. Khởi tạo tiến trình Khi tạo mới tiến trình hệ điều hành thực hiện các thao tác sau: - Hệ điều hành gán PID cho tiến trình mới và đưa tiến trình vào danh sách quản lý của hệ thống, tức là, dùng một entry trong PCB để chứa các thông tin liên quan đến tiến trình mới tạo ra này. - Cấp phát không gian bộ nhớ cho tiến trình. Ở đây hệ điều hành cần phải xác định được kích thước của tiến trình, bao gồm code, data và stack. Giá trị kích thước này có thể được gán mặt định dựa theo loại của tiến trình hoặc được gán theo yêu cầu của người sử dụng khi có một công việc (job) được tạo. Nếu một tiến trình được sinh ra bởi một tiến trình khác, thì tiến trình cha có thể chuyển kích thước của nó đến hệ điều hành trong yêu cầu tạo tiến trình. - Khởi tạo các thông tin cần thiết cho khối điều khiển tiến trình như các PID của tiến trình cha (nếu có), thông tin trạng thái tiến trình, độ ưu tiên của tiến trình, thông tin ngữ cảnh của CPU (bộ đến chương trình và các thanh ghi khác), vv. - Cung cấp đầy đủ các tài nguyên cần thiết nhất, trừ CPU, để tiến trình có thể vào trạng thái ready được hoặc bắt đầu hoạt động được. - Đưa tiến trình vào một danh sách tiến trình nào đó: ready list, suspend list, waiting list, vv, sao cho phù hợp với chiến lược điều phối tiến trình hiện tại của bộ phận điều phối tiến trình của hệ điều hành. Khi một tiến trình tạo lập một tiến trình con, tiến trình con có thể được cấp phát tài nguyên bởi chính hệ điều hành, hoặc được tiến trình cha cho thừa hưởng một số tài nguyên ban đầu của nó. 2.3.2. Kết thúc tiến trình Khi tiến trình kết thúc xử lý, hoàn thành chỉ thị cuối cùng, hệ điều hành sẽ thực hiện các thao tác sau đây: 29 - Thu hồi tài nguyên đã cấp phát cho tiến trình. - Loại bỏ tiến trình ra khỏi danh sách quản lý của hệ thống. - Huỷ bỏ khối điều khiển tiến trình. Hầu hết các hệ điều hành đều không cho phép tiến trình con hoạt động khi tiến trình cha đã kết thúc. Trong những trường hợp như thế hệ điều hành sẽ chủ động việc kết thúc tiến trình con khi tiến trình cha vừa kết thúc. 2.3.3. Chuyển ngữ cảnh (context switch) Muốn chuyển quyền sử dụng CPU giữa các tiến trình thì phải thay đổi tiến trình của các tiến trình. Khi một tiến trình đang ở trạng thái running bị chuyển sang trạng thái khác (ready, waiting,…) thì hệ điều hành phải tạo ra sự thay đổi trong môi trường làm việc của nó, quá trình này được gọi là chuyển ngữ cảnh. Sau đây là các bước mà hệ điều hành phải thực hiện đầy đủ khi thay đổi trạng thái tiến trình: Hình 2.6. Sơ đồ chuyển ngữ cảnh. - Lưu (save) ngữ cảnh của CPU, bao gồm thanh ghi bộ đếm chương trình (PC: program counter) và các thanh ghi khác. - Cập nhật PCB của tiến trình, sao cho phù hợp với trạng thái mới của tiến trình, bao gồm trạng thái mới của tiến trình, các thông tin tính toán, vv. - Di chuyển PCB của tiến trình đến một hàng đợi thích hợp, đế đáp ứng được các yêu cầu của công tác điều phối tiến trình. - Chọn một tiến trình khác để cho phép nó thực hiện. - Cập nhật PCB của tiến trình vừa được chọn thực hiện ở trên, chủ yếu là thay đổi trạng thái của tiến trình đến trạng thái running. 30 - Cập nhật các thông tin liên quan đến quản lý bộ nhớ. Bước này phụ thuộc vào các yêu cầu chuyển đổi địa chỉ bộ nhớ đang được sử dụng. - Khôi phục (Restore) lại ngữ cảnh của CPU và thay đổi giá trị của bộ đếm chương trình và các thanh ghi khác sao cho phù hợp với tiến trình được chọn ở trên, để tiến trình này có thể bắt đầu hoạt động được. Như vậy, khi hệ điều hành chuyển một tiến trình từ trạng thái running (đang chạy) sang một trạng thái nào đó (tạm dừng) thì hệ điều hành phải lưu trữ các thông tin cần thiết, nhất là Program Count, để sau này hệ điều hành có thể cho tiến trình tiếp tục hoạt động trở (tái kích hoạt) lại được. Đồng thời hệ điều hành phải chọn một tiến trình nào đó đang ở trạng thái ready để cho tiến trình này chạy (chuyển tiến trình sang trạng thái running). Tại đây, trong các thao tác phải thực hiện, hệ điều hành phải thực hiện việc thay đổi giá trị của PC, thay đổi ngữ cảnh CPU, để PC chỉ đến địa chỉ của chỉ thị đầu tiên của tiến trình running mới này trong bộ nhớ. Đây cũng chính là bản chất của việc thực hiện các tiến trình trong các hệ thống một CPU. 2.4. Giao tiếp giữa các tiến trình 2.4.1. Cộng tác giữa các tiến trình Các tiến trình thực thi trong hệ điều hành có thể là những tiến trình độc lập hay những tiến trình cộng tác. Một tiến trình là độc lập (independent) nếu nó không thể ảnh hưởng hay bị ảnh hưởng bởi các tiến trình khác thực thi trong hệ thống. Rõ ràng, bất kỳ một tiến trình không chia sẻ bất cứ dữ liệu (tạm thời hay cố định) với tiến trình khác là độc lập. Ngược lại, một tiến trình là cộng tác (cooperating) nếu nó có thể ảnh hưởng hay bị ảnh hưởng bởi các tiến trình khác trong hệ thống. Hay nói cách khác, bất cứ tiến trình chia sẻ dữ liệu với tiến trình khác là tiến trình cộng tác. Chúng ta có thể cung cấp một môi trường cho phép cộng tác tiến trình với nhiều lý do: - Chia sẻ thông tin: vì nhiều người dùng có thể quan tâm cùng phần thông tin (thí dụ, tập tin chia sẻ), chúng phải cung cấp một môi trường cho phép truy xuất đồng hành tới những loại tài nguyên này. - Gia tăng tốc độ tính toán: nếu chúng ta muốn một tác vụ chạy nhanh hơn, chúng ta phải chia nó thành những tác vụ nhỏ hơn, mỗi tác vụ sẽ thực thi song song với các tác vụ khác. Việc tăng tốc như thế có thể đạt được chỉ nếu máy tính có nhiều thành phần đa xử lý (như các CPU hay các kênh I/O). - Tính module hóa: chúng ta muốn xây dựng hệ thống trong một kiểu mẫu dạng module, chia các chức năng hệ thống thành những tiến trình hay luồng như đã thảo luận ở chương trước. - Tính tiện dụng: Thậm chí một người dùng đơn có thể có nhiều tác vụ thực hiện tại cùng thời điểm. Thí dụ, một người dùng có thể đang soạn thảo, in, và biên dịch cùng một lúc. 2.4.2. Giao tiếp giữa các tiến trình Thực thi đồng hành của các tiến trình cộng tác yêu cầu các cơ chế cho phép các tiến trình giao tiếp với các tiến trình khác và đồng bộ hóa các hoạt động của chúng. Đó chính là cơ chế giao tiếp giữa các tiến trình (Interprocess communication - IPC). Sau đây chúng ta sẽ tìm hai mô hình giao tiếp giữa các tiến trình là chia sẻ bộ nhớ và 31 truyền thông điệp. Hình 2.7. Mô hình giao tiếp giữa các tiến trình. (a) truyền thông điệp. (b) chia sẻ bộ nhớ 2.4.3. Mô hình giao tiếp qua chia sẻ bộ nhớ Để minh họa khái niệm của các quá trình cộng tác, chúng ta xem xét bài toán người sản xuất - người tiêu thụ, là mô hình chung cho các tiến trình cộng tác. Tiến trình người sản xuất cung cấp thông tin được tiêu thụ bởi tiến trình người tiêu thụ. Thí dụ, một chương trình in sản xuất các ký tự được tiêu thụ bởi trình điều khiển máy in. Một trình biên dịch có thể sản xuất mã hợp ngữ được tiêu thụ bởi trình hợp ngữ. Sau đó, trình hợp ngữ có sản xuất module đối tượng, được tiêu thụ bởi bộ nạp. Để cho phép người sản xuất và người tiêu thụ chạy đồng hành, chúng ta phải có sẵn một vùng đệm chứa các sản phẩm có thể được điền vào bởi người sản xuất và được lấy đi bởi người tiêu thụ. Người sản xuất có thể sản xuất một sản phẩm trong khi người tiêu thụ đang tiêu thụ một sản phẩm khác. Người sản xuất và người tiêu thụ phải được đồng bộ để mà người tiêu thụ không cố gắng tiêu thụ một sản phẩm mà chưa được sản xuất. Trong trường hợp này, người tiêu thụ phải chờ cho tới khi các sản phẩm mới được tạo ra. Nếu độ lớn vùng đệm không bị giới hạn (unbounded-buffer), người tiêu thụ có thể phải chờ các sản phẩm mới nhưng người sản xuất có thể luôn tạo ra sản phẩm mới. Nhưng với vùng đệm có kích thước giới hạn (bounded-buffer), người tiêu thụ phải chờ nếu vùng đệm rỗng, và người sản xuất phải chờ nếu vùng đệm đầy. Vùng đệm có thể được cung cấp bởi hệ điều hành thông qua việc sử dụng bộ nhớ được chia sẻ. Chúng ta xem xét một giải pháp chia sẻ bộ nhớ đối với vấn đề vùng đệm bị giới hạn (bounded-buffer). Tiến trình người sản xuất và người tiêu thụ chia sẻ các biến sau: #define BUFFER_SIZE 10 typedef struct{ … } item; 32 item buffer[BUFFER_SIZE]; int in = 0; int out = 0; Vùng đệm được chia sẻ được cài đặt như một mảng vòng với hai con trỏ logic: in và out. Biến in chỉ tới vị trí trống kế tiếp trong vùng đệm; out chỉ tới vị trí đầy đầu tiên trong vùng đệm. Vùng đệm là rỗng khi in==out; vùng đệm là đầy khi ((in + 1)%BUFFER_SIZE) ==out. Mã cho tiến trình người sản xuất và người tiêu thụ được trình bày dưới đây. Tiến trình người sản xuất có một biến nextProduced trong đó sản phẩm mới được tạo ra và được lưu trữ: while (1) { /*produce an item in nextProduced*/ while (((in + 1)%BUFFER_SIZE) ==out) ; /*do nothing*/ buffer[in] = nextProduced; in = (in + 1) % BUFFER_SIZE; } Tiến trình người tiêu thụ có biến cục bộ nextConsumed trong đó sản phẩm được tiêu thụ: while (1){ while (in==out) ; //nothing nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; /*consume the item in nextConsumed*/ } 2.4.3. Mô hình giao tiếp qua truyền thông điệp Giao tiếp giữa các tiến trình thông qua việc gửi thông điệp. Để hỗ trợ cơ chế liên lạc bằng thông điệp, hệ điều hành cung cấp các hàm IPC chuẩn, cơ bản là hai hàm: Send(message), gửi một thông điệp và Receive(message), nhận một thông điệp. a. Đặt tên Các tiến trình muốn giao tiếp phải có cách tham chiếu với nhau. Chúng có thể dùng giao tiếp trực tiếp hay gián tiếp. Với giao tiếp trực tiếp, mỗi tiến trình muốn giao tiếp phải đặt tên rõ ràng người gửi và người nhận của giao tiếp. Trong cơ chế này, các hàm cơ sở send và receive được định nghĩa như sau: 33 Send(P, message): gửi một thông điệp tới tiến trình P Receive(Q, message): nhận một thông điệp từ tiến trình Q Một liên kết giao tiếp trong cơ chế này có những thuộc tính sau: - Một liên kết được thiết lập tự động giữa mỗi cặp tiến trình muốn giao tiếp. - Các tiến trình cần biết định danh của nhau khi giao tiếp. - Một liên kết được nối kết với chính xác hai tiến trình. - Chính xác một liên kết tồn tại giữa mỗi cặp tiến trình. Với giao tiếp gián tiếp, một thông điệp được gửi tới và nhận từ các hộp thư (mailboxes), hay cổng (ports). Một hộp thư có thể được hiển thị trừu tượng như một đối tượng trong đó các thông điệp có thể được đặt bởi các tiến trình và sau đó các thông điệp này có thể được xóa đi. Mỗi hộp thư có một định danh duy nhất. Trong cơ chế này, một tiến trình có thể giao tiếp với một vài tiến trình khác bằng một số hộp thư khác nhau. Hai tiến trình có thể giao tiếp chỉ nếu chúng chia sẻ cùng một hộp thư. Hàm cơ sở send và receive được định nghĩa như sau: Send(A, message): gửi một thông điệp tới hộp thư A. Receive(A, message): nhận một thông điệp từ hộp thư A. Trong cơ chế này, một liên kết giao tiếp có các thuộc tính sau: - Một liên kết được thiết lập giữa một cặp tiến trình chỉ nếu cả hai thành viên của cặp có một hộp thư được chia sẻ. - Một liên kết có thể được nối kết với nhiều hơn hai tiến trình. - Số các liên kết khác nhau có thể tồn tại giữa mỗi cặp tiến trình giao tiếp với mỗi liên kết tương ứng với một hộp thư. b. Đồng bộ hóa Giao tiếp giữa hai quá trình xảy ra bởi lời gọi hàm cơ sở send và receive. Có các tùy chọn thiết kế khác nhau cho việc cài đặt mỗi hàm cơ sở. Truyền thông điệp có thể là nghẽn (block) hay không nghẽn (nonblocking)-cũng được xem như đồng bộ và bất đồng bộ. Hàm send nghẽn: quá trình gửi bị nghẽn cho đến khi thông điệp được nhận bởi quá trình nhận hay bởi hộp thư. Hàm send không nghẽn: quá trình gửi gửi thông điệp và thực hiện tiếp hoạt động. Hàm receive nghẽn: người nhận nghẽn cho đến khi thông điệp sẵn sàng. Hàm receive không nghẽn: người nhận nhận lại một thông điệp hợp lệ hay rỗng. Sự kết hợp khác nhau giữa send và receive là có thể. Khi cả hai hàm send và receive là nghẽn chúng ta có sự thống nhất giữa người gửi và người nhận. c. Tạo vùng đệm Dù giao tiếp có thể là trực tiếp hay gián tiếp, các thông điệp được chuyển đổi bởi các quá trình giao tiếp thường trú trong một hàng đợt tạm thời. Về cơ bản, một hàng đợi như thế có thể được cài đặt trong ba cách: 34 - Khả năng chứa là 0 (zero capacity): hàng đợi có chiều dài tối đa là 0; do đó liên kết không thể có bất kỳ thông điệp nào chờ trong nó. Trong trường hợp này, người gửi phải nghẽn cho tới khi người nhận nhận thông điệp. - Khả năng chứa có giới hạn (bounded capacity): hàng đợi có chiều dài giới hạn n; do đó, nhiều nhất n thông điệp có thể thường trú trong nó. Nếu hàng đợi không đầy khi một thông điệp mới được gửi, sau đó nó được đặt trong hàng đợi (thông điệp được chép hay một con trỏ thông điệp được giữ) và người gửi có thể tiếp tục thực thi không phải chờ. Tuy nhiên, liên kết có khả năng chứa giới hạn. Nếu một liên kết đầy, người gửi phải nghẽn cho tới khi không gian là sẳn dùng trong hàng đợi - Khả năng chứa không giới hạn (unbounded capacity): Hàng đợi có khả năng có chiều dài không giới hạn; do đó số lượng thông điệp bất kỳ có thể chờ trong nó. Người gửi không bao giờ nghẽn. 2.5. Luồng (Thread) Trong các hệ điều hành cũ, mỗi tiến trình có một không gian địa chỉ riêng và chỉ một dòng chạy chương trình (tuần tự chạy các lệnh từ lệnh đầu tiên). Tuy nhiên trong nhiều trường hợp công việc cần có nhiều dòng chạy chương trình chạy “song song” trong cùng một không gian địa chỉ. 2.5.1 Khái niệm luồng Mô hình tiến trình dựa trên hai điều: gom nhóm tài nguyên và chạy chương trình. Gom nhóm tài nguyên là gom các tài nguyên có liên quan đến một công việc lại với nhau như không gian địa chỉ để nạp chương trình xử lý công việc, các tập tin được mở liên quan đến công việc … Chạy chương trình là chạy các mã để thực hiện công việc, đó là dòng chạy chương trình. Dòng chạy chương trình có bộ đếm chương trình, thanh ghi, ngăn xếp, … riêng. Trong một tiến trình có thể có nhiều dòng chạy chương trình đồng thời, mỗi dòng chạy chương trình được gọi là luồng. Các luồng của một tiến trình sẽ dùng chung môi trường của tiến trình, tức là dùng chung không gian địa chỉ, các tập tin được mở,… của tiến trình. Hình 2.8. Luồng. Tiến trình đa luồng (multithreaded process) là tiến trình có nhiều luồng. Hình (a): Có ba tiến trình, mỗi tiến trình chỉ có một dòng chạy chương trình và có 35 không gian địa chỉ riêng. Hình (b) có một tiến trình, trong tiến trình có ba luồng. Ba luồng này có cùng không gian địa chỉ, tài nguyên của tiến trình. Trong tiến trình đa luồng, các luồng chạy được luân phiên chạy (giống như sự luân phiên chạy của các tiến trình). Trong khi tiến trình đang chạy, CPU sẽ lần lượt phục vụ cho các luồng trong tiến trình này. Các luồng trong một tiến trình thì không hoàn toàn độc lập nhau như giữa các tiến trình. Các luồng trong một tiến trình dùng chung không gian địa chỉ, nghĩa là các luồng có thể dùng chung các biến toàn cục, luồng có thể truy xuất đến bất cứ địa chỉ nào trong không gian địa chỉ của tiến trình, luồng có thể đọc, ghi lên cả ngăn xếp của luồng khác trong cùng tiến trình. Không có cơ chế bảo vệ giữa các luồng vì không thể làm được, và không cần thiết vì các luồng trong một tiến trình cùng phối hợp nhau để thực hiện một công việc nào đó (trách nhiệm thuộc về người lập trình), điều này khác với tiến trình vì các tiến trình là những chương trình thực hiện các công việc khác nhau (của những người viết khác nhau). 2.5.2. Cài đặt luồng a. Cài đặt luồng mức người dùng Có hai hướng chính cài đặt luồng: trong mức người dùng và trong kernel. Khi cài đặt luồng trong mức người dùng thì kernel không biết gì về luồng, kernel chỉ biết tiến trình (có một luồng). Thuận lợi khi cài đặt luồng ở mức người dùng là luồng có thể cài đặt luồng trong những hệ điều hành không hỗ trợ luồng. Mỗi tiến trình có riêng bảng luồng để lưu trữ thông tin về các luồng trong tiến trình đó. Bảng luồng tương tự như bảng tiến trình của nhân, lưu giữ bộ đếm chương trình của luồng, con trỏ ngăn xếp, thanh ghi, trạng thái luồng … Khi luồng chuyển về trạng thái sẵn sàng chạy hoặc bị treo thì các thông tin của luồng sẽ được lưu lại. Hình 2.9. Luồng ở mức người dùng. Khi một luồng thực hiện một lệnh có thể dẫn đến bị treo (như đợi một luồng khác), nó gọi thủ tục hệ thống đang chạy (run-time), thủ tục này lưu các thanh ghi vào bảng luồng và bắt đầu cho luồng khác chạy. Trong tiến trình có một hệ thống đang chạy (run-time system). 36 Khi một luồng ngưng chạy như gọi hàm luồng_yeild, hàm này sẽ lưu các thông tin của luồng vào trong bảng luồng, sau đó gọi bộ lập lịch luồng để cho luồng khác chạy. Vì bộ lập lịch luồng là thủ tục cục bộ, không phải gọi hàm hệ thống nên thực hiện nhanh. Mỗi tiến trình có thể có giải thuật lập lịch luồng riêng. Tuy nhiên cài đặt luồng trong mức người dùng có những điểm hạn chế sau: Cài đặt các hàm gọi hệ thống blocking (blocking system call) ? Khi một luồng thực hiện gọi hệ thống (blocking) thì tất cả các luồng trong tiến trình đều bị ngưng. Điều này dẫn đến phải có các hàm gọi hệ thống không blocking (nonblocking system call), và làm ảnh hưởng rất nhiều đến các chương trình khác. Khi một luồng chạy thì các luồng trong tiến trình đó không có cơ hội để chạy trừ khi luồng đang chạy tự nguyện nhường CPU vì trong một tiến trình không có ngắt thời gian nên bộ lập lịch luồng không thể chạy được. b. Cài đặt luồng ở mức nhân Trong tiến trình không có bảng luồng, kernel sẽ có bảng luồng lưu giữ thông tin của tất cả các luồng trong hệ thống. Khi một luồng được tạo hay hủy bỏ thì gọi hàm hệ thống, do vậy thời gian thực hiện chậm. Khi một luồng thực hiện gọi hệ thống, trong trường hợp blocking thì luồng này sẽ bị treo, và kernel sẽ cho luồng khác trong cùng tiến trình chạy nếu có, hoặc luồng của tiến trình khác chạy. Do vậy các luồng không cần thiết phải có những hàm gọi thống không blocking. Hình 2.10. Luồng ở mức nhân c. Kết hợp Kết hợp cách cài đặt luồng trong mức người dùng và mức nhân. Luồng có thể được cài dặt theo một trong các mô hình sau: Mô hình many-to-one Nhiều luồng mức người dùng “chia sẻ” một luồng mức nhân để thực thi. Việc quản lý luồng được thực hiện thông qua các hàm của một thư viện luồng được gọi ở mức người dùng. Khi một luồng bị treo thì luồng mức nhân cũng bị treo, do đó mọi luồng khác của tiến trình cũng sẽ bị treo. 37 Hình 2.11. Mô hình many-to-one. Có thể được hiện thực đối với hầu hết các hệ điều hành. Mô hình one-to-one Hình 2.12. Mô hình one-to-one. Mỗi luồng ở mức người dùng thực thi thông qua một luồng ở mức nhân riêng của nó. Mỗi khi một luồng ở mức người được tạo ra thì cũng cần tạo một luồng ở mức nhân tương ứng. Hệ điều hành phải có cơ chế cung cấp được nhiều luồng ở mức nhân cho một tiến trình. Mô hình many-to-many Hình 2.11. Mô hình many-to-many. 38 Nhiều ở mức người dùng được phân chia thực thi (multiplexed) trên một số luồng ở mức nhân. Tránh được một số khuyết điểm của hai mô hình many-to-one và one-to-one CÂU HỎI ÔN TẬP 1. Phân biệt chương trình với tiến trình. 2. Nêu các trạng thái của tiến trình. 3. Xác định các thành phần chính của khối điều kiển tiến trình. 4. Tại sao phải giao tiếp giữa các tiến trình. Nêu các cơ chế giao tiếp giữa các tiến trình. 5. Trình bày các mô hình luồng. TÀI LIỆU THAM KHẢO 1. Hồ Đắc Phương. Giáo trình nguyên lý hệ điều hành. Nhà xuất bản Giáo dục, 2009. Chương 4. 2. A.Silberschatz, P. B. Galvin, G. Gagne. Operating System Concepts, 8th Edition. John Wiley & Sons, 2009. Chương 3,4. 3. William Stallings. Operating Systems: Internals and Design Principles (6th Edition). Pearson Prentice Hall, 2009. Chương 3,4. 39 CHƯƠNG 3. LẬP LỊCH CPU 3.1. Các khái niệm cơ bản 3.1.1. Chu kỳ CPU-I/O Sự thành công của việc lập lịch CPU phụ thuộc vào thuộc tính được xem xét sau đây của quá trình. Việc thực thi quá trình chứa một chu kỳ (cycle) thực thi CPU và chờ đợi vào/ra. Các quá trình chuyển đổi giữa hai trạng thái này. Sự thực thi quá trình bắt đầu với một chu kỳ CPU (CPU burst), theo sau bởi một chu kỳ vào/ra (I/O burst), sau đó một chu kỳ CPU khác, sau đó lại tới một chu kỳ vào/ra khác khác, Sau cùng, chu kỳ CPU cuối cùng sẽ kết thúc với một yêu cầu hệ thống để kết thúc việc thực thi, hơn là với một chu kỳ vào/ra khác, được mô tả như hình 3.1. Một chương trình hướng vào/ra (I/O-bound) thường có nhiều chu kỳ CPU ngắn. Một chương trình hướng xử lý (CPU-bound) có thể có một nhiều chu kỳ CPU dài. Sự phân bổ này có thể giúp chúng ta chọn giải thuật lập lịch CPU hợp lý. Hình 3.1. Sơ đồ thực hiện tiến trình. 3.1.2. Phân loại các hoạt động lập lịch Lập lịch dài hạn (long-term scheduling): xác định tiến trình nào được chấp nhận vào hệ 40 thống. Quyết định độ-đa-lập-trình (degree of multiprogramming) Nếu càng nhiều tiến trình được đưa vào hệ thống thì: khả năng các process bị block có xu hướng giảm; sử dụng CPU hiệu quả hơn; mỗi process được phân chia khoảng thời gian sử dụng CPU thấp hơn. Thường có xu hướng đưa vào một tập lẫn lộn các tiến trình CPU-bound và tiến trình I/O-bound. Hình 3.2. Các hoạt động lập lịch. Lập lịch trung hạn (medium-term scheduling): xác định process nào được đưa vào (swap in), đưa ra khỏi (swap out) bộ nhớ chính. Phụ thuộc vào yêu cầu quản lý việc đa-lập-trình (multiprogramming). Cho phép bộ lập lịch dài hạn chấp nhận nhiều process hơn số lượng process mà có tổng kích thước được chứa vừa trong bộ nhớ chính. Nhưng nếu có quá nhiều process thì sẽ làm tăng việc truy xuất đĩa, do đó cần phải lựa chọn độ-đa-lập-trình cho phù hợp. Được thực hiện bởi phần mềm quản lý bộ nhớ. Lập lịch ngắn hạn (short-term scheduling): xác định process nào được thực thi tiếp theo nên còn được gọi là lập lịch CPU. Được kích hoạt khi có một sự kiện có thể dẫn đến khả năng chọn một process để thực thi:ngắt thời gian, ngắt ngoại vi, lời gọi hệ thống… 3.1.3. Hai thành phần của chiến lược lập lịch a. Hàm lựa chọn (selection function) Xác định process nào trong ready queue sẽ được thực thi tiếp theo. Thường theo các tiêu chí như: tổng thời gian đợi trong hệ thống, thời gian đã được phục vụ, tổng thời gian thực thi của tiến trình. b. Chế độ quyết định (decision mode) Chọn thời điểm hàm lựa chọn lập lịch thực thi. Có hai chế độ: - Nonpreemptive (độc quyền): Một tiến trình sẽ ở trạng thái running cho đến khi nó bị block hoặc nó kết thúc. - Preemptive (không độc quyền): tiến trình đang thực thi có thể bị ngắt và chuyển về 41 trạng thái sẵn sàng nhằm tránh trường hợp một tiến trình độc chiếm CPU. Hàm lập lịch có thể được thực thi khi có tiến trình: (1) chuyển từ trạng thái đang thực thi sang chờ. (2) chuyển từ trạng thái đang thực thi sang sẵn sàng. (3) chuyển từ trạng thái chờ, mới sang sẵn sàng. (4) kết thúc thực thi. Lập lịch nonpreemptive: chỉ thực thi hàm lập lịch trong trường hợp 1 và 4 Lập lịch preemptive: ngoài trường hợp 1 và 4 còn thực thi thêm hàm lập lịch trong trường hợp 2 hoặc 3 (hoặc cả hai). 3.1.4. Bộ điều phối (dispatcher) Bộ điều phối sẽ chuyển quyền điều khiển CPU về cho tiến trình được chọn bởi bộ lập lịch ngắn hạn. Chức năng này thực hiện: - Chuyển ngữ cảnh (sử dụng thông tin ngữ cảnh trong PCB). - Chuyển về chế độ người dùng. - Nhảy đến vị trí thích hợp trong chương trình ứng dụng để khởi động lại chương trình (chính là program counter trong PCB). Thời gian để bộ điều phối dừng một tiến trình này và bắt đầu chạy một tiến trình khác được gọi là thời gian trễ cho việc điều phối (dispatch latency). 3.2. Tiêu chí lập lịch 3.2.1. Tiêu chuẩn lập lịch Để so sánh và đánh giá các giải thuật lập lịch có thể dựa trên các tiêu chuẩn khác nhau. Các tiêu chuẩn bao gồm: Hiệu suất sử dụng CPU (CPU utilization): thời gian vô ích của CPU càng ít càng tốt. Việc sử dụng CPU có thể từ 0 đến 100%. Thông lượng (throughput): là số lượng tiến trình được hoàn thành trên một đơn vị thời gian. gọi là thông lượng Đối với các tiến trình dài, tỉ lệ này có thể là 1 tiến trình trên 1 giờ; đối với các giao dịch ngắn, thông lượng có thể là 10 tiến trình trên giây. Thời gian hoàn thành (turnaround time): Thời gian để một tiến trình hoàn tất, kể từ lúc vào hệ thống đến lúc kết thúc. Thời gian hoàn thành là tổng các thời gian chờ đưa tiến trình vào bộ nhớ, chờ hàng đợi sẳn sàng, thực thi CPU và thực hiện vào/ra. Thời gian chờ (waiting time): là tổng thời gian tiến trình chờ trong hàng đợi sẵn sàng. Thời gian đáp ứng (response time): Thời gian từ lúc có yêu cầu của người dùng đến khi có đáp ứng đầu tiên. 3.2.2. Mục tiêu điều phối: Bộ điều phối tiến trình của hệ điều hành phải đạt được các mục tiêu sau đây trong công tác điều phối: 42 Sự công bằng (Fairness): Các tiến trình đều công bằng với nhau trong việc chia sẻ thời gian xử lý của CPU, không có tiến trình nào phải chờ đợi vô hạn để được cấp CPU. Tính hiệu quả: Tận dụng được 100% thời gian xử lý của CPU. Trong công tác điều phối, khi CPU rỗi bộ điều phối sẽ chuyển ngay nó cho tiến trình khác, nếu trong hệ thống có tiến trình đang ở trạng thái chờ CPU, nên mục tiêu này dễ đạt được. Tuy nhiên, nếu hệ điều hành đưa vào hệ thống quá nhiều tiến trình thiên hướng vào/ra, thì nguy cơ CPU bị rỗi là có thể. Do đó, để đạt được mục tiêu này hệ điều hành phải tính toán và quyết định nên đưa vào hệ thống bao nhiêu tiến trình hướng vào/ra, bao nhiêu tiến trình hướng xử lý, là thích hợp. Thời gian đáp ứng hợp lý: Một tiến trình đáp ứng yêu cầu của người sử dụng, phải nhận được thông tin hồi đáp từ yêu cầu của nó thì nó mới có thể trả lời người sử dụng. Do đó, theo người sử dụng thì bộ phận điều phối phải cực tiểu hoá thời gian đáp ứng của các tiến trình, có như vậy thì tính tương tác của tiến trình mới tăng lên. Thời gian lưu lại trong hệ thống: Phải cực tiểu thời gian hoàn thành nhất là các tác vụ xử lý theo lô. Thông lượng tối đa: Chính sách điều phối phải cố gắng để cực đại được số lượng tiến trình hoàn thành trên một đơn vị thời gian. Mục tiêu này ít phụ thuộc vào chính sách điều phối mà phụ thuộc nhiều vào thời gian thực hiện trung bình của các tiến trình. Công tác điều phối của hệ điều hành khó có thể thỏa mãn đồng thời tất cả các mục tiêu trên vì bản thân các mục tiêu này đã có sự mâu thuẫn với nhau. Các hệ điều hành chỉ có thể dung hòa các mục tiêu này ở một mức độ nào đó. 3.3. Các thuật toán lập lịch 3.3.1. Đến trước phục vụ trước (First Come Pirst Served - FCFS) Trong thuật toàn này, độ ưu tiên phục vụ tiến trình căn cứ vào thời điểm hình thành tiến trình. Việc cài đặt thuật toán FCFS được quản lý dễ dàng với hàng đợi FIFO Khi CPU rỗi thì hệ điều hành sẽ cấp nó cho tiến trình đầu tiên trong hàng đợi sẵn sàng, đây là tiến trình được chuyển sang trạng thái sẵn sàng sớm nhất, có thể là tiến trình được đưa vào hệ thống sớm nhất. FIFO được sử dụng trong điều phối độc quyền nên khi tiến trình được cấp CPU nó sẽ sở hữu CPU cho đến khi kết thúc xử lý hay phải đợi một thao tác vào/ra hoàn thành, khi đó tiến trình chủ động trả lại CPU cho hệ thống. Ví dụ: cho các tiến trình sau đến ở thời điểm 0 theo thứ tự P1 , P2 , P3 có thời gian xử lý như sau: Tiến trình Thời gian xử lý (ms) P1 24 P2 3 P3 3 Giản đồ Gantt cho việc lập lịch là: 43 Thời gian đợi cho P1 = 0, P2 = 24, P3 = 27 Thời gian đợi trung bình: (0 + 24 + 27)/3 = 17 Nếu các tiến trình đến theo thứ tự: P2 , P3 , P1 thì giản đồ Gantt cho việc lập lịch là: Thời gian đợi cho P1 = 6, P2 = 0, P3 = 3. Thời gian đợi trung bình là: (6 + 0 + 3)/3 = 3. Tốt hơn rất nhiều so với trường hợp trước. Ưu điểm của thuật toán này là thời gian CPU không bị phân phối lại (không bị ngắt) và chi phí tổ chức thực hiện thấp nhất (vì khỏng phải thay đồỉ thứ tự ưu tiên phục vụ, thứ tự trư tiên là thứ tự của tiến trình trong hàng đợi). Nhược điểm của thuật toán là thời gian trung bình chờ phục vụ của các tiến trình là như nhau không kể tiến trình ngắn hay đài), do đó dẫn tới ba nhược điểm sau: - Thời gian chờ đời trung bình sẽ tăng vô hạn khi hệ thống tiếp cận tới hạn khả năng phục vụ của mình. - Nếu độ phát tán thời gian thực hiện tiến trình tăng thì thời gian chờ đợi trung bình cũng tăng theo. - Khi có tiến trình dài, ít bị ngắt thì các tiến trình khác phải chờ đợi lâu hơn. 3.3.2. Công việc ngắn nhất trước (Shortest Job First - SJF) Thuật toán SJF xác định thứ tự ưu tiên thực hiện tiến trình tựa vào tổng thời gian thực hiện tiến trình. Tiến trình nào có tổng thời gian thực hiện ngắn sẽ được ưu tiên phục vụ trước. Ví dụ: cho dãy tiến trình có thời điểm đến và thời gian xử lý như sau: Tiến trình Thời điểm đến Thời gian xử lý(ms) P1 0.0 7 P2 2.0 4 P3 4.0 1 P4 5.0 4 Giản đồ Gantt khi lập lịch theo SJF là: Thời gian đợi trung bình = (0 + 6 + 3 + 7)/4 = 4 Ưu điểm của thuật toán là thời gian chờ đợi trung bình của các tiến trình ngắn hơn so với FCFS. SJF nhanh chóng loại bỏ các tiến trình ngắn giảm số lượng các tiến trình trong hàng đợi. Nhược điểm chính của thuật toán là chế độ phân phối lại thời gian CPU cũng được áp 44 dụng trong trường hợp ngắt các tiến trình dài đang thực hiện để phục vụ các tiến trình ngắn hơn mới mất hiện trong hàng đợi. Nếu tiến trình mới xuất hiện có tổng thời gian thực hiện ngắn nhưng vẫn lớn hơn thời gian cần thiết để thực hiện nốt tiến trình đang thực hiện thì việc ngắt tiến trình là không hợp lý. Khó khăn thật sự với thuật toán SJF là làm thế nào để biết chiều dài của yêu cầu CPU tiếp theo. Một kỹ thuật thường dùng để dự đoán thời gian sử dụng CPU là dùng trung bình hàm mũ (exponential averaging). Chu kỳ CPU kế tiếp thường được đoán như trung bình số mũ của chiều dài các chu kỳ CPU trước đó. Gọi tn là chiều dài của chu kỳ CPU thứ n và gọi τn+1 giá trị được đoán cho chu kỳ CPU kế tiếp. Thì đối với α, với 0 ≤ α ≤ 1, định nghĩa: τn+1 = α tn + (1 - α) τn Thay thế ta có: τn+1 = αtn + (1- α) α tn-1 +…+ (1- α)jαtn-j +…+ (1- α)n+1 ατ0 Nếu chọn α = ½ thì có nghĩa là trị đo được tn và trị dự đoán τn được xem quan trọng như nhau. Hình 3.3. dự đoán thời gian sử dụng CPU. 3.3.3. Công việc còn lại ngắn nhất trước (Shortest Remaining Time First - SRTF) Tương tự như SJF nhưng trong thuật toán này, độ ưu tiên thực hiện các tiến trình dựa vào thời gian cần thiết để thực hiện nốt tiền trình (bằng tổng thời gian trừ đi thời gian đã thực hiện). Như vậy, trong thuật toàn này cần phải thường xuyên cặp nhật thông tin về thời gian đã thực hiện tiến trình. Đồng thời, chế độ phân bố lại thời CPU cũng phải được áp dụng nếu không sẽ làm mất tính tưu việt của thuật toán. Đây có thể coi là phiên bản của SJF theo chế độ không độc quyền. Ví dụ: cho dãy tiến trình có thời điểm đến và thời gian phục vụ như sau: Tiến trình Thời điểm đến Thời gian xử lý(ms) P1 0.0 7 P2 2.0 4 P3 4.0 1 45 P4 5.0 4 Giản đồ Gantt khi lập lịch theo SRTF là: Thời gian đợi trung bình = (9 + 1 + 0 + 2)/4 = 3. Tốt hơn giải thuật SJF. 3.3.4. Lập lịch xoay vòng (Round Robin - RR) Trong thuật toán này, hệ thống quy định một lượng tử thời gian (time quantum) khoảng từ 10 - 100 mili giây. Mỗi tiến trình trong hàng đợi lần lượt được phân phối một lượng tử thời gian để thực hiện. Sau khoảng thời gian đó, nếu tiến trình chưa kết thúc hoặc không rơi vào trạng thái đợi thì nó được chuyển về cuối hàng đợi. Hàng đợi các tiến trình được tổ chức theo kiểu vòng tròn và các tiến trình luôn luôn đảm bảo được phục vụ. Khi có tiến trình mới phát sinh, nó sẽ được đưa vào hàng đợi vòng tròn. Các tiến trình dù ngắn hay dài đều có độ ưu tiên phục vụ như nhau. Nhược điềm là do phải thường xuyên phân phối lại CPU nên thời gian chờ đợi trung bình của RR có thể lớn hơn so với FCFS. Chú ý: Trong thuật toán, cần chọn giá trị lượng tử thời gian thích hợp. Nếu chọn giá trị lượng tử thời gian lớn thì việc bố sung tiến trình mới hoặc kích hoạt tiến trình bị ngắt sẽ làm tăng thời gian chờ đợi trung bình, nhưng ngược lại nếu chọn giá trị lượng tử thời gian nhỏ thì nó sẽ làm cho các tiến trình phải liên tục chuyển trạng thái đầu đến giảm hệ số hữu ích của CPU. Thông thường giá trị lượng tử thời gian được chọn theo công thức q = t/n hoặc q = t/n - s. Trong đó t là thời gian khống chế trước; n là số tiến trình; s là thời gian chuyển từ tiến trình này sang tiến trình khác (chuyển ngữ cảnh). Ví dụ: cho dãy tiến trình có thời điểm đến và thời gian xử lý như sau: Tiến trình Thời gian xử lý(ms) P1 53 P2 17 P3 68 P4 24 Với lượng tử thời gian q = 20 ms Giản đồ Gantt theo thuật toán RR là: Ta có thể thấy thuật toán RR thường có thời gian quay vòng cao hơn SJF, nhưng lại có