Sự khác biệt giữa các đối tượng được chia sẻ (.so), thư viện tĩnh (.a) và DLL (.so)?


272

Tôi đã tham gia vào một số cuộc tranh luận liên quan đến các thư viện trong Linux và muốn xác nhận một số điều.

Theo hiểu biết của tôi (vui lòng sửa cho tôi nếu tôi sai và tôi sẽ chỉnh sửa bài đăng của mình sau), rằng có hai cách sử dụng thư viện khi xây dựng ứng dụng:

  1. Thư viện tĩnh (tệp .a): Tại thời điểm liên kết, một bản sao của toàn bộ thư viện được đưa vào ứng dụng cuối cùng để các chức năng trong thư viện luôn có sẵn cho ứng dụng gọi điện
  2. Các đối tượng được chia sẻ (tệp .so): Tại thời điểm liên kết, đối tượng chỉ được xác minh đối với API của nó thông qua tệp tiêu đề (.h) tương ứng. Thư viện không thực sự được sử dụng cho đến khi thời gian chạy, nơi cần thiết.

Ưu điểm rõ ràng của các thư viện tĩnh là chúng cho phép độc lập toàn bộ ứng dụng, trong khi lợi ích của các thư viện động là tệp ".so" có thể được thay thế (ví dụ: trong trường hợp cần cập nhật do bảo mật lỗi) mà không yêu cầu ứng dụng cơ sở được biên dịch lại.

Tôi đã nghe một số người phân biệt giữa các đối tượng được chia sẻ và các thư viện được liên kết động (DLL), mặc dù cả hai đều là các tệp ".so". Có sự khác biệt nào giữa các đối tượng được chia sẻ và DLL khi nói đến phát triển C / C ++ trên Linux hoặc bất kỳ HĐH tuân thủ POSIX nào khác (ví dụ: MINIX, UNIX, QNX, v.v.) không? Tôi được biết rằng một điểm khác biệt chính (cho đến nay) là các đối tượng dùng chung chỉ được sử dụng trong thời gian chạy, trong khi DLL phải được mở trước bằng cách sử dụng lệnh gọi dlopen () trong ứng dụng.

Cuối cùng, tôi cũng đã nghe một số nhà phát triển đề cập đến "lưu trữ được chia sẻ", theo hiểu biết của tôi, cũng là các thư viện tĩnh, nhưng không bao giờ được sử dụng trực tiếp bởi một ứng dụng. Thay vào đó, các thư viện tĩnh khác sẽ liên kết với "kho lưu trữ được chia sẻ" để kéo một số (nhưng không phải tất cả) các chức năng / tài nguyên từ kho lưu trữ được chia sẻ vào thư viện tĩnh đang được xây dựng.

Cảm ơn tất cả các bạn trước sự giúp đỡ của bạn.

Cập nhật


Trong bối cảnh các thuật ngữ này được cung cấp cho tôi, đó là những thuật ngữ sai lầm được sử dụng bởi một nhóm các nhà phát triển Windows phải học Linux. Tôi đã cố gắng sửa chúng, nhưng các chuẩn mực ngôn ngữ (không chính xác) bị mắc kẹt.

  1. Đối tượng chia sẻ: Một thư viện được tự động liên kết vào một chương trình khi chương trình khởi động và tồn tại dưới dạng một tệp độc lập. Thư viện được bao gồm trong danh sách liên kết tại thời gian biên dịch (nghĩa là: LDOPTS+=-lmylibđối với tệp thư viện có tên mylib.so). Thư viện phải có mặt tại thời điểm biên dịch và khi ứng dụng khởi động.
  2. Thư viện tĩnh: Một thư viện được hợp nhất vào chính chương trình thực tế khi xây dựng một ứng dụng (lớn hơn) chứa mã ứng dụng và mã thư viện được tự động liên kết thành một chương trình khi chương trình được xây dựng và nhị phân cuối cùng chứa cả hai chương trình chính và thư viện tồn tại dưới dạng một tệp nhị phân độc lập. Thư viện được bao gồm trong danh sách liên kết tại thời gian biên dịch (nghĩa là: LDOPTS+=-lmylibđối với tệp thư viện có tên mylib.a). Thư viện phải có mặt tại thời gian biên dịch.
  3. DLL: Về cơ bản giống như một đối tượng chia sẻ, nhưng thay vì được đưa vào danh sách liên kết tại thời gian biên dịch, thư viện được tải thông qua dlopen()/ dlsym()lệnh để thư viện không cần phải có mặt trong thời gian xây dựng để chương trình biên dịch. Ngoài ra, thư viện không cần phải có mặt (nhất thiết) khi khởi động ứng dụng hoặc biên dịch thời gian , vì nó chỉ cần thiết tại thời điểm dlopen/ dlsymcuộc gọi được thực hiện.
  4. Lưu trữ được chia sẻ: Về cơ bản giống như một thư viện tĩnh, nhưng được biên dịch với các cờ "xuất khẩu chia sẻ" và "-fPIC". Thư viện được bao gồm trong danh sách liên kết tại thời gian biên dịch (nghĩa là: LDOPTS+=-lmylibSđối với tệp thư viện có tên mylibS.a). Sự khác biệt giữa hai loại này là cờ bổ sung này là bắt buộc nếu một đối tượng chia sẻ hoặc DLL muốn liên kết tĩnh kho lưu trữ được chia sẻ thành mã riêng của nó VÀ có thể cung cấp các chức năng trong đối tượng chia sẻ cho các chương trình khác, thay vì chỉ sử dụng chúng nội bộ của DLL. Điều này rất hữu ích trong trường hợp khi ai đó cung cấp cho bạn một thư viện tĩnh và bạn muốn đóng gói lại dưới dạng SO. Thư viện phải có mặt tại thời gian biên dịch.

