Khi nào nên sử dụng thư viện động so với tĩnh


437

Khi tạo thư viện lớp trong C ++, bạn có thể chọn giữa thư viện động ( .dll, .so) và tĩnh ( .lib, .a). Sự khác biệt giữa chúng là gì và khi nào thì thích hợp để sử dụng cái nào?


2
Cần lưu ý rằng cũng có một cái gì đó được gọi là "Thư viện nhập khẩu", hãy kiểm tra stackoverflow.com/questions/3573475/
Kẻ

Câu trả lời:


299

Thư viện tĩnh tăng kích thước mã trong nhị phân của bạn. Chúng luôn được tải và bất kỳ phiên bản mã nào bạn biên dịch là phiên bản mã sẽ chạy.

Thư viện động được lưu trữ và phiên bản riêng. Có thể tải phiên bản của thư viện động không phải là phiên bản gốc được gửi cùng với mã của bạn nếu bản cập nhật được coi là tương thích nhị phân với phiên bản gốc.

Ngoài ra, các thư viện động không nhất thiết phải được tải - chúng thường được tải khi được gọi lần đầu tiên - và có thể được chia sẻ giữa các thành phần sử dụng cùng một thư viện (tải nhiều dữ liệu, tải một mã).

Các thư viện động được coi là cách tiếp cận tốt hơn hầu hết thời gian, nhưng ban đầu chúng có một lỗ hổng lớn (google DLL hell), tất cả đã bị loại bỏ bởi các HĐH Windows gần đây (đặc biệt là Windows XP).


