Xử lý Windows là gì?


153

"Xử lý" khi thảo luận về tài nguyên trong Windows là gì? Họ làm việc như thế nào?

Câu trả lời:


167

Đó là một giá trị tham chiếu trừu tượng cho tài nguyên, thường là bộ nhớ hoặc tệp mở hoặc đường ống.

Chính xác , trong Windows, (và nói chung là trong điện toán), một điều khiển là một sự trừu tượng ẩn địa chỉ bộ nhớ thực từ người dùng API, cho phép hệ thống sắp xếp lại bộ nhớ vật lý trong suốt cho chương trình. Giải quyết một tay cầm thành một con trỏ khóa bộ nhớ và giải phóng tay cầm làm mất hiệu lực con trỏ. Trong trường hợp này, hãy nghĩ về nó như một chỉ mục vào một bảng con trỏ ... bạn sử dụng chỉ mục cho các lệnh gọi API hệ thống và hệ thống có thể thay đổi con trỏ trong bảng theo ý muốn.

Ngoài ra, một con trỏ thực có thể được cung cấp làm tay cầm khi người viết API dự định rằng người dùng API sẽ được cách ly khỏi các chi tiết cụ thể về địa chỉ mà địa chỉ trả về; trong trường hợp này phải xem xét rằng những gì điểm xử lý có thể thay đổi bất cứ lúc nào (từ phiên bản API sang phiên bản hoặc thậm chí từ cuộc gọi đến cuộc gọi của API trả về tay cầm) - do đó, xử lý nên được coi là một giá trị mờ chỉ có ý nghĩa đối với API.

Tôi nên thêm rằng trong bất kỳ hệ điều hành hiện đại nào, ngay cả cái gọi là "con trỏ thực" vẫn xử lý mờ vào không gian bộ nhớ ảo của quy trình, cho phép O / S quản lý và sắp xếp lại bộ nhớ mà không làm mất hiệu lực con trỏ trong quy trình .


4
Tôi thực sự đánh giá cao phản ứng nhanh chóng. Thật không may, tôi nghĩ rằng tôi vẫn còn quá nhiều người mới để hiểu đầy đủ về nó :-(
Al C

4
Có câu trả lời mở rộng của tôi làm sáng tỏ?
Lawrence Dol

100

A HANDLElà một định danh duy nhất theo ngữ cảnh. Theo ngữ cảnh cụ thể, tôi có nghĩa là một tay cầm thu được từ một bối cảnh không nhất thiết phải được sử dụng trong bất kỳ bối cảnh phi lý nào khác cũng hoạt động trên HANDLEs.

Ví dụ, GetModuleHandletrả về một mã định danh duy nhất cho mô-đun hiện đang được tải. Tay cầm trả về có thể được sử dụng trong các chức năng khác chấp nhận tay cầm mô-đun. Nó không thể được trao cho các chức năng yêu cầu các loại tay cầm khác. Ví dụ, bạn không thể cung cấp cho một tay cầm trở về từ GetModuleHandleđến HeapDestroyvà hy vọng nó sẽ làm điều gì đó hợp lý.

Bản HANDLEthân nó chỉ là một loại không thể thiếu. Thông thường, nhưng không nhất thiết, nó là một con trỏ đến một số loại hoặc vị trí bộ nhớ cơ bản. Ví dụ, HANDLEtrả về bởi GetModuleHandlethực sự là một con trỏ đến địa chỉ bộ nhớ ảo cơ sở của mô-đun. Nhưng không có quy tắc nói rằng tay cầm phải là con trỏ. Một tay cầm cũng có thể chỉ là một số nguyên đơn giản (có thể được sử dụng bởi một số API Win32 như một chỉ mục thành một mảng).

HANDLEs là các biểu diễn mờ đục có chủ ý cung cấp sự đóng gói và trừu tượng hóa từ các tài nguyên Win32 nội bộ. Theo cách này, API Win32 có khả năng thay đổi loại cơ bản phía sau TAY, mà không ảnh hưởng đến mã người dùng theo bất kỳ cách nào (ít nhất đó là ý tưởng).

Hãy xem xét ba triển khai nội bộ khác nhau của API Win32 mà tôi vừa tạo và cho rằng đó Widgetlà một struct.

Widget * GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return w;
}
void * GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return reinterpret_cast<void *>(w);
}
typedef void * HANDLE;

HANDLE GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

Ví dụ đầu tiên tiết lộ các chi tiết nội bộ về API: nó cho phép mã người dùng biết rằng GetWidgettrả về một con trỏ tới a struct Widget. Điều này có một vài hậu quả:

  • mã người dùng phải có quyền truy cập vào tệp tiêu đề xác định Widgetcấu trúc
  • mã người dùng có khả năng có thể sửa đổi các phần bên trong của Widgetcấu trúc được trả về

Cả hai hậu quả này có thể là không mong muốn.