Cập nhật bổ sung

Sự khác biệt giữa " DLL" và " shared library" chỉ là một từ thông tục (lười biếng, không chính xác) trong công ty tôi làm việc tại thời điểm đó (các nhà phát triển Windows bị buộc phải chuyển sang phát triển Linux và thuật ngữ bị mắc kẹt), tuân thủ các mô tả đã nêu ở trên.

Ngoài ra, Schữ "" theo sau tên thư viện, trong trường hợp "lưu trữ chia sẻ" chỉ là một quy ước được sử dụng tại công ty đó, và không phải trong ngành công nghiệp nói chung.


14
Đối với .acác tệp, "a" thực sự là viết tắt của "archove" và nó chỉ đơn giản là một kho lưu trữ các tệp đối tượng. Các trình liên kết hiện đại phải đủ tốt để không cần bao gồm thư viện while, chỉ các tệp đối tượng trong kho lưu trữ cần thiết và thậm chí có thể chỉ sử dụng các phần của mã / dữ liệu trong các tệp đối tượng được tham chiếu.
Một số lập trình viên anh chàng

4
DLL chỉ là thuật ngữ Windows. Nó không được sử dụng trên các đơn vị.
R .. GitHub DỪNG GIÚP ICE



2
@DevNull "vòm tôi đã " tất nhiên. :)
Một số lập trình viên

Câu trả lời:


93

Tôi đã luôn nghĩ rằng DLL và các đối tượng chia sẻ chỉ là các thuật ngữ khác nhau cho cùng một thứ - Windows gọi chúng là DLL, trong khi trên các hệ thống UNIX, chúng là các đối tượng được chia sẻ, với thuật ngữ chung - thư viện được liên kết động - bao gồm cả (thậm chí cả chức năng mở một .so trên UNIX được gọi là dlopen() sau 'thư viện động').

Chúng thực sự chỉ được liên kết khi khởi động ứng dụng, tuy nhiên quan niệm xác minh của bạn đối với tệp tiêu đề là không chính xác. Tệp tiêu đề xác định các nguyên mẫu được yêu cầu để biên dịch mã sử dụng thư viện, nhưng tại thời điểm liên kết, trình liên kết sẽ nhìn vào bên trong thư viện để đảm bảo các chức năng mà nó cần thực sự ở đó. Trình liên kết phải tìm các cơ quan chức năng ở đâu đó tại thời điểm liên kết hoặc nó sẽ gây ra lỗi. Nó cũng làm điều đó trong thời gian chạy, vì như bạn chỉ ra chính xác thư viện có thể đã thay đổi kể từ khi chương trình được biên dịch. Đây là lý do tại sao sự ổn định của ABI rất quan trọng trong các thư viện nền tảng, vì sự thay đổi ABI là thứ phá vỡ các chương trình hiện có được biên dịch so với các phiên bản cũ hơn.

Các thư viện tĩnh chỉ là các gói tệp đối tượng ra khỏi trình biên dịch, giống như các tệp mà bạn đang tự xây dựng như một phần trong quá trình biên dịch dự án của mình, do đó chúng được kéo vào và đưa vào trình liên kết theo cách chính xác và các bit không được sử dụng rơi chính xác theo cùng một cách.


1
Tại sao một số dự án tôi thấy trên Linux phải sử dụng lệnh gọi dlopen () để truy cập các chức năng trong tệp ".so" và một số không phải làm điều đó? Cảm ơn bạn bằng cách này!
Đám mây

9
Những người không làm điều đó có được các hàm được trao cho họ bởi trình tải quy trình, tức là trình tải elf của linux. dlopen tồn tại nếu ứng dụng muốn mở và sử dụng .so hoặc dll không có ở đó để biên dịch hoặc chỉ thêm chức năng bổ sung, như plugin.
rapadura