71
Trên Windows / Mac (không có trình quản lý gói) thực sự không có lý do chính đáng để sử dụng các thư viện động qua tĩnh. Vì các DLL của Windows không thể định vị lại, nên việc chia sẻ mã thường không hoạt động (và thông thường mỗi ứng dụng sẽ vận chuyển và sử dụng các phiên bản thư viện riêng của nó. Lợi ích thực sự duy nhất là cập nhật thư viện dễ dàng hơn.
Zifre

5
Trên mac, tôi sử dụng rất nhiều thư viện động. ví dụ, mac os x có nhúng sqlite3. tôi đã tạo một chương trình có tính năng cơ sở dữ liệu sqlite3 để lưu trữ hiệu năng. tuy nhiên, vì hiếm khi được sử dụng liên kết động giúp tiết kiệm thời gian biên dịch, giúp việc kiểm tra dễ dàng hơn / nhanh hơn, tuy nhiên, nếu tôi xây dựng phiên bản phát hành, tôi nghĩ rằng tôi sẽ luôn sử dụng thư viện tĩnh trong trường hợp có vấn đề tương thích
ReachConnection

6
@Zifre: relocitable = có thể được tải tại các địa chỉ ảo khác nhau. DLL chắc chắn hỗ trợ này.
dma_k

20
@dma_k: DLL Windows có thể được tải tại các địa chỉ khác nhau, nhưng chỉ vì trình liên kết sao chép tất cả mã và thay đổi số địa chỉ. Với các đối tượng được chia sẻ, tất cả các tham chiếu địa chỉ là tương đối, vì vậy nhiều quá trình có thể chia sẻ cùng một bộ nhớ cho đối tượng được chia sẻ. Nói cách khác, trên Windows, DLL 1 MB được sử dụng bởi 3 chương trình = 3 MB. Trên Linux, A MB SO được sử dụng bởi 3 chương trình = 1 MB.
Zifre

7
Cả Windows và Linux đều có khái niệm di chuyển theo thời gian tải của các thư viện dùng chung eli.thegreenplace.net/2011/08/08/ . Điều lớn nhất cho phép Mã độc lập vị trí không phải là điều gì đặc biệt đối với Linux, thay vào đó là địa chỉ liên quan đến RIP với tập lệnh x64; cả Windows và Linux đều có thể sử dụng địa chỉ tương đối RIP để giảm số lượng bản sửa lỗi khi di chuyển thư viện.
clemahieu

194

Những người khác đã giải thích thỏa đáng thư viện tĩnh là gì, nhưng tôi muốn chỉ ra một số lưu ý khi sử dụng thư viện tĩnh, ít nhất là trên Windows:

  • Singletons: Nếu một cái gì đó cần phải toàn cầu / tĩnh và duy nhất, hãy cẩn thận về việc đưa nó vào một thư viện tĩnh. Nếu nhiều DLL được liên kết với thư viện tĩnh đó, mỗi tệp sẽ nhận được bản sao riêng của singleton. Tuy nhiên, nếu ứng dụng của bạn là một EXE duy nhất không có DLL tùy chỉnh, điều này có thể không thành vấn đề.

  • Loại bỏ mã không được ước tính: Khi bạn liên kết với thư viện tĩnh, chỉ các phần của thư viện tĩnh được tham chiếu bởi DLL / EXE của bạn sẽ được liên kết vào DLL / EXE của bạn.

    Ví dụ: nếu mylib.libchứa a.objb.objvà DLL / EXE của bạn chỉ tham chiếu các hàm hoặc biến từ a.obj, toàn bộ b.objsẽ bị loại bỏ bởi trình liên kết. Nếu b.objchứa các đối tượng toàn cục / tĩnh, các hàm tạo và hàm hủy của chúng sẽ không được thực thi. Nếu những người xây dựng / phá hủy có tác dụng phụ, bạn có thể thất vọng vì sự vắng mặt của họ.

    Tương tự như vậy, nếu thư viện tĩnh chứa các điểm vào đặc biệt, bạn có thể cần phải quan tâm rằng chúng thực sự được bao gồm. Một ví dụ về điều này trong lập trình nhúng (được chứ không phải Windows) sẽ là một trình xử lý ngắt được đánh dấu là ở một địa chỉ cụ thể. Bạn cũng cần đánh dấu trình xử lý ngắt là điểm vào để đảm bảo nó không bị loại bỏ.

    Một hậu quả khác của việc này là một thư viện tĩnh có thể chứa các tệp đối tượng hoàn toàn không sử dụng được do các tham chiếu chưa được giải quyết, nhưng nó sẽ không gây ra lỗi liên kết cho đến khi bạn tham chiếu một hàm hoặc biến từ các tệp đối tượng đó. Điều này có thể xảy ra lâu sau khi thư viện được viết.

  • Các biểu tượng gỡ lỗi: Bạn có thể muốn có một PDB riêng cho mỗi thư viện tĩnh hoặc bạn có thể muốn các biểu tượng gỡ lỗi được đặt trong các tệp đối tượng để chúng được đưa vào PDB cho DLL / EXE. Tài liệu Visual C ++ giải thích các tùy chọn cần thiết .

  • RTTI: Bạn có thể kết thúc với nhiều type_infođối tượng cho cùng một lớp nếu bạn liên kết một thư viện tĩnh duy nhất thành nhiều DLL. Nếu chương trình của bạn giả định rằng đó type_infolà dữ liệu "đơn lẻ" và sử dụng &typeid()hoặc type_info::before(), bạn có thể nhận được kết quả không mong muốn và đáng ngạc nhiên.


23
Đối với quan điểm về singletons, đừng quên rằng một DLL có thể được tải nhiều lần (cùng phiên bản hoặc phiên bản mulitple) và vẫn không có bảo đảm đơn lẻ.
Orion Adrian

Điểm bổ sung về loại bỏ mã không được ước tính: Các cuộc gọi được thực hiện với DLL cũng yêu cầu một cuộc gọi thực tế để buộc tải DLL được tham chiếu. Thêm nó làm tài liệu tham khảo, nhưng sau đó không bao gồm bất kỳ cuộc gọi nào tham chiếu, nó vẫn sẽ mang lại cho bạn kết quả tương tự như có một thư mục tĩnh không gọi bất cứ thứ gì. Sự khác biệt duy nhất là những gì thực sự tàu. Trong cả hai trường hợp, các hàm tạo và phá hủy tĩnh đều không kích hoạt.
Orion Adrian

@ bk1e Điều đó không nên xảy ra. .a sẽ luôn chứa tất cả các biểu tượng được tạo. Khi được liên kết tĩnh vào ứng dụng của bạn, chỉ có những biểu tượng được sử dụng mới được liên kết.
Miles Rout

62

Một lib là một đơn vị mã được gói trong ứng dụng của bạn.

Một dll là một đơn vị độc lập của mã thực thi. Nó chỉ được tải trong quá trình khi một cuộc gọi được thực hiện trong mã đó. Một dll có thể được sử dụng bởi nhiều ứng dụng và được tải trong nhiều quy trình, trong khi vẫn chỉ có một bản sao của mã trên ổ cứng.

Ưu điểm của DLL : có thể được sử dụng để tái sử dụng / chia sẻ mã giữa một số sản phẩm; tải trong bộ nhớ quá trình theo yêu cầu và có thể được tải khi không cần thiết; có thể được nâng cấp độc lập với phần còn lại của chương trình.

Nhược điểm của DLL : tác động hiệu suất của việc tải dll và khởi động lại mã; vấn đề về phiên bản ("dll hell")

Lib thuận : không có tác động hiệu suất vì mã luôn được tải trong quá trình và không bị từ chối; không có vấn đề về phiên bản.

Nhược điểm của Lib : thực thi / xử lý "phình to" - tất cả các mã nằm trong tệp thực thi của bạn và được tải khi bắt đầu quá trình; không sử dụng lại / chia sẻ - mỗi sản phẩm có bản sao mã riêng.


Việc khởi động lại cũng có thể được thực hiện khi xây dựng bằng rebase.exe hoặc bằng cách chuyển tùy chọn / BASE cho link.exe. Điều này có hiệu quả hay không phụ thuộc vào việc có bất kỳ xung đột không gian địa chỉ bất ngờ nào trong thời gian chạy hay không.
bk1e

24

Bên cạnh ý nghĩa kỹ thuật của thư viện tĩnh so với thư viện động (tệp tĩnh đóng gói mọi thứ trong một thư viện nhị phân lớn so với thư viện động cho phép chia sẻ mã giữa một số tệp thực thi khác nhau), còn có ý nghĩa pháp lý .

Ví dụ: nếu bạn đang sử dụng mã được cấp phép LGPL và bạn liên kết tĩnh với thư viện LGPL (và do đó tạo một nhị phân lớn), mã của bạn sẽ tự động trở thành Mã nguồn mở ( miễn phí như tự do) . Nếu bạn liên kết với các đối tượng được chia sẻ, thì bạn chỉ cần LGPL các cải tiến / sửa lỗi mà bạn thực hiện cho chính thư viện LGPL.

Điều này trở thành một vấn đề quan trọng hơn nhiều nếu bạn quyết định cách biên dịch các ứng dụng di động chẳng hạn (trong Android bạn có lựa chọn tĩnh so với động, trong iOS bạn không - nó luôn tĩnh).


23

Các chương trình C ++ được xây dựng theo hai giai đoạn

  1. Biên dịch - tạo mã đối tượng (.obj)
  2. Liên kết - tạo mã thực thi (.exe hoặc.)

Thư viện tĩnh (.lib) chỉ là một gói các tệp .obj và do đó không phải là một chương trình hoàn chỉnh. Nó đã không trải qua giai đoạn thứ hai (liên kết) để xây dựng một chương trình. Mặt khác, các DLL giống như exe và do đó là các chương trình hoàn chỉnh.

Nếu bạn xây dựng thư viện tĩnh, nó chưa được liên kết và do đó người tiêu dùng của thư viện tĩnh của bạn sẽ phải sử dụng cùng trình biên dịch mà bạn đã sử dụng (nếu bạn sử dụng g ++, họ sẽ phải sử dụng g ++).

Thay vào đó, nếu bạn xây dựng một dll (và xây dựng nó một cách chính xác ), bạn đã xây dựng một chương trình hoàn chỉnh mà tất cả người tiêu dùng có thể sử dụng, bất kể họ đang sử dụng trình biên dịch nào. Tuy nhiên, có một số hạn chế khi xuất từ ​​dll, nếu muốn tương thích trình biên dịch chéo.


1
Tin mới đối với tôi. Có những hạn chế nào với trình biên dịch chéo khi sử dụng DLL? Việc lập trình viên xây dựng mà không cần cùng một chuỗi công cụ có vẻ như là một điểm cộng rất lớn cho DLL
Dan

1
Câu trả lời này là thông tin. Thêm cảnh báo nhỏ: consumers of your static library will have to use the same compiler that you usednếu thư viện tĩnh sử dụng thư viện C ++, chẳng hạn như #include <iostream>.
Truthadjustr

người ta không thể sử dụng một dll c ++ trừ khi sử dụng cùng một trình biên dịch (vì không có c ++ abi tiêu chuẩn, các ký hiệu được đọc theo nhiều cách khác nhau). Cả dll và mô đun máy khách phải sử dụng cùng một trình biên dịch và cùng cài đặt bản dựng
kcris

19

Tạo thư viện tĩnh

$$:~/static [32]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/static [33]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/static [34]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/static [35]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/static [36]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/static [37]> cat makefile
hello: hello.o libtest.a
        cc -o hello hello.o -L. -ltest
hello.o: hello.c
        cc -c hello.c -I`pwd`
libtest.a:foo.o foo2.o
        ar cr libtest.a foo.o foo2.o
foo.o:foo.c
        cc -c foo.c
foo2.o:foo.c
        cc -c foo2.c
clean:
        rm -f foo.o foo2.o libtest.a hello.o

$$:~/static [38]>

tạo một thư viện động

$$:~/dynamic [44]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/dynamic [45]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/dynamic [46]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/dynamic [47]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/dynamic [48]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/dynamic [49]> cat makefile
hello:hello.o libtest.sl
        cc -o hello hello.o -L`pwd` -ltest
hello.o:
        cc -c -b hello.c -I`pwd`
libtest.sl:foo.o foo2.o
        cc -G -b -o libtest.sl foo.o foo2.o
foo.o:foo.c
        cc -c -b foo.c
foo2.o:foo.c
        cc -c -b foo2.c
clean:
        rm -f libtest.sl foo.o foo

2.o hello.o
$$:~/dynamic [50]>

13

Một thư viện tĩnh được biên dịch vào máy khách. Một .lib được sử dụng tại thời gian biên dịch và nội dung của thư viện trở thành một phần của việc thực thi tiêu thụ.

Một thư viện động được tải trong thời gian chạy và không được biên dịch vào tệp thực thi của máy khách. Các thư viện động linh hoạt hơn vì nhiều tệp thực thi của máy khách có thể tải DLL và sử dụng chức năng của nó. Điều này cũng giữ cho kích thước tổng thể và khả năng duy trì của mã khách hàng của bạn ở mức tối thiểu.


13

Bạn nên suy nghĩ cẩn thận về những thay đổi theo thời gian, phiên bản, tính ổn định, khả năng tương thích, v.v.

Nếu có hai ứng dụng sử dụng mã được chia sẻ, bạn có muốn buộc các ứng dụng đó thay đổi cùng nhau không, trong trường hợp chúng cần tương thích với nhau? Sau đó sử dụng dll. Tất cả các exe sẽ được sử dụng cùng một mã.

Hoặc bạn muốn cách ly chúng với nhau, để bạn có thể thay đổi cái này và tự tin rằng bạn đã không phá vỡ cái kia. Sau đó sử dụng lib tĩnh.

DLL địa ngục là khi bạn có thể NÊN sử dụng lib tĩnh, nhưng thay vào đó bạn đã sử dụng một dll, và không phải tất cả các exes đều có thể kết hợp với nó.


9

Một thư viện tĩnh phải được liên kết vào tệp thực thi cuối cùng; nó trở thành một phần của thực thi và đi theo nó bất cứ nơi nào nó đi. Một thư viện động được tải mỗi khi tệp thực thi được thực thi và tách biệt với tệp thực thi dưới dạng tệp DLL.

Bạn sẽ sử dụng DLL khi bạn muốn có thể thay đổi chức năng do thư viện cung cấp mà không phải liên kết lại tệp thực thi (chỉ cần thay thế tệp DLL, mà không phải thay thế tệp thực thi).

Bạn sẽ sử dụng thư viện tĩnh bất cứ khi nào bạn không có lý do để sử dụng thư viện động.


Bạn cũng có thể sử dụng DLL khi nhiều ứng dụng khác sử dụng cùng chức năng - điều này có thể làm giảm dấu chân.
Tim

Ngoài ra, việc mở rộng khái niệm ban đầu của bạn, kiến ​​trúc "trình cắm" nơi bạn muốn cho phép chức năng được thêm / chưa biết sau này mà không phải xây dựng lại hoặc phát hành lại chỉ có thể được thực hiện với các thư viện động.
Tim

8

Bài viết của Ulrich Drepper về " Cách viết thư viện chia sẻ " cũng là tài nguyên tốt, chi tiết cách tận dụng tốt nhất các thư viện dùng chung hoặc cái mà anh ta gọi là "Đối tượng chia sẻ động" (DSOs). Nó tập trung nhiều hơn vào các thư viện được chia sẻ ở định dạng nhị phân ELF , nhưng một số cuộc thảo luận cũng phù hợp với Windows DLL.


5

Đối với một cuộc thảo luận tuyệt vời về chủ đề này, hãy đọc bài viết này từ Sun.

Nó đi vào tất cả các lợi ích bao gồm có thể chèn các thư viện xen kẽ. Chi tiết hơn về can thiệp có thể được tìm thấy trong bài viết này ở đây .


4

Thực sự sự đánh đổi mà bạn đang thực hiện (trong một dự án lớn) đang trong thời gian tải ban đầu, các thư viện sẽ được liên kết lúc này hay lúc khác, quyết định phải đưa ra là liên kết sẽ đủ lâu để trình biên dịch cần cắn viên đạn và làm nó lên phía trước, hoặc trình liên kết động có thể làm điều đó khi tải.


3

Nếu thư viện của bạn sẽ được chia sẻ giữa một số tệp thực thi, thường có ý nghĩa làm cho nó động để giảm kích thước của các tệp thực thi. Nếu không, chắc chắn làm cho nó tĩnh.

Có một số nhược điểm của việc sử dụng một dll. Có thêm chi phí để tải và dỡ nó. Ngoài ra còn có một phụ thuộc bổ sung. Nếu bạn thay đổi dll để làm cho nó không tương thích với các tệp thực thi của bạn, chúng sẽ ngừng hoạt động. Mặt khác, nếu bạn thay đổi thư viện tĩnh, các tệp thực thi được biên dịch của bạn bằng phiên bản cũ sẽ không bị ảnh hưởng.


3

Nếu thư viện là tĩnh, thì tại thời điểm liên kết, mã được liên kết với tệp thực thi của bạn. Điều này làm cho khả năng thực thi của bạn lớn hơn (so với khi bạn đi theo tuyến đường động).

Nếu thư viện là động thì tại thời điểm liên kết tham chiếu đến các phương thức cần thiết được tích hợp vào tệp thực thi của bạn. Điều này có nghĩa là bạn phải gửi thư thực thi của bạn và thư viện động. Bạn cũng nên xem xét liệu quyền truy cập được chia sẻ vào mã trong thư viện có an toàn không, địa chỉ tải ưu tiên trong số các nội dung khác.

Nếu bạn có thể sống với thư viện tĩnh, hãy đi với thư viện tĩnh.


3

Chúng tôi sử dụng rất nhiều DLL (> 100) trong dự án của chúng tôi. Các DLL này có sự phụ thuộc lẫn nhau và do đó chúng tôi đã chọn thiết lập liên kết động. Tuy nhiên, nó có những nhược điểm sau:

  • khởi động chậm (> 10 giây)
  • DLL phải được phiên bản, vì các cửa sổ tải các mô-đun về tính duy nhất của tên. Các thành phần được viết riêng sẽ nhận được phiên bản sai của DLL (tức là phiên bản đã được tải thay vì bộ phân tán của chính nó)
  • Trình tối ưu hóa chỉ có thể tối ưu hóa trong ranh giới DLL. Ví dụ: trình tối ưu hóa cố gắng đặt dữ liệu và mã được sử dụng thường xuyên cạnh nhau, nhưng điều này sẽ không hoạt động trên các ranh giới DLL

Có lẽ một thiết lập tốt hơn là làm cho mọi thứ trở thành một thư viện tĩnh (và do đó bạn chỉ cần có một tệp thực thi). Điều này chỉ hoạt động nếu không có sự sao chép mã. Một thử nghiệm dường như ủng hộ giả định này, nhưng tôi không thể tìm thấy một trích dẫn chính thức của MSDN. Vì vậy, ví dụ tạo 1 exe với:

  • exe sử dụng shared_lib1, shared_lib2
  • shared_lib1 sử dụng shared_lib2
  • đã chia sẻ_lib2

Mã và các biến của shared_lib2 phải có mặt trong tệp thực thi được hợp nhất cuối cùng chỉ một lần. Bất cứ ai có thể hỗ trợ câu hỏi này?


Bạn không có ý định sử dụng một số chỉ thị tiền biên dịch theo một cách nào đó để tránh trùng lặp mã?
Paceman

Biên dịch trước Afaiac chỉ hoạt động trên cơ sở mỗi mô-đun (exe / dll / lib). Tiền biên dịch có nghĩa là chủ yếu để tăng tốc độ biên dịch mặc dù nó cũng ngăn chặn nhiều vùi trong một đơn vị biên dịch. Tuy nhiên bao gồm các vệ sĩ là cách tốt hơn để đạt được hiệu ứng này.
gast128

2

Thư viện tĩnh là kho lưu trữ chứa mã đối tượng cho thư viện, khi được liên kết vào một ứng dụng mà mã được biên dịch thành tệp thực thi. Các thư viện dùng chung khác nhau ở chỗ chúng không được biên dịch thành tệp thực thi. Thay vào đó, trình liên kết động tìm kiếm một số thư mục tìm kiếm (các) thư viện mà nó cần, sau đó tải nó vào bộ nhớ. Nhiều hơn một thực thi có thể sử dụng cùng một thư viện chia sẻ cùng một lúc, do đó làm giảm việc sử dụng bộ nhớ và kích thước thực thi. Tuy nhiên, sau đó có nhiều tệp để phân phối với tệp thực thi. Bạn cần đảm bảo rằng thư viện được cài đặt vào hệ thống sử dụng ở nơi mà trình liên kết có thể tìm thấy nó, liên kết tĩnh loại bỏ vấn đề này nhưng dẫn đến một tệp thực thi lớn hơn.


2

Nếu bạn làm việc trên các dự án nhúng hoặc các thư viện tĩnh nền tảng chuyên dụng là cách duy nhất để đi, thì nhiều khi chúng sẽ ít gặp rắc rối hơn để biên dịch vào ứng dụng của bạn. Cũng có các dự án và makefile bao gồm mọi thứ làm cho cuộc sống hạnh phúc hơn.


2

Tôi sẽ đưa ra một quy tắc chung rằng nếu bạn có một cơ sở mã lớn, tất cả được xây dựng trên các thư viện cấp thấp hơn (ví dụ: khung Utils hoặc Gui), mà bạn muốn phân vùng thành các thư viện dễ quản lý hơn, sau đó biến chúng thành các thư viện tĩnh. Các thư viện động không thực sự mua cho bạn bất cứ thứ gì và có ít bất ngờ hơn - ví dụ sẽ chỉ có một ví dụ về singletons.

Nếu bạn có một thư viện hoàn toàn tách biệt với phần còn lại của cơ sở mã (ví dụ: thư viện của bên thứ ba) thì hãy xem xét biến nó thành một dll. Nếu thư viện là LGPL, bạn có thể cần phải sử dụng dll do các điều kiện cấp phép.

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.