LƯU Ý: Tôi sẽ giả định rằng máy của bạn có đơn vị ánh xạ bộ nhớ (MMU). Có một phiên bản Linux (GianClinux) không yêu cầu MMU và câu trả lời này không áp dụng ở đó.
MMU là gì? Đó là phần cứng của bộ xử lý và / hoặc bộ điều khiển bộ nhớ. Hiểu liên kết thư viện dùng chung không yêu cầu bạn hiểu chính xác cách MMU hoạt động, chỉ là MMU cho phép có sự khác biệt giữa các địa chỉ bộ nhớ logic (các địa chỉ được sử dụng bởi các chương trình) và vật lýđịa chỉ bộ nhớ (những địa chỉ thực sự hiện diện trên bus bộ nhớ). Bộ nhớ được chia thành các trang, thường có kích thước 4K trên Linux. Với 4k trang, địa chỉ lôgic 0404095 là trang 0, địa chỉ lôgic 4096 Biệt8191 là trang 1, v.v. MMU ánh xạ chúng tới các trang vật lý của RAM và mỗi trang logic có thể được ánh xạ thành 0 hoặc 1 trang vật lý. Một trang vật lý nhất định có thể tương ứng với nhiều trang logic (đây là cách chia sẻ bộ nhớ: nhiều trang logic tương ứng với cùng một trang vật lý). Lưu ý điều này áp dụng bất kể hệ điều hành; đó là một mô tả về phần cứng.
Khi chuyển đổi quy trình, nhân thay đổi ánh xạ trang MMU, để mỗi quy trình có không gian riêng. Địa chỉ 4096 trong quy trình 1000 có thể (và thường là) hoàn toàn khác với địa chỉ 4096 trong quy trình 1001.
Khá nhiều bất cứ khi nào bạn nhìn thấy một địa chỉ, đó là một địa chỉ hợp lý. Các chương trình không gian người dùng hầu như không bao giờ xử lý các địa chỉ vật lý.
Bây giờ, có nhiều cách để xây dựng thư viện là tốt. Giả sử một chương trình gọi hàm foo()
trong thư viện. CPU không biết gì về các ký hiệu, hoặc các hàm gọi thực sự, nó chỉ biết cách nhảy đến một địa chỉ logic và thực thi bất kỳ mã nào nó tìm thấy ở đó. Có một số cách nó có thể làm điều này (và những điều tương tự được áp dụng khi thư viện truy cập dữ liệu toàn cầu của chính nó, v.v.):
- Nó có thể mã cứng một số địa chỉ logic để gọi nó tại. Điều này đòi hỏi thư viện luôn được tải tại cùng một địa chỉ logic. Nếu hai thư viện yêu cầu cùng một địa chỉ, liên kết động không thành công và bạn không thể khởi chạy chương trình. Các thư viện có thể yêu cầu các thư viện khác, vì vậy về cơ bản, điều này đòi hỏi mọi thư viện trên hệ thống phải có các địa chỉ logic duy nhất. Tuy nhiên, nó rất nhanh, nếu nó hoạt động. (Đây là cách a.out đã làm mọi thứ, và loại thiết lập mà prelinking làm, sắp xếp).
- Nó có thể mã hóa một địa chỉ logic giả và yêu cầu trình liên kết động chỉnh sửa theo đúng địa chỉ khi tải thư viện. Điều này tốn một chút thời gian khi tải các thư viện, nhưng sau đó thì rất nhanh.
- Nó có thể thêm một lớp không xác định: sử dụng một thanh ghi CPU để giữ địa chỉ logic mà thư viện được tải vào, và sau đó truy cập mọi thứ như một phần bù từ thanh ghi đó. Điều này áp đặt một chi phí hiệu suất trên mỗi truy cập.
Khá nhiều người không sử dụng số 1 nữa, ít nhất là không phải trên các hệ thống có mục đích chung. Giữ danh sách địa chỉ lôgic duy nhất đó là không thể trên các hệ thống 32 bit (không có đủ để đi xung quanh) và một cơn ác mộng hành chính trên các hệ thống 64 bit. Mặc dù vậy, loại liên kết trước thực hiện điều này trên cơ sở mỗi hệ thống.
Việc số 2 hay số 3 được sử dụng tùy thuộc vào việc thư viện được xây dựng với -fPIC
tùy chọn (mã độc lập vị trí) của GCC . # 2 là không có, # 3 là với. Nói chung, các thư viện được xây dựng cùng -fPIC
, vì vậy # 3 là những gì xảy ra.
Để biết thêm chi tiết, hãy xem Cách viết thư viện chia sẻ (PDF) của Ulrich Drepper .
Vì vậy, cuối cùng, câu hỏi của bạn có thể được trả lời:
- Nếu thư viện được xây dựng với
-fPIC
(vì nó gần như chắc chắn phải như vậy), phần lớn các trang hoàn toàn giống nhau cho mọi quy trình tải nó. Các quy trình của bạn a
và b
cũng có thể tải thư viện ở các địa chỉ logic khác nhau, nhưng các địa chỉ đó sẽ trỏ đến cùng các trang vật lý: bộ nhớ sẽ được chia sẻ. Hơn nữa, dữ liệu trong RAM khớp chính xác với những gì trên đĩa, do đó, nó chỉ có thể được tải khi cần bởi trình xử lý lỗi trang.
- Nếu thư viện được xây dựng mà không có
-fPIC
, thì hóa ra hầu hết các trang của thư viện sẽ cần chỉnh sửa liên kết và sẽ khác. Do đó, chúng phải là các trang vật lý riêng biệt (vì chúng chứa dữ liệu khác nhau). Điều đó có nghĩa là chúng không được chia sẻ. Các trang không khớp với những gì trên đĩa, vì vậy tôi sẽ không ngạc nhiên nếu toàn bộ thư viện được tải. Tất nhiên sau đó nó có thể được hoán đổi ra đĩa (trong tệp hoán đổi).
Bạn có thể kiểm tra điều này với pmap
công cụ hoặc trực tiếp bằng cách kiểm tra các tệp khác nhau trong /proc
. Ví dụ, đây là một đầu ra (một phần) của pmap -x
hai bc
s mới được sinh ra khác nhau . Lưu ý rằng các địa chỉ được hiển thị bởi pmap là địa chỉ lôgic điển hình:
pmap -x 14739
Address Kbytes RSS Dirty Mode Mapping
00007f81803ac000 244 176 0 r-x-- libreadline.so.6.2
00007f81803e9000 2048 0 0 ----- libreadline.so.6.2
00007f81805e9000 8 8 8 r---- libreadline.so.6.2
00007f81805eb000 24 24 24 rw--- libreadline.so.6.2
pmap -x 17739
Address Kbytes RSS Dirty Mode Mapping
00007f784dc77000 244 176 0 r-x-- libreadline.so.6.2
00007f784dcb4000 2048 0 0 ----- libreadline.so.6.2
00007f784deb4000 8 8 8 r---- libreadline.so.6.2
00007f784deb6000 24 24 24 rw--- libreadline.so.6.2
Bạn có thể thấy rằng thư viện được tải thành nhiều phần và pmap -x
cung cấp cho bạn thông tin chi tiết về từng phần riêng biệt. Bạn sẽ nhận thấy rằng các địa chỉ logic khác nhau giữa hai quy trình; bạn có thể mong đợi chúng giống nhau một cách hợp lý (vì cùng một chương trình đang chạy và máy tính thường có thể dự đoán được như vậy), nhưng có một tính năng bảo mật được gọi là ngẫu nhiên bố trí không gian địa chỉ , ngẫu nhiên chúng ngẫu nhiên.
Bạn có thể thấy từ sự khác biệt về kích thước (Kbytes) và kích thước lưu trú (RSS) mà toàn bộ phân khúc thư viện chưa được tải. Cuối cùng, bạn có thể thấy rằng đối với các ánh xạ lớn hơn, bẩn là 0, có nghĩa là nó tương ứng chính xác với những gì trên đĩa.
Bạn có thể chạy lại pmap -XX
và nó sẽ hiển thị cho bạn tùy thuộc vào phiên bản kernel mà bạn đang chạy, vì đầu ra -XX thay đổi theo phiên bản kernel mà ánh xạ đầu tiên có Shared_Clean
176, khớp chính xác với RSS
. Shared
bộ nhớ có nghĩa là các trang vật lý được chia sẻ giữa nhiều quy trình và vì nó khớp với RSS, điều đó có nghĩa là tất cả các thư viện trong bộ nhớ đều được chia sẻ (xem phần Xem thêm bên dưới để biết thêm về chia sẻ so với riêng tư):
pmap -XX 17739
Address Perm Offset Device Inode Size Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous AnonHugePages Swap KernelPageSize MMUPageSize Locked VmFlagsMapping
7f784dc77000 r-xp 00000000 fd:00 1837043 244 176 19 176 0 0 0 176 0 0 0 4 4 0 rd ex mr mw me sd libreadline.so.6.2
7f784dcb4000 ---p 0003d000 fd:00 1837043 2048 0 0 0 0 0 0 0 0 0 0 4 4 0 mr mw me sd libreadline.so.6.2
7f784deb4000 r--p 0003d000 fd:00 1837043 8 8 8 0 0 0 8 8 8 0 0 4 4 0 rd mr mw me ac sd libreadline.so.6.2
7f784deb6000 rw-p 0003f000 fd:00 1837043 24 24 24 0 0 0 24 24 24 0 0 4 4 0 rd wr mr mw me ac sd libreadline.so.6.2
Xem thêm
-fPIC
sử dụng đã hoàn toàn thay đổi một thời gian trước đây)?