Nhưng ứng dụng sẽ không hoàn toàn biên dịch nếu .so không có mặt tại thời điểm xây dựng? Có thể buộc trình liên kết chỉ xây dựng chương trình cuối cùng mà không có .so hiện diện không? Cảm ơn bạn.
Đám mây

1
Tôi tin rằng nó phụ thuộc vào cách bạn sử dụng các hàm từ .so, nhưng ở đây kiến ​​thức của tôi về điều này dừng lại: / Câu hỏi hay.
rapadura

1
Về dlopen () và họ chức năng của nó, tôi hiểu rằng cái này được sử dụng để mở / đóng một chương trình để nó không phải được tải vào bộ nhớ trong toàn bộ quá trình chạy ứng dụng. Mặt khác, bạn phải thông báo cho trình liên kết trong các đối số dòng lệnh của nó (còn gọi là tệp tạo tệp của bạn) rằng bạn muốn thư viện được tải. Nó sẽ được tải trong thời gian chạy và được tải trong bộ nhớ cho đến khi ứng dụng thoát. Có nhiều điều có thể xảy ra ở cấp độ HĐH, nhưng đây là những gì xảy ra khi có liên quan đến ứng dụng của bạn.
Taylor Giá

197

Một thư viện tĩnh (.a) là một thư viện có thể được kết nối trực tiếp vào thực thi cuối cùng được tạo bởi các mối liên kết, nó được chứa trong nó và không cần phải có thư viện vào hệ thống nơi thực thi sẽ được triển khai.

Một thư viện chia sẻ (.so) là một thư viện được liên kết nhưng không được nhúng trong thực thi chính thức, vì vậy sẽ được nạp khi thực thi được khởi động và cần thiết phải có mặt trong hệ thống mà thực thi được triển khai.

Một thư viện liên kết động trên cửa sổ (.dll) giống như một thư viện chia sẻ (.so) trên linux nhưng có một số khác biệt giữa hai ngôn ngữ này có liên quan đến hệ điều hành (Windows vs Linux):

Một DLL có thể định nghĩa hai loại chức năng: xuất khẩu và nội bộ. Các hàm được xuất khẩu được gọi bởi các mô-đun khác, cũng như từ bên trong DLL nơi chúng được xác định. Các hàm nội bộ thường chỉ được gọi từ bên trong DLL nơi chúng được xác định.

Một thư viện SO trên Linux không cần câu lệnh xuất đặc biệt để chỉ ra các ký hiệu có thể xuất được, vì tất cả các ký hiệu đều có sẵn cho một quy trình thẩm vấn.


1
+1 giải thích đơn giản tốt đẹp. Nếu một hàm được khai báo là "Internal" trong DLL, điều đó có nghĩa là nó không thể được gọi từ bên ngoài thư viện?
Mike

23
Không nhất thiết là tất cả các biểu tượng đều có sẵn trong thư viện SO. Các biểu tượng ẩn là có thể và được khuyến nghị vì không có lý do chính đáng để người dùng thư viện nhìn thấy tất cả các biểu tượng của bạn.
Zan Lynx

3
FYI: g ++ có __attribute__cú pháp cho các biểu tượng 'xuất khẩu' có chọn lọc:#define DLLEXPORT __attribute__ ((visibility("default"))) #define DLLLOCAL __attribute__ ((visibility("hidden")))
Brian Haak

33

Tôi có thể giải thích chi tiết về DLL trong Windows để giúp làm rõ những bí ẩn đó với bạn bè của tôi ở đây trong * NIX-đất ...

Một DLL giống như một tệp đối tượng được chia sẻ. Cả hai đều là hình ảnh, sẵn sàng tải vào bộ nhớ bởi trình tải chương trình của HĐH tương ứng. Các hình ảnh được kèm theo các bit siêu dữ liệu khác nhau để giúp các trình liên kết và trình tải thực hiện các liên kết cần thiết và sử dụng thư viện mã.

Windows DLL có một bảng xuất. Việc xuất có thể theo tên hoặc theo vị trí bảng (số). Phương thức thứ hai được coi là "trường học cũ" và mỏng manh hơn nhiều - xây dựng lại DLL và thay đổi vị trí của hàm trong bảng sẽ kết thúc trong thảm họa, trong khi không có vấn đề thực sự nếu liên kết các điểm vào là theo tên. Vì vậy, hãy quên rằng đó là một vấn đề, nhưng hãy lưu ý rằng nó ở đó nếu bạn làm việc với mã "khủng long" như libs của nhà cung cấp bên thứ 3.

