2012-02-15 35 views
13

Gần đây tôi đã lén nhìn vào việc thực hiện hạt nhân Linux của một đọc và viết nguyên tử và một vài câu hỏi xuất hiện.Đọc và viết thực hiện hoạt động nguyên tử trong hạt nhân Linux

Đầu tiên các mã có liên quan từ kiến ​​trúc ia64:

typedef struct { 
    int counter; 
} atomic_t; 

#define atomic_read(v)  (*(volatile int *)&(v)->counter) 
#define atomic64_read(v) (*(volatile long *)&(v)->counter) 

#define atomic_set(v,i)  (((v)->counter) = (i)) 
#define atomic64_set(v,i) (((v)->counter) = (i)) 
  1. Đối với cả đọc và ghi các hoạt động, có vẻ như cách tiếp cận trực tiếp được thực hiện để đọc từ hay ghi vào biến. Trừ khi có một thủ thuật khác ở đâu đó, tôi không hiểu những gì đảm bảo tồn tại rằng hoạt động này sẽ là nguyên tử trong miền lắp ráp. Tôi đoán một câu trả lời rõ ràng sẽ là một hoạt động như vậy dịch sang một mã opcode lắp ráp, nhưng ngay cả như vậy, làm thế nào là đảm bảo khi có tính đến các cấp độ bộ nhớ cache khác nhau (hoặc tối ưu hóa khác)?

  2. Trên các macro đã đọc, loại dễ bay hơi được sử dụng trong thủ thuật đúc. Bất cứ ai cũng có một đầu mối làm thế nào điều này ảnh hưởng đến nguyên tử ở đây? (Lưu ý rằng nó không được sử dụng trong thao tác ghi)

Trả lời

13

Tôi nghĩ rằng bạn hiểu nhầm (sử dụng rất mơ hồ) của từ "nguyên tử" và "dễ bay hơi" ở đây. Nguyên tử chỉ thực sự có nghĩa là các từ sẽ được đọc hoặc viết một cách nguyên tử (trong một bước, và đảm bảo rằng nội dung của vị trí bộ nhớ này sẽ luôn là một chữ viết hoặc cái kia, và không phải cái gì ở giữa). Và từ khóa volatile cho trình biên dịch không bao giờ giả sử dữ liệu ở vị trí đó do đọc/ghi trước đó (về cơ bản, không bao giờ tối ưu hóa việc đọc).

Những từ "nguyên tử" và "dễ bay hơi" KHÔNG có nghĩa ở đây là có bất kỳ hình thức đồng bộ hóa bộ nhớ nào. Không ngụ ý bất kỳ rào cản hoặc hàng rào đọc/ghi nào. Không có gì được đảm bảo liên quan đến bộ nhớ và kết hợp bộ nhớ cache.Những chức năng này về cơ bản chỉ ở mức phần mềm, và phần cứng có thể tối ưu hóa/nói dối tuy nhiên nó có vẻ phù hợp.

Bây giờ, tại sao chỉ cần đọc là đủ: các mô hình bộ nhớ cho mỗi kiến ​​trúc là khác nhau. Nhiều kiến ​​trúc có thể đảm bảo đọc hoặc viết nguyên tử cho dữ liệu được căn chỉnh với một số byte bù đắp, hoặc x từ độ dài, vv và thay đổi từ CPU sang CPU. Hạt nhân Linux chứa nhiều định nghĩa cho các kiến ​​trúc khác nhau cho phép nó thực hiện mà không cần bất kỳ cuộc gọi nguyên tử nào (trên nền tảng) đảm bảo (đôi khi thậm chí chỉ trong thực tế ngay cả khi trong thực tế thông số của chúng nói không thực sự đảm bảo) viết.

Đối với volatile, trong khi không có nhu cầu cho nó nói chung trừ khi bạn đang truy cập IO bộ nhớ ánh xạ, tất cả phụ thuộc vào khi/nơi/tại sao atomic_readatomic_write macro đang được gọi. Nhiều trình biên dịch sẽ (mặc dù nó không được thiết lập trong spec C) tạo ra các rào cản bộ nhớ/hàng rào cho các biến dễ bay hơi (GCC, ngoài đỉnh đầu của tôi, là một. MSVC làm cho chắc chắn.). Trong khi điều này sẽ bình thường có nghĩa là tất cả lần đọc/ghi vào biến này hiện chính thức được miễn trừ về bất kỳ tối ưu hóa trình biên dịch nào, trong trường hợp này bằng cách tạo biến biến động ảo "ảo" -các lợi ích cho tối ưu hóa và sắp xếp lại.

2

Nếu bạn viết cho một kiến ​​trúc cụ thể, bạn có thể đưa ra các giả định cụ thể cho nó.
Tôi đoán IA-64 sẽ biên dịch những thứ này thành một lệnh duy nhất.

Bộ nhớ cache không phải là vấn đề, trừ khi bộ đếm vượt qua giới hạn đường bộ nhớ cache. Nhưng nếu cần có sự liên kết byte 4/8, điều này không thể xảy ra.

Hướng dẫn nguyên tử "thực" là bắt buộc khi lệnh máy dịch thành hai truy cập bộ nhớ. Đây là trường hợp gia số (đọc, tăng, viết) hoặc so sánh số & hoán đổi.

volatile ảnh hưởng đến tối ưu hóa trình biên dịch có thể thực hiện.
Ví dụ, nó ngăn cản trình biên dịch chuyển đổi nhiều lần đọc thành một lần đọc.
Nhưng trên cấp độ hướng dẫn máy, nó không có gì.

3

Lần đọc là nguyên tử trên hầu hết các kiến ​​trúc lớn, miễn là chúng được căn chỉnh với nhiều kích thước của chúng (và không lớn hơn kích thước đọc của kiểu cung cấp), xem sách hướng dẫn Cấu trúc Intel. Mặt khác, viết dưới dạng x86, ghi byte đơn và viết liên kết có thể là nguyên tử, theo IPF (IA64), mọi thứ đều sử dụng và phát hành ngữ nghĩa, làm cho nó được đảm bảo nguyên tử, xem this.

volatile ngăn trình biên dịch lưu vào bộ nhớ đệm giá trị cục bộ, buộc nó phải truy xuất ở nơi có quyền truy cập vào nó.

+0

Trong hạt nhân hiện đại, 'biến động' ở đây được sử dụng thông qua macro được gọi là' READ_ONCE() '/' WRITE_ONCE'. Cách diễn giải trong đầu của tôi là các trình biên dịch về mặt kỹ thuật được phép đọc/ghi giá trị * nhiều * lần. Ví dụ. nếu mã sao chép một giá trị đọc vào một biến cục bộ, sau đó nó được sử dụng ở các vị trí khác nhau. Vì vậy, chúng tôi phải mô tả điều này nhiều hơn một chút so với việc ngăn chặn giá trị được lưu trữ cục bộ. Mô tả đầy đủ: https://lwn.net/Articles/508991/ – sourcejedi

Các vấn đề liên quan