Thứ Hai, 29 tháng 7, 2013

Processes and Threads

Processes và threads cả hai là đều là các đơn vị cấu trúc cơ bản trong Windows, chúng ta phải hiểu được chính xác chúng biểu diễn những gì. Bài viết này mô tả các khái niệm cơ bản về processes và threads và thảo luận về các chi tiết như việc chúng được implemented trong Windows như thế nào.

Processes

Một process là một khối xây dựng cơ bản (a fundamental building block) trong Windows. Một process là rất nhiều thứ, nhưng chủ yếu nó là một không gian địa chỉ bộ nhớ được biệt lập. Không gian địa chỉ này có thể sử dụng để running một chương trình, và các không gian địa chỉ được tạo ra cho mọi chương trình để chắc chắn rằng mỗi chương trình runs trong không gian địa chỉ của riêng nó. Bên trong không gian địa chỉ của một process hệ thống có thể nạp code modules, nhưng để thực sự run một chương trình, một process phải có ít nhất một thread running.

Threads

Một thread là một đơn vị thực thi code nguyên thủy (a primitive code excution unit). Tại bất kì một thời điểm cho trước, mỗi processor trong hệ thống đang running một thread, những gì có nghĩa là nó chỉ đang running một đoạn code; nó có thể code của chương trình hoặc OS code, không có vấn đề gì cả. Ý tưởng về threads đó là thay vì tiếp tục run một đoạn code cho khi nó kết thúc, Windows có thể quyết định ngắt một running thread tại bất cứ thời điểm nào và chuyển qua thread khác. Quá trình đó là trái tim trong khả năng của Windows nhằm đạt tới sự đồng thời.

Có thể dễ để hiểu threads là gì nếu bạn suy nghĩ về cách chúng được implemented bởi hệ thống. Về cốt lõi, một thread chỉ là một cấu trúc dữ liệu mà có CONTEXT data structure nói cho hệ thống về trạng thái của processor khi thread chạy lần cuối cùng. Khi bạn nghĩ về nó, một thread giống như là một little virtual processor mà có context riêng của nó và có stack riêng. Physical processor switches giữa nhiều virutal processors và thường xuyên bắt đầu thực thi từ thread's current context information và sử dụng stack của thread.

Lý do của việc một thread có thể có 2 stacks đó là trong Windows threads được luân phiên giữa running user-mode code và kernel-mode code. Cho ví dụ, một thread ứng dụng thông thường runs trong user mode, nhưng nó có thể gọi các system APIs được implemented trong kernel code. Trong các trường hợp như vậy system API code runs trong kernel mode từ bên trong calling thread. Bởi vì thread có thể run trong cả user mode và kernel mode nó phải có 2 stacks: một dành cho khi running trong user mode và một để khi nó running trong kernel mode. Việc tách biệt stack là một yêu cầu bảo mật và mạnh mẽ cơ bản. Nếu user-mode code có quyền truy nhập đến kernel stacks hệ thống có thể bị lỗ hổng đến các cuộc tấn công và nó có thể bị compromised bởi application bugs mà có thể ghi đề lên các phần của kernel stack.

Các thành phần quản lý threads trong Windows là scheduler và dispatcher, 2 thành phần này cùng vơi nhau chịu trách nhiệm cho việc quyết định những thread nào được chạy cho bao lâu, và cho việc thực hiện context switching khi gặp thời điểm nó muốn thay đổi running thread hiện hành.

Một khía cạnh đáng chú ý của kiến trúc Windows đó là kernel là preemtive interruptible , nghĩa là một thread có thể thường xuyên bị ngắt khi running trong kernel mode như là nó có thể bị ngắt trong khi running trong user mode. Cho ví dụ, hầu như mọi Win32 API là có thể bị ngắt, như là hầu hết các internal kernel components. Cũng không phải ngạc nhiên, có một vài components hoặc code areas là không thể bị ngắt (bạn nghĩ điều gì sẽ xảy ra nếu chính scheduler bị ngắt...), nhưng chúng thường chỉ là các đoạn code  rất ngắn.

Context Switching

Chúng ta thường rất khó để hình dung được quá trình mà multithreaded kernel đạt được sự đồng thời với multiple threads, nhưng nó thực sự khá đơn giản. Bước thứ nhất cho kernel là để một thread run. Nó có ý nghĩa là để nạp context của nó (điều đó có nghĩa là entering một không gian địa chỉ bộ nhớ hợp lệ và khởi tạo các giá trị của tất cả các thanh ghi CPU) và để nó bắt đầu running. Thread sau đó runs bình thường trên processor (kernel sẽ không làm bất cứ thứ gì đặc biệt tại điểm này), cho đến thời điểm để chuyển qua một thread mới. Trước khi chúng ta thảo luận về quy trình thực sự của switching contexts, hãy nói về làm thế nào và tại sao một thread bị ngắt.

Sự thực rằng threads hay "từ bỏ" CPU theo ý muốn của nó, và kernel thậm chí không phải thực sự ngắt chúng. Điều đó xảy ra bất cứ khi nào một chương trình đang chờ cho một thứ gì đó. Trong Windows một trong những ví dụ phổ biến nhất đó là khi một chương trình gọi GetMessage Win32 API. GetMessage được gọi tại mọi thời điểm - nó là cách các ứng dụng yêu cầu hệ thống nếu user sinh ra bất cứ new input events (ví dụ như nhấp chuột hoặc gõ bàn phím) . Trong hầu hết các trường hợp, GetMessage truy nhập một message queue và chỉ extracts sự kiện tiếp theo, nhưng trong một vài trường hợp không có bất cứ messages trong queue. Trong những trường hợp như vậy, GetMessage chỉ enter vào waiting mode và không return cho đến khi new user input trở nên available.  

Không có nhận xét nào:

Đăng nhận xét