Ngày nay, với sự phát triển với tốc độ chóng mặt
của khoa học kỹ thuật, một kỷ nguyên mới được mở ra, kỷ nguyên của công nghệ
thông tin. Nhu cầu của loài người ngày càng lớn, đặc biệt là các ngành khoa học
kỹ thuật khác đều cần đến sự hổ trợ của công nghệ thông tin, mặc dù công nghệ
phần cứng phát triển rất nhanh, CPU với tốc độ xử lý ngày càng cao, nhưng lại
nảy sinh nhiều bài toán trong thực tế sản xuất đòi hỏi phải xử lí nhanh hơn
nữa.
Vấn đề xử lý song song đang ngày càng được nghiên
cứu nhiều để giải quyết một số bài toán mà thực tiễn đang đặt ra, những vấn đề
cần có kết quả trong thời gian thực như: bài toán dự báo thời tiết, điều tiết
giao thông, điều khiển các con tàu vũ trụ,các bài toán về mô phỏng…Vì vậy, việc
nghiên cứu các giải thuật cho xử lý song song là một yêu cầu và là một thách
thức cho các nhà khoa học liên quan đến khoa học máy tính. Trong bài viết này mình và các bạn cùng đi tìm hiểu về khái niệm và cơ chế của đa luồng
1. Khái niệm
- Luồng là một cách thông dụng để nâng cao năng
lực xử lý của các ứng dụng nhờ vào cơ chế song song.
- Một luồng là một đơn vị cơ bản của việc sử dụng
CPU.
- Nó hình thành gồm: một định danh luồng (thread
ID), một bộ đếm chương trình, tập thanh ghi và ngăn xếp.
- Nó chia sẻ với các luồng khác thuộc cùng một
quá trình một không gian địa chỉ. Nhờ đó các luồng có thể sử dụng các biến toàn
cục, chia sẻ các tài nguyên.
- Cách thức các luồng chia sẻ CPU cũng giống như
cách thức của các quá trình.
- Một luồng cũng có những trạng thái: đang chạy
(running), sẵn sàng (ready), nghẽn (blocked) và kết thúc (dead). Một luồng thì
được xem như là một quá trình nhẹ.
Nhờ vào luồng, người ta thiết kế các server có thể đáp ứng nhiều
yêu cầu một cách đồng thời.
Các bước tổng quát của một server phục vụ song
song
Server phục vụ song song gồm hai phần thực hiện
song song nhau:
- Phần 1 ( Dispatcher thread ): Xử lý các yêu cầu
kết nối, lặp lại các công việc sau:
+ Lắng nghe yêu cầu kết nối của clients
+ Chấp nhận một yêu cầu kết nối
Tạo kênh giao tiếp ảo mới với clients
Tạo phần 2 để xử lý các thông điệp yêu cầu của
clients.
- Phần 2 (Worker Thread): Xử lý các thông điệp
yêu cầu từ clients, lặp lại các công việc sau:
+ Chờ nhận thông điệp yêu cầu của clients.
+ Phân tích và xử lý yêu cầu.
+ Gửi thông điệp trả lời cho clients.
Phần 2 sẽ kết thúc khi kênh ảo bị xóa đi.
Với mỗi client, trên server sẽ có một Phần 2 để
xử lý yêu cầu của clients. Như vậy tại thời điểm bất kỳ luôn tồn tại một Phần 1
và 0 hoặc nhiều Phần 2
Do phần 2 thực thi song song với phần 1 cho nên
nó được thiết kế là một thread
- Nhìn từ góc độ hệ điều hành, luồng có thể được
cài đặt ở một trong hai mức:
• Trong không gian người dùng (user space)
• Trong không gian nhân (kernel mode)
2. Luồng ở mức người dùng
Kiến trúc luồng
cài đặt ở mức người dùng
được hỗ trợ dưới nhân và được cài đặt bởi thư viện luồng tại cấp
người dùng. Thư viện cung cấp hỗ trợ cho việc tạo luồng, lập thời biểu, và quản lý mà không có sự hỗtrợ
từ nhân. Vì nhân không biết các luồng cấp người dùng, tất cả việc tạo luồng và lập thời biểu được thực
hiện trong không gian người dùng mà không cần sựcan thiệp của nhân. Do đó, các luồng cấp người dùng
thường tạo và quản lý nhanh, tuy nhiên chúng cũng có những trởngại. Thí dụ, nếu nhân là đơn luồng thì
bất cứ luồng cấp người dùng thực
hiện một lời gọi hệ thống nghẽn sẽ làm cho toàn bộ quá trình bị nghẽn,
thậm chí nếu các luồng khác sẳn dùng để chạy trong ứng dụng. Các thư viện luồng người dùng gồm các
luồng POSIX Pthreads, Mach C-threads và Solaris 2 UIthreads.
Không gian người dùng bao gồm một hệ thống runtime mà nó tập hợp
những thủ tục quản lý luồng. Các luồng chạy trong không gian nằm bên trên hệ thống runtime thì được
quản lý bởi hệ thống này. Hệ thống runtime cũng lưu giữ một bảng tin trạng thái để theo dõi trạng
thái hiện hành của mỗi luồng.
Tương ứng với mỗi luồng sẽ có một mục từ trong bảng, bao gồm các
thông tin về trạng thái, giá trị thanhghi, độ ưu tiên và các thông tin khác về luồng.
Tiếp cận này có hai mức định thời biểu (Scheduling): bộ định thời
biểu cho các quá trình nặng và bộ định thời biểu trong hệ thống
runtime. Bộ lập biểu của hệ thống runtime chia thời gian sử dụng CPU được cấp cho một quá trình thành những khoảng nhỏ hơn để cấp cho các luồng
trong quá trình đó. Như vậy việc kết thúc một luồng thì vượt ra ngoài tầm kiểm soát của kernel hệ
thống.
3. Luồng ở mức hạt nhân hệ điều hành
Kiến trúc cài đặt ở mức hệ thống
được hỗ trợ
trực tiếp bởi hệ điều hành. Nhân thực hiện việc tạo luồng, lập thời biểu, và quản
lý khônggian nhân.
Vì quản lý luồng được thực hiện bởi hệ điều hành, luồng nhân thường tạo và quản
lý chậm hơn luồng người
dùng. Tuy nhiên, vì nhân được quản lý các luồng nếu một luồng thực hiện lời gọi hệ thống
nghẽn, nhân có thểlập thời biểu một luồng khác trong ứng dụng thực thi. Trong
môi trường đa xử lý, nhân
có thểlập thời biểu luồng trên một bộxửlý khác. Hầu hết các hệ điều hành hiện
nay như Windows NT,
Windows 2000, Solaris 2, BeOS và Tru64 UNIX (trước Digital UNIX)-hỗtrợ các luồng
nhân. Trong tiếp
cận này không có hệ thống runtime và các luồng thì được quản lý bởi kernel của
hệ điềuhành. Vì vậy,
bảng thông tin trạng thái của tất cả các luồng thì được lưu trữ bởi kernel. Tất
cả những lời gọi mà nó
làm nghẽn luồng sẽ được bẫy (TRAP) đến kernel. Khi một luồng bị nghẽn, kernel
chọn luồng khác cho thực
thi. Luồng được chọn có thể cùng một quá trình với luồng bị nghẽn hoặc thuộc một
quá
trình khác,
vì vậy sự tồn tại của một luồng thì được biết bởi kernel và chỉ có một mức lập
biểu trong hệ thống.
4. Các loại cài đặt luồng
4.1
Mô hình nhiều-một
Mô
hình nhiều-một ánh xạ nhiều luồng cấp người dùng tới
Mô hình nhiều – một
một
luồng cấp nhân. Quản lý luồng được thực hiện trong không gian người dùng vì thế
nó hiệu quả nhưng toàn bộquá trình sẽ bị khóa nếu một
luồng thực hiện lời gọi hệ thống khóa. Vì chỉ một luồng có thể truy xuất nhân tại
một thời điểm nên nhiều luồng không thể chạy song song trên nhiều bộ xử lý. Greenthreads-một
thư viện luồng được cài đặt trên các hệ điều hành không hỗ trợ luồng nhân dùng
mô hình nhiều-một.
4.2
Mô hình một-một
Mô hình một-một ánh xạ mỗi luồng người dùng tới một luồng nhân. Nó cung cấp khả năng đồng hành tốt hơn mô hình nhiều-một bằng cách
cho một uồng khác chạy khi một luồng thực hiện lời gọi hệ thống nghẽn; nó
cũng cho phép nhiều luồng chạy song song trên các bộ xử lý khác nhau. Chỉ có một trở ngại trong mô hình
này là tạo luồng người dùng yêu cầu tạo một luồng nhân tương ứng. Vì chi phí cho việc tạo luồng nhân
có thể đè nặng lên năng lực thực hiện của ứng dụng, các cài đặt cho mô hình này giới hạn số luồng
được hỗ trợ bởi hệ thống. Windows NT, Windows 2000 và OS/2
cài đặt mô hình một-một này.
Mô hình một - một
4.3
Mô hình nhiều-nhiều
Mô hình nhiều-nhiều đa hợp nhiều luồng cấp người dùng tới số lượng nhỏ hơn hay bằng các luồng nhân. Số lượng các luồng nhân có thể
được xác định hoặc một ứng dụng cụthể hay một máy cụ thể(một ứng dụng có
thể được cấp nhiều luồng nhân trên một bộ đa xử lý hơn trên một bộ đơn xử lý). Trong khi mô hình nhiều-một
cho phép người phát triển tạo nhiều luồng người dùng như họ muốn, thì đồng hành thật sự là
không đạt được vì nhân có thểl ập thời biểu chỉ một luồng tại một thời điểm. Mô hình một-một cho phép đồng
hành tốt hơn nhưng người phát triển phải cẩn thận không tạo ra quá nhiều luồng trong một ứng
dụng. Mô hình nhiều-nhiều gặp phải một trong hai vấn đề khiếm khuyết: người phát triển có thểtạo
nhiều luồng người dùng khi cần thiết và các luồng nhân tương ứng có thểchạy song song trên
một bộ đa xử lý. Khi một luồng thực hiện một lời gọi hệ thống khóa, nhân có thể lập thời biểu một
luồng khác thực thi. Solaris 2, IRIX, HP-UX, và Tru64 UNIX hỗ trợ mô hình này.
Mô hình nhiều - nhiều
5. Cấp phát luồng
5.1
Lời gọi hệ thống fork và exec
Trong chương trước
chúng ta mô tả lời gọi hệ thống fork được dùng để tạo một quá trình bản sao Riêng như thế nào. Trong một chương trình đa
luồng, ngữ nghĩa của các lời gọi hệ thống fork và Exec thay đổi. Nếu một luồng trong lời gọi
chương trình fork thì quá trình mới sao chép lại quá trình tất cả luồng hay là một quá
trình đơn luồng mới? Một số hệ thống UNIX chọn hai ấn bản fork, một sao chép lại tất cảluồng và một
sao chép lại chỉluồng được nạp lên lời gọi hệ thống fork. Lời gọi hệ thống exec điển hình thực hiện công việc trong cùng một
cách như được mô tảtrong chương trước. Nghĩa là, nếu một luồng nạp lời gọi hệ thống exec, chương
trình được xác định trong tham số exec sẽ thay thế toàn bộ quá trình-chứa tất cảluồng
và các quá trình tải nhẹ. Việc sử dụng hai ấn bản fork phụthuộc vào ứng dụng. Nếu exec bịhủy tức thì sau khi phân
nhánh (forking) thì sựsao chép lại tất cảluồng là không cần thiết khi chương trình được xác định trong các
tham số exec sẽt hay thế quá trình. Trong trường hợp này, việc sao chép lại chỉ gọi luồng hợp lý.
Tuy nhiên, nếu quá trình riêng biệt này không gọi exec sau khi phân nhánh thì
quá trình riêng biệt này nên sao chép lại tất cả luồng
5.2 Sự hủy bỏ luồng
Hủy một luồng là một
tác vụ kết thúc một luồng trước khi nó hoàn thành.Thí dụ, nếu nhiều luồng đangtìm kiếm đồng thời
thông qua một cơ sở dữ liệu và một luồng trả về kết quả, các luồng còn lại có
thể bị hủy. Một trường hợp khác có thể xảy ra khi người
dùng nhấn một nút trên trình duyệt web để dừng trang web đang được tải. Thường
một trang web được tải trong một luồng riêng. Khi người dùng nhấn nút stop, luồng đang nạp
trang bịhủy bỏ. Một luồng bị hủy thường được xem như luồng đích. Sự hủy bỏ một luồng đích có thểxảy ra
hai viễn cảnh khác nhau:
• Hủy bất đồng bộ: một luồng lập tức kết thúc
luồng đích
• Hủy trì hoãn: luồng đích có thể kiểm tra định
kỳ nếu nó sắp kết thúc, cho phép luồng đích một cơ hộitự kết thúc trong một
cách có thứ tự. Sựkhó khăn của việc hủy này xảy ra trong những trường hợp khi tài nguyên được cấp
phát tới một luồng bị hủy hay một luồng bị hủy trong khi việc cập nhật dữliệu xảy ra giữa chừng, nó đang
chia sẻ với các luồng khác. Điều này trởnên đặc biệt khó khăn với sựhủy bất đồng bộ. Hệ điều hành
thường đòi lại tài nguyên hệ thống từ luồng bị hủy nhưng thường nó sẽ không
đòi lại tất cả tài nguyên. Do đó, việc hủy một
luồng bất đồng bộ có thể không giải phóng hết tài nguyênhệ thống cần thiết. Một
chọn lựa khác, sự hủy trì hoãn thực hiện bằng một luồng báo hiệu rằng một luồng đích bị này cho phép một luồng kiểm tra
nếu nó sẽ bị hủy tại điểm nó có thể an toàn bị hủy. Pthreads
gọi những điểm như thế là các điểm hủy (cancellation points). Hầu hết hệ điều
hành cho phép một quá trình hay một luồng bị hủy bất đồng bộ. Tuy nhiên,
Pthread API cung cấp sự hủy trì hoãn. Điều này có nghĩa rằng một hệ điều hành
cài đặt Pthread API sẽ cho phép sự hủy có trì hoãn
6. Nhóm luồng
Trong phần trước, chúng ta mô tả
kịch bản đa luồng của một trình phục vụ web. Trong trường hợp này, bất cứ khi
nào trình phục vụ nhận một yêu cầu, nó tạo một luồng riêng để phục vụ yêu cầu
đó. Ngược lại, tạo một luồng riêng thật sự cao hơn tạo một quá trình riêng, dù
sao một trình phục vụ đa luồng có thể phát sinh vấn đề. Quan tâm đầu tiên là
lượng thời gian được yêu cầu để tạo luồng trước khi phục vụ yêu cầu, và lượng
thời gian xoá luồng khi nó hoàn thành. Vấn đề thứ hai là vấn đề khó giải quyết
hơn: nếu chúng ta cho phép tất cả yêu cầu đồng hành được phục vụ trong một
luồng mới, chúng ta không thay thế giới hạn trên số lượng luồng hoạt động đồng
hành trong hệ thống. Những luồng không giới hạn có thể làm cạn kiệt tài nguyên
hệ thống, như thời gian CPU và bộ nhớ. Một giải pháp cho vấn đề này là sử dụng
nhóm luồng.
Ý tưởng chung nằm sau nhóm luồng là
tạo số lượng luồng tại thời điểm khởi động và đặt chúng vào nhóm, nơi chúng
ngồi và chờ công việc. Khi một trình phục vụ nhận một yêu cầu, chúng đánh thức
một luồng từ nhóm- nếu một luồng sẳn dùng – truyền nó yêu cầu dịch vụ. Một khi
luồng hoàn thành dịch vụ của nó, nó trả về nhóm đang chờ công việc kế. Nếu nhóm
không chứa luồng sẳn dùng, trình phục vụ chờ cho tới khi nó rảnh.
Nói cụ thể, các lợi ích của nhóm
luồng là:
Thường
phục vụ yêu cầu nhanh hơn với luồng đã có hơn là chờ để tạo luồng.
Một
nhóm luồng bị giới hạn số lượng luồng tồn tại bất kỳ thời điểm nào. Điều này
đặc biệt quan trọng trên những hệ thống không hỗ trợ số lượng lớn các luồng
đồng hành.
Số lượng luồng trong nhóm có thể
được đặt theo kinh nghiệm (heuristics) dựa trên các yếu tố như số CPU trong hệ
thống, lượng bộ nhớ vật lý và số yêu cầu khách hàng đồng hành. Kiến trúc nhóm
luồng tinh vi hơn có thể tự điều chỉnh số lượng luồng trong nhóm dựa theo các
mẫu sử dụng. Những kiến trúc như thế cung cấp lợi điểm xa hơn của các nhóm
luồng nhỏ hơn-do đó tiêu tốn ít bộ nhớ hơn-khi việc nạp trên hệ thống là chậm.
7.
Pthreads
Pthreads tham chiếu tới chuẩn POSIX
(IEEE 1003.1c) định nghĩa API cho việc tạo và đồng bộ luồng. Đây là một đặc tả
cho hành vi luồng không là một cài đặt. Người thiết kế hệ điều hành có thể cài
đặt đặc tả trong cách mà họ muốn. Thông thường, các thư viện cài đặt đặc tả
Pthread bị giới hạn đối với các hệ thống dựa trên cơ sở của UNIX như Solaris 2.
Hệ điều hành Windows thường không hỗ trợ Pthreads mặc dù các ấn bản shareware
là sẳn dùng trong phạm vi công cộng.
Trong phần này chúng ta giới thiệu
một số Pthread API như một thí dụ cho thư viện luồng cấp người dùng. Chúng ta
sẽ xem nó như thư viện cấp người dùng vì không có mối quan hệ khác biệt giữa
một luồng được tạo dùng Pthread và luồng được gắn với nhân. Chương trình C hiển
thị trong hình dưới đây, mô tả một Pthread API cơ bản để xây dựng một chương
trình đa luồng.
Chương trình hiển thị trong hình tạo
một luồng riêng xác định tính tổng của một số nguyên không âm. Trong chương
trình Pthread, các luồng riêng bắt đầu thực thi trong một hàm xác định. Trong
hình, đây là một hàm runner. Khi chương trình này bắt đầu, một luồng riêng điều
khiển bắt đầu trong main. Sau khi khởi tạo, main tạo ra luồng thứ hai bắt đầu
điều khiển trong hàm runner.
Bây giờ chúng ta sẽ cung cấp tổng
quan của chương trình này chi tiết hơn. Tất cả chương trình Pthread phải chứa
tập tin tiêu đề pthread.h. pthread_t tid khai báo danh biểu cho luồng sẽ được
tạo. Mỗi luồng có một tập các thuộc tính gồm kích thước ngăn xếp và thông tin
định thời. Khai báo pthread_attr_t attr hiện diện các thuộc tính cho luồng.
Chúng ta sẽ thiết lập các thuộc tính trong gọi hàm
pthread_attr_init(&attr). Vì chúng ta không thiết lập rõ thuộc tính, chúng
ta sẽ dùng thuộc tính mặc định được cung cấp. Một luồng riêng được tạo với lời
gọi hàm pthread_create. Ngoài ra, để truyền định danh của luồng và các thuộc
tính cho luồng, chúng ta cũng truyền tên của hàm, nơi một luồng mới sẽ bắt đầu
thực thi, trong trường hợp này là hàm runner. Cuối cùng chúng ta sẽ truyền số
nguyên được cung cấp tại dòng lệnh, argv[1].
Tại điểm này, chương trình có hai
luồng: luồng khởi tạo trong main và luồng thực hiện việc tính tổng trong hàm runner.
Sau khi tạo luồng thứ hai, luồng main sẽ chờ cho luồng runner hoàn thành bằng
cách gọi hàm pthread_join. Luồng runner sẽ hoàn thành khi nó gọi hàm
pthread_exit.
#include<pthread>
#include<stdio.h>
int sum: /*Dữ liệu này được chia sẻ
bởi thread(s)*/
void *runner(void *param); /*luồng*/
main(int argc, char *argv[])
{
pthread_t tid; /*định danh của
luồng*/
pthread_attr_t attr; /*tập hợp các
thuộc tính*/
if(argc !=2){
fprintf(stderr, “usage: a.out
<integer value>”);
exit();
}
if (atoi(argv[1] < 0)){
fprintf(stderr,”%d must be >= 0
\n”, atoi(argv[1]));
exit();
}
/*lấy các thuộc tính mặc định*/
pthread_attr_init(&attr);
/*tạo một luồng*/
pthread_create(&tid,&attr,runner,
argv[1]);
/*bây giờ chờ luồng kết thúc*/
pthread_join(tid,NULL);
printf(“sum = %d\n”,sum);
/*Luồng sẽ bắt đầu điều khiển trong
hàm này*/
void *runner(void *param)
{
int upper = atoi(param);
int i;
sum = 0;
if (upper > 0){
sum+= i;
}
pthread_exit(0);
}
}
8.
Luồng Solaris 2
Solaris 2 là một ấn bản của UNIX với
hỗ trợ luồng tại cấp độ nhân và cấp độ người dùng, đa xử lý đối xứng (SMP) và
định thời thời thực. Solaris 2 cài đặt Pthread API hỗ trợ luồng cấp người dùng
với thư viện chứa APIs cho việc tạo và quản lý luồng (được gọi luồng UI). Sự
khác nhau giữa hai thư viện này rất lớn, mặc dù hầu hết người phát triển hiện
nay chọn thư viện Pthread. Solaris 2 cũng định nghĩa một cấp độ luồng trung
gian. Giữa luồng cấp nhân và cấp người dùng là các quá trình nhẹ (lightweight
process- LWPs). Mỗi quá trình chứa ít nhất một LWP. Thư viện luồng đa hợp luồng
người dùng trên nhóm LWP cho quá trình và chỉ luồng cấp người dùng hiện được
nối kết tới một LWP hoàn thành công việc. Các luồng còn lại bị khoá hoặc chờ
cho một LWP mà chúng có thể thực thi trên nó.
Luồng cấp nhân chuẩn thực thi tất cả
thao tác trong nhân. Mỗi LWP có một luồng cấp nhân, và một số luồng cấp nhân
(kernel) chạy trên một phần của nhân và không có LWP kèm theo (thí dụ, một
luồng phục vụ yêu cầu đĩa ). Các luồng cấp nhân chỉ là những đối tượng được
định thời trong hệ thống. Solaris 2 cài mô hình nhiều-nhiều; toàn bộ hệ thống
luồng của nó được mô tả trong hình dưới đây:
Luồng Solaris 2
Các luồng cấp người dùng có thể giới
hạn hay không giới hạn. Một luồng cấp người dùng giới hạn được gán vĩnh viễn
tới một LWP. Chỉ luồng đó chạy trên LWP và yêu cầu LWP có thể được tận hiến tới
một bộ xử lý đơn (xem luồng trái nhất trong hình trên). Liên kết một luồng có
ích trong trường hợp yêu cầu thời gian đáp ứng nhanh, như ứng dụng thời thực.
Một luồng không giới hạn gán vĩnh viễn tới bất kỳ LWP nào. Tất cả các luồng
không giới hạn được đa hợp trong một nhóm cac LWP sẳn dùng cho ứng dụng. Các
luồng không giới hạn là mặc định. Solaris 8 cũng cung cấp một thư viện luồng
thay đổi mà mặc định chúng liên kết tới tất cả các luồng với một LWP.
Xem xét hệ thống trong hoạt động: bất
cứ một quá trình nào có thể có nhiều luồng người dùng. Các luồng cấp người dùng
này có thể được định thời và chuyển đổi giữa LWPs bởi thư viện luồng không có
sự can thiệp của nhân. Các luồng cấp người dùng cực kỳ hiệu quả vì không có sự
hỗ trợ nhân được yêu cầu cho việc tạo hay huỷ, hay thư viện luồng chuyển ngữ
cảnh từ luồng người dùng này sang luồng khác.
Mỗi LWP được nối kết tới chính xác
một luồng cấp nhân, ngược lại mỗi luồng cấp người dùng là độc lập với nhân.
Nhiều LWPs có thể ở trong một quá trình, nhưng chúng được yêu cầu chỉ khi luồng
cần giao tiếp với một nhân. Thí dụ, một LWP được yêu cầu mỗi luồng có thể khoá
đồng hành trong lời gọi hệ thống. Xem xét năm tập tin khác nhau-đọc các yêu cầu
xảy ra cùng một lúc. Sau đó, năm LWPs được yêu cầu vì chúng đang chờ hoàn thành
nhập/xuất trong nhân. Nếu một tác vụ chỉ có bốn LWPs thì yêu cầu thứ năm sẽ
không phải chờ một trong những LWPs để trả về từ nhân. Bổ sung một LWP thứ sáu
sẽ không đạt được gì nếu chỉ có đủ công việc cho năm.
Các luồng nhân được định thời bởi bộ
lập thời biểu của nhân và thực thi trên một hay nhiều CPU trong hệ thống. Nếu
một luồng nhân khoá (trong khi chờ một thao tác nhập/xuất hoàn thành), thì bộ
xử lý rảnh để thực thi luồng nhân khác. Nếu một luồng bị khoá đang chạy trên
một phần của LWP thì LWP cũng khoá. Ở trên vòng, luồng cấp người dùng hiện được
gán tới LWP cũng bị khoá. Nếu một quá trình có nhiều hơn một LWP thì nhân có
thể định thời một LWP khác.
Thư viện luồng tự động thay đổi số
lượng LWPs trong nhóm để đảm bảo năng lực thực hiện tốt nhất cho ứng dụng. Thí
dụ, nếu tất cả LWPs trong một quá trình bị khoá bởi những luồng có thể chạy thì
thư viện tự tạo một LWP khác được gán tới một luồng đang chờ. Do đó, một chương
trình được ngăn chặn từ một chương trình khác bởi sự nghèo nàn của những LWPs
không bị khoá. LWPs là những tài nguyên nhân đắt để duy trì nếu chúng không
được dùng. Thư viện luồng “ages” LWPs và xoá chúng khi chúng không được dùng
cho khoảng thời gian dài, điển hình khoảng 5 phút.
Các nhà phát triển dùng những cấu
trúc dữ liệu cài đặt luồng trên Solaris 2:
- Luồng cấp người dùng
chứa một luồng ID; tập thanh ghi (gồm một bộ đếm chương trình và con trỏ ngăn xếp);
ngăn xếp; và độ ưu tiên (được dùng bởi thư viện cho mục đích định thời). Không
có cấu trúc dữ liệu nào là tài nguyên nhân; tất cả chúng tồn tại trong không
gian người dùng.
- Một LWP có một tập
thanh ghi cho luồng cấp nhân nó đang chạy cũng như bộ nhớ và thông tin tính
toán. Một LWP là một cấu trúc dữ liệu nhân và nó nằm trong không gian nhân
-
Một luồng nhân chỉ có một cấu trúc dữ liệu
nhân và ngăn xếp. Cấu trúc dữ liệu gồm bản sao các thanh ghi nhân, con trỏ tới
LWP mà nó được gán, độ ưu tiên và thông tin định thời.
Mỗi quá trình trong Solaris 2 gồm
nhiều thông tin được mô tả trong khối điều khiển quá trình (Process Control
Block-PCB ). Trong thực tế, một quá trình Solaris 2 chứa một định danh quá
trình (Process ID-PID); bản đồ bộ nhớ; danh sách các tập tin đang mở, độ ưu
tiên; và con trỏ của các luồng nhân vơi quá trình.
Quá trình Solaris 2
Comments
Post a Comment