Các DLL Windows được xây dựng bằng cách biên dịch và liên kết, giống như bạn làm với EXE (ứng dụng thực thi), nhưng DLL có nghĩa là không đứng một mình, giống như một SO được sử dụng bởi một ứng dụng, thông qua tải động, hoặc bằng liên kết thời gian liên kết (tham chiếu đến SO được nhúng trong siêu dữ liệu nhị phân của ứng dụng và trình tải chương trình HĐH sẽ tự động tải các SO tham chiếu). DLL có thể tham chiếu các DLL khác, giống như SO có thể tham chiếu các SO khác.

Trong Windows, DLL sẽ chỉ có sẵn các điểm nhập cụ thể. Chúng được gọi là "xuất khẩu". Nhà phát triển có thể sử dụng một từ khóa trình biên dịch đặc biệt để làm cho biểu tượng hiển thị bên ngoài (với các trình liên kết khác và trình tải động) hoặc xuất khẩu có thể được liệt kê trong tệp định nghĩa mô-đun được sử dụng tại thời điểm liên kết khi chính DLL được tạo ra. Thực tiễn hiện đại là trang trí định nghĩa hàm với từ khóa để xuất tên biểu tượng. Cũng có thể tạo các tệp tiêu đề với các từ khóa sẽ khai báo biểu tượng đó là một tệp được nhập từ một DLL bên ngoài đơn vị biên dịch hiện tại. Tra cứu các từ khóa __declspec (dllexport) và __declspec (dllimport) để biết thêm thông tin.

Một trong những tính năng thú vị của DLL là chúng có thể khai báo hàm xử lý tiêu chuẩn "khi tải / không tải". Bất cứ khi nào DLL được tải hoặc không tải, DLL có thể thực hiện một số khởi tạo hoặc dọn dẹp, tùy theo từng trường hợp. Điều này ánh xạ độc đáo vào việc có một DLL như một trình quản lý tài nguyên hướng đối tượng, chẳng hạn như trình điều khiển thiết bị hoặc giao diện đối tượng dùng chung.

Khi nhà phát triển muốn sử dụng DLL đã được xây dựng, cô ấy phải tham chiếu "thư viện xuất khẩu" (* .LIB) được tạo bởi nhà phát triển DLL khi cô tạo DLL, hoặc cô ấy phải tải DLL một cách rõ ràng trong thời gian chạy và yêu cầu địa chỉ điểm nhập theo tên thông qua các cơ chế LoadL Library () và GetProcAddress (). Hầu hết thời gian, liên kết với tệp LIB (chỉ đơn giản chứa siêu dữ liệu liên kết cho các điểm nhập đã xuất của DLL) là cách DLL được sử dụng. Tải động thường được dành riêng để thực hiện "đa hình" hoặc "cấu hình thời gian chạy" trong các hành vi của chương trình (truy cập các tiện ích bổ sung hoặc chức năng được xác định sau này, còn gọi là "bổ trợ").

Cách làm việc của Windows đôi khi có thể gây ra một số nhầm lẫn; hệ thống sử dụng phần mở rộng .LIB để chỉ cả thư viện tĩnh thông thường (lưu trữ, như tệp POSIX * .a) và thư viện "xuất khẩu gốc" cần thiết để liên kết ứng dụng với DLL tại thời điểm liên kết. Vì vậy, người ta phải luôn luôn xem liệu tệp * .LIB có tệp * .DLL có cùng tên hay không; nếu không, rất có thể tệp * .LIB là một kho lưu trữ thư viện tĩnh và không xuất siêu dữ liệu ràng buộc cho một DLL.


4

Bạn đã đúng khi các tệp tĩnh được sao chép vào ứng dụng tại thời điểm liên kết và các tệp được chia sẻ chỉ được xác minh tại thời điểm liên kết và được tải khi chạy.

Cuộc gọi dlopen không chỉ dành cho các đối tượng được chia sẻ, nếu ứng dụng muốn thực hiện điều đó trong thời gian chạy, nếu không các đối tượng chia sẻ sẽ được tải tự động khi ứng dụng khởi động. DLL và .so là cùng một điều. dlopen tồn tại để thêm các khả năng tải động hạt mịn hơn nữa cho các quy trình. Bạn không phải sử dụng dlopen để mở / sử dụng DLL, điều này cũng xảy ra khi khởi động ứng dụng.


Điều gì sẽ là một ví dụ về việc sử dụng dlopen () để kiểm soát tải nhiều hơn? Nếu SO / DLL được tải tự động khi khởi động, chẳng hạn dlopen () sẽ đóng và mở lại với các quyền hoặc hạn chế khác nhau, chẳng hạn? Cảm ơn bạn.
Đám mây

1
Tôi tin rằng dlopen là dành cho plugin hoặc chức năng tương tự. Các quyền / hạn chế phải giống như đối với tải tự động, và dù sao thì một dlopen sẽ tải đệ quy các thư viện phụ thuộc.
rapadura

DLL và .sokhông chính xác những điều tương tự. Xem câu trả lời này
Basile Starynkevitch
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.