Ví dụ thứ hai che giấu chi tiết nội bộ này khỏi mã người dùng, bằng cách trả về void *. Mã người dùng không cần truy cập vào tiêu đề xác định Widgetcấu trúc.

Ví dụ thứ ba hoàn toàn giống với ví dụ thứ hai, nhưng chúng ta chỉ gọi void *một HANDLEthay thế. Có lẽ điều này không khuyến khích mã người dùng cố gắng tìm ra chính xác những gì các void *điểm.

Tại sao phải trải qua rắc rối này? Xem xét ví dụ thứ tư về phiên bản mới hơn của cùng API này:

typedef void * HANDLE;

HANDLE GetWidget (std::string name)
{
    NewImprovedWidget *w;

    w = findImprovedWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

Lưu ý rằng giao diện của hàm giống hệt với ví dụ thứ ba ở trên. Điều này có nghĩa là mã người dùng có thể tiếp tục sử dụng phiên bản API mới này mà không có bất kỳ thay đổi nào, mặc dù việc triển khai "đằng sau hậu trường" đã thay đổi để sử dụng NewImprovedWidgetcấu trúc thay thế.

Các thẻ điều khiển trong các ví dụ này thực sự chỉ là một tên mới, có lẽ là thân thiện hơn, void *chính xác là cái mà a HANDLEcó trong API Win32 (tìm kiếm tại MSDN ). Nó cung cấp một bức tường mờ giữa mã người dùng và các biểu diễn bên trong của thư viện Win32 để tăng tính di động, giữa các phiên bản Windows, mã sử dụng API Win32.


5
Cố tình hay không, bạn đúng - khái niệm chắc chắn mờ đục (ít nhất là với tôi :-)
Al C

5
Tôi đã mở rộng câu trả lời ban đầu của mình với một số ví dụ cụ thể. Hy vọng điều này sẽ làm cho khái niệm minh bạch hơn một chút.
Dan Mould

2
Mở rộng rất hữu ích ... Cảm ơn!
Al C

4
Đây phải là một trong những câu trả lời rõ ràng nhất, trực tiếp và được viết tốt nhất cho bất kỳ câu hỏi nào tôi từng thấy trong một thời gian. Cảm ơn bạn chân thành đã dành thời gian để viết nó!
Andrew

@DanMoulding: Vì vậy, lý do chính để sử dụng handlethay vì void *không khuyến khích người sử dụng mã từ cố gắng tìm ra chính xác những gì * điểm khoảng trống để . Tôi có đúng không?
Sư tử Lai

37

Một TAY trong lập trình Win32 là một mã thông báo đại diện cho một tài nguyên được quản lý bởi nhân Windows. Một tay cầm có thể là một cửa sổ, một tập tin, vv

Xử lý đơn giản là một cách để xác định tài nguyên hạt mà bạn muốn làm việc với việc sử dụng API Win32.

Vì vậy, ví dụ, nếu bạn muốn tạo một Cửa sổ và hiển thị nó trên màn hình, bạn có thể làm như sau:

// Create the window
HWND hwnd = CreateWindow(...); 
if (!hwnd)
   return; // hwnd not created

// Show the window.
ShowWindow(hwnd, SW_SHOW);

Trong ví dụ trên, HWND có nghĩa là "một tay cầm cho một cửa sổ".

Nếu bạn đã quen với một ngôn ngữ hướng đối tượng, bạn có thể nghĩ về một TAY như một thể hiện của một lớp không có phương thức mà trạng thái của chúng chỉ có thể sửa đổi bởi các hàm khác. Trong trường hợp này, hàm ShowWindow sẽ sửa đổi trạng thái của Window HandLE.

Xem Xử lý và các loại dữ liệu để biết thêm thông tin.


Các đối tượng được tham chiếu thông qua HANDLEADT được quản lý bởi kernel. Mặt khác, các loại xử lý mà bạn đặt tên ( HWND, v.v.) là các đối tượng USER. Những người không được quản lý bởi nhân Windows.
IInspectable

1
@IInspectable đoán những người được quản lý bởi công cụ User32.dll?
the_endian

8

Một tay cầm là một định danh duy nhất cho một đối tượng được quản lý bởi Windows. Nó giống như một con trỏ , nhưng không phải là một con trỏ trong trường hợp đó không phải là một địa chỉ có thể được quy định bởi mã người dùng để có quyền truy cập vào một số dữ liệu. Thay vào đó, một tay cầm sẽ được chuyển đến một tập hợp các hàm có thể thực hiện các hành động trên đối tượng mà tay cầm xác định.


5

Vì vậy, ở cấp độ cơ bản nhất, một TAY của bất kỳ loại nào là một con trỏ tới một con trỏ hoặc

#define HANDLE void **

Bây giờ là lý do tại sao bạn muốn sử dụng nó

Hãy thiết lập:

class Object{
   int Value;
}

class LargeObj{

   char * val;
   LargeObj()
   {
      val = malloc(2048 * 1000);
   }

}

void foo(Object bar){
    LargeObj lo = new LargeObj();
    bar.Value++;
}

void main()
{
   Object obj = new Object();
   obj.val = 1;
   foo(obj);
   printf("%d", obj.val);
}

Vì vậy, vì obj được truyền theo giá trị (tạo một bản sao và cung cấp cho hàm đó) cho foo, printf sẽ in giá trị ban đầu là 1.

Bây giờ nếu chúng tôi cập nhật foo thành:

void foo(Object * bar)
{
    LargeObj lo = new LargeObj();
    bar->val++;
}

Có khả năng printf sẽ in giá trị cập nhật là 2. Nhưng cũng có khả năng foo sẽ gây ra một số dạng hỏng bộ nhớ hoặc ngoại lệ.

Lý do là trong khi bạn hiện đang sử dụng một con trỏ để truyền obj cho chức năng bạn cũng đang phân bổ 2 Megs bộ nhớ, điều này có thể khiến HĐH di chuyển bộ nhớ xung quanh việc cập nhật vị trí của obj. Vì bạn đã chuyển con trỏ theo giá trị, nếu obj được di chuyển thì HĐH sẽ cập nhật con trỏ nhưng không phải là bản sao trong hàm và có khả năng gây ra sự cố.

Bản cập nhật cuối cùng cho foo của:

void foo(Object **bar){
    LargeObj lo = LargeObj();
    Object * b = &bar;
    b->val++;
}

Điều này sẽ luôn luôn in giá trị cập nhật.

Xem, khi trình biên dịch cấp phát bộ nhớ cho các con trỏ, nó đánh dấu chúng là bất động, do đó, bất kỳ sự xáo trộn bộ nhớ nào do đối tượng lớn được phân bổ giá trị được truyền cho hàm sẽ trỏ đến địa chỉ chính xác để tìm ra vị trí cuối cùng trong bộ nhớ cập nhật.

Bất kỳ loại TAY nào (hWnd, FILE, v.v.) đều thuộc miền cụ thể và trỏ đến một loại cấu trúc nhất định để bảo vệ chống tham nhũng bộ nhớ.


1
Đây là lý luận thiếu sót; hệ thống con cấp phát bộ nhớ C không thể làm mất hiệu lực con trỏ theo ý muốn. Mặt khác, không có chương trình C hoặc C ++ nào có thể chính xác được; tệ hơn là bất kỳ chương trình nào đủ độ phức tạp sẽ không chính xác theo định nghĩa. Bên cạnh đó, cảm ứng kép không giúp ích gì nếu bộ nhớ được trỏ trực tiếp di chuyển xung quanh bên dưới chương trình trừ khi con trỏ thực sự là một sự trừu tượng hóa từ bộ nhớ thực - điều này sẽ khiến nó trở thành tay cầm .
Lawrence Dol

1
Hệ điều hành Macintosh (trong các phiên bản lên đến 9 hoặc 8) đã làm chính xác như trên. Nếu bạn đã phân bổ một số đối tượng hệ thống, bạn thường sẽ xử lý nó, để HĐH tự do di chuyển đối tượng xung quanh. Với kích thước bộ nhớ hạn chế của các máy Mac đầu tiên khá quan trọng.
Rhialto hỗ trợ Monica

5

Một tay cầm giống như một giá trị khóa chính của một bản ghi trong cơ sở dữ liệu.

chỉnh sửa 1: tốt, tại sao downvote, khóa chính xác định duy nhất một bản ghi cơ sở dữ liệu và một thẻ điều khiển trong hệ thống Windows xác định duy nhất một cửa sổ, một tệp đã mở, v.v., đó là những gì tôi đang nói.


1
Tôi không tưởng tượng bạn có thể khẳng định rằng tay cầm là duy nhất. Nó có thể là duy nhất cho mỗi Windows Station của người dùng, nhưng nó không được đảm bảo là duy nhất nếu có nhiều người dùng truy cập cùng một hệ thống cùng một lúc. Nghĩa là, nhiều người dùng có thể lấy lại giá trị xử lý giống hệt nhau về mặt số, nhưng trong bối cảnh Windows Station của người dùng, họ ánh xạ tới những thứ khác nhau ...
Nick

2
@nick Thật độc đáo trong một bối cảnh nhất định. Khóa chính sẽ không phải là duy nhất giữa các bảng khác nhau ...
Benny Mackney 17/07/18

2

Hãy nghĩ về cửa sổ trong Windows như là một cấu trúc mô tả nó. Cấu trúc này là một phần nội bộ của Windows và bạn không cần phải biết chi tiết về nó. Thay vào đó, Windows cung cấp một typedef cho con trỏ để cấu trúc cho cấu trúc đó. Đó là "tay cầm" mà bạn có thể giữ trên cửa sổ.,


Đúng, nhưng luôn luôn đáng nhớ rằng xử lý thường không phải là một địa chỉ bộ nhớ và một mã người dùng không nên bỏ qua nó.
sharptooth
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.