'Liên kết tĩnh' và 'liên kết động' nghĩa là gì?


229

Tôi thường nghe các thuật ngữ 'liên kết tĩnh' và 'liên kết động', thường liên quan đến mã được viết bằng C , C ++ hoặc C # . Họ là ai, chính xác họ đang nói về cái gì, và họ liên kết với nhau là gì?

Câu trả lời:


445

Có (trong hầu hết các trường hợp, chiết khấu mã được diễn giải) hai giai đoạn trong việc chuyển từ mã nguồn (những gì bạn viết) sang mã thực thi (những gì bạn chạy).

Đầu tiên là biên dịch biến mã nguồn thành các mô đun đối tượng.

Thứ hai, liên kết, là những gì kết hợp các mô-đun đối tượng với nhau để tạo thành một thực thi.

Sự khác biệt được tạo ra, trong số những thứ khác, cho phép các thư viện của bên thứ ba được bao gồm trong tệp thực thi của bạn mà không cần bạn nhìn thấy mã nguồn của chúng (chẳng hạn như thư viện để truy cập cơ sở dữ liệu, giao tiếp mạng và giao diện người dùng đồ họa) hoặc để biên dịch mã bằng các ngôn ngữ khác nhau ( C và mã lắp ráp chẳng hạn) và sau đó liên kết tất cả chúng lại với nhau.

Khi bạn liên kết tĩnh một tệp thành tệp thực thi, nội dung của tệp đó được bao gồm tại thời điểm liên kết. Nói cách khác, nội dung của tệp được chèn vật lý vào tệp thực thi mà bạn sẽ chạy.

Khi bạn liên kết động , một con trỏ tới tệp được liên kết trong (ví dụ tên tệp của tệp) được bao gồm trong tệp thực thi và nội dung của tệp đã nói không được đưa vào tại thời điểm liên kết. Chỉ khi bạn chạy tệp thực thi này, các tệp được liên kết động này mới được mua và chúng chỉ được mua vào bản sao trong bộ nhớ của tệp thực thi, chứ không phải tệp trên đĩa.

Về cơ bản, đây là một phương pháp liên kết hoãn lại. Có một phương thức thậm chí còn bị trì hoãn hơn (được gọi là ràng buộc muộn trên một số hệ thống) sẽ không mang lại tệp được liên kết động cho đến khi bạn thực sự cố gắng gọi một hàm trong nó.

Các tệp được liên kết tĩnh được 'khóa' để thực thi tại thời điểm liên kết để chúng không bao giờ thay đổi. Một tệp được liên kết động được tham chiếu bởi một tệp thực thi có thể thay đổi chỉ bằng cách thay thế tệp trên đĩa.

Điều này cho phép cập nhật chức năng mà không phải liên kết lại mã; trình tải lại liên kết lại mỗi khi bạn chạy nó.

Điều này vừa tốt vừa xấu - một mặt, nó cho phép cập nhật và sửa lỗi dễ dàng hơn, mặt khác nó có thể dẫn đến các chương trình ngừng hoạt động nếu các bản cập nhật không tương thích - đôi khi điều này chịu trách nhiệm cho "địa ngục DLL" đáng sợ mà một số người đề cập đến việc các ứng dụng có thể bị phá vỡ nếu bạn thay thế một thư viện được liên kết động bằng một thư viện không tương thích (nhân viên phát triển làm điều này sẽ bị săn lùng và trừng phạt nghiêm khắc).


Như một ví dụ , chúng ta hãy nhìn vào trường hợp của một người sử dụng biên dịch của họ main.ctập tin cho tĩnh và liên kết động.

Phase     Static                    Dynamic
--------  ----------------------    ------------------------
          +---------+               +---------+
          | main.c  |               | main.c  |
          +---------+               +---------+
Compile........|.........................|...................
          +---------+ +---------+   +---------+ +--------+
          | main.o  | | crtlib  |   | main.o  | | crtimp |
          +---------+ +---------+   +---------+ +--------+
Link...........|..........|..............|...........|.......
               |          |              +-----------+
               |          |              |
          +---------+     |         +---------+ +--------+
          |  main   |-----+         |  main   | | crtdll |
          +---------+               +---------+ +--------+
Load/Run.......|.........................|..........|........
          +---------+               +---------+     |
          | main in |               | main in |-----+
          | memory  |               | memory  |
          +---------+               +---------+

Bạn có thể thấy trong trường hợp tĩnh rằng chương trình chính và thư viện thời gian chạy C được liên kết với nhau tại thời điểm liên kết (bởi các nhà phát triển). Vì người dùng thường không thể liên kết lại tệp thực thi, nên họ bị mắc kẹt với hành vi của thư viện.

Trong trường hợp động, chương trình chính được liên kết với thư viện nhập thời gian chạy C (một cái gì đó khai báo những gì trong thư viện động nhưng không thực sự xác định nó). Điều này cho phép trình liên kết liên kết mặc dù mã thực tế bị thiếu.

Sau đó, tại thời gian chạy, trình tải hệ điều hành thực hiện liên kết muộn chương trình chính với DLL thời gian chạy C (thư viện liên kết động hoặc thư viện chia sẻ hoặc danh pháp khác).

Chủ sở hữu của thời gian chạy C có thể thả một DLL mới bất cứ lúc nào để cung cấp các bản cập nhật hoặc sửa lỗi. Như đã nêu trước đó, điều này có cả ưu điểm và nhược điểm.


11
Vui lòng sửa lỗi cho tôi nếu tôi sai, nhưng trên Windows, phần mềm có xu hướng bao gồm các thư viện riêng với cài đặt, ngay cả khi chúng được liên kết động. Trên nhiều hệ thống Linux có trình quản lý gói, nhiều thư viện được liên kết động ("đối tượng chia sẻ") thực sự được chia sẻ giữa các phần mềm.
Paul Fisher

6
@PaulF: những thứ như các điều khiển chung của Windows, DirectX, .NET, v.v. rất nhiều ứng dụng trong khi trên Linux, bạn có xu hướng sử dụng apt hoặc yum hoặc một cái gì đó tương tự để quản lý các phụ thuộc - vì vậy bạn hiểu đúng . Giành ứng dụng gửi mã riêng của họ dưới dạng DLL có xu hướng không chia sẻ chúng.
paxdiablo

31
Có một nơi đặc biệt dành riêng trong vòng địa ngục thứ chín cho những người cập nhật DLL của họ và phá vỡ tính tương thích ngược. Có, nếu các giao diện biến mất hoặc được sửa đổi, thì liên kết động sẽ rơi vào một đống. Đó là lý do tại sao nó không nên được thực hiện. Bằng mọi cách, hãy thêm hàm2 () vào DLL của bạn nhưng không thay đổi hàm () nếu mọi người đang sử dụng nó. Cách tốt nhất để xử lý đó là mã hóa lại hàm () theo cách mà nó gọi hàm2 (), nhưng không thay đổi chữ ký của hàm ().
paxdiablo

1
@Paul Fisher, tôi biết điều này là muộn nhưng ... thư viện gửi Windows DLL không phải là thư viện đầy đủ, nó chỉ là một nhóm sơ khai cho trình liên kết biết DLL chứa gì. Trình liên kết sau đó có thể tự động đưa thông tin vào .exe để tải DLL và các ký hiệu không hiển thị dưới dạng không xác định.
Đánh dấu tiền chuộc

1
@Santropedro, bạn đã đúng về tất cả các ý nghĩa của tên lib, nhập và DLL. Hậu tố chỉ là quy ước vì vậy đừng đọc quá nhiều vào đó (ví dụ, DLL có thể có .dllhoặc .somở rộng) - hãy nghĩ câu trả lời là giải thích các khái niệm thay vì mô tả chính xác. Và, theo văn bản, đây là một ví dụ hiển thị liên kết tĩnh và động cho chỉ các tệp thời gian chạy C, vì vậy, đó là những gì `crt chỉ ra trong tất cả chúng.
paxdiablo

221

Tôi nghĩ rằng một câu trả lời tốt cho câu hỏi này nên giải thích liên kết gì .

Khi bạn biên dịch một số mã C (ví dụ), nó được dịch sang ngôn ngữ máy. Chỉ là một chuỗi byte, khi chạy, làm cho bộ xử lý thêm, trừ, so sánh, "goto", đọc bộ nhớ, ghi bộ nhớ, loại đó. Công cụ này được lưu trữ trong các tệp đối tượng (.o).

Bây giờ, một thời gian dài trước đây, các nhà khoa học máy tính đã phát minh ra thứ "chương trình con" này. Thực thi-this-chunk-of-code-and-return-here. Không quá lâu trước khi họ nhận ra rằng các chương trình con hữu ích nhất có thể được lưu trữ ở một nơi đặc biệt và được sử dụng bởi bất kỳ chương trình nào cần chúng.

Bây giờ trong những ngày đầu, các lập trình viên sẽ phải bấm vào địa chỉ bộ nhớ mà các chương trình con này được đặt tại. Một cái gì đó như CALL 0x5A62. Điều này thật tẻ nhạt và có vấn đề nếu những địa chỉ bộ nhớ đó cần phải được thay đổi.

Vì vậy, quá trình đã được tự động. Bạn viết một chương trình gọi printf()và trình biên dịch không biết địa chỉ bộ nhớ của printf. Vì vậy, trình biên dịch chỉ cần ghi CALL 0x0000và thêm một ghi chú vào tệp đối tượng có nội dung "phải thay thế 0x0000 này bằng vị trí bộ nhớ của printf ".

Liên kết tĩnh có nghĩa là chương trình liên kết (GNU được gọi là ld ) thêm printfmã máy trực tiếp vào tệp thực thi của bạn và thay đổi 0x0000 thành địa chỉ của printf. Điều này xảy ra khi thực thi của bạn được tạo ra.

Liên kết động có nghĩa là bước trên không xảy ra. Tệp thực thi vẫn có một ghi chú cho biết "phải thay thế 0x000 bằng vị trí bộ nhớ của printf". Trình tải của hệ điều hành cần tìm mã printf, tải nó vào bộ nhớ và sửa địa chỉ CALL, mỗi khi chương trình được chạy .

Các chương trình thường gọi một số chức năng sẽ được liên kết tĩnh (các chức năng thư viện tiêu chuẩn như printfthường được liên kết tĩnh) và các chức năng khác được liên kết động. Những cái tĩnh "trở thành một phần" của thực thi và những cái động "tham gia" khi thực thi được chạy.

Có những ưu điểm và nhược điểm của cả hai phương pháp và có sự khác biệt giữa các hệ điều hành. Nhưng vì bạn không hỏi, tôi sẽ kết thúc chuyện này ở đây.


4
Tôi cũng vậy, tuy nhiên tôi chỉ được chọn 1 câu trả lời.
UnkwnTech

1
Artelius, tôi đang tìm hiểu sâu về lời giải thích của bạn về cách những thứ cấp thấp điên rồ này hoạt động. vui lòng trả lời những cuốn sách chúng ta phải đọc để có kiến ​​thức chuyên sâu về những điều trên. cảm ơn bạn.
mahesh

1
Xin lỗi, tôi không thể đề xuất bất kỳ cuốn sách. Bạn nên học ngôn ngữ lắp ráp trước. Sau đó Wikipedia có thể đưa ra một cái nhìn tổng quan về các chủ đề đó. Bạn có thể muốn xem ldtài liệu GNU .
Artelius

31

Các thư viện liên kết tĩnh được liên kết tại thời gian biên dịch. Các thư viện liên kết động được tải vào thời gian chạy. Liên kết tĩnh mang bit thư viện vào tệp thực thi của bạn. Liên kết động chỉ bakes trong một tham chiếu đến thư viện; các bit cho thư viện động tồn tại ở nơi khác và có thể được hoán đổi sau đó.


16

Bởi vì không có bài viết nào ở trên thực sự chỉ ra cách liên kết tĩnh một cái gì đó và thấy rằng bạn đã làm đúng, vì vậy tôi sẽ giải quyết vấn đề này:

Một chương trình C đơn giản

#include <stdio.h>

int main(void)
{
    printf("This is a string\n");
    return 0;
}

Tự động liên kết chương trình C

gcc simpleprog.c -o simpleprog

Và chạy filetrên nhị phân:

file simpleprog 

Và điều đó sẽ cho thấy nó được liên kết động một cái gì đó dọc theo dòng:

"Simpleprog: ELF 64-bit LSB thực thi, x86-64, phiên bản 1 (SYSV), được liên kết động (sử dụng libs được chia sẻ), cho GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c75

Thay vào đó, hãy để chúng tôi liên kết tĩnh chương trình lần này:

gcc simpleprog.c -static -o simpleprog

Chạy tệp trên nhị phân được liên kết tĩnh này sẽ hiển thị:

file simpleprog 

"Simpleprog: ELF 64-bit LSB thực thi, x86-64, phiên bản 1 (GNU / Linux), được liên kết tĩnh, đối với GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644"

Và bạn có thể thấy nó được liên kết tĩnh hạnh phúc. Tuy nhiên, thật đáng buồn là không phải tất cả các thư viện đều đơn giản để liên kết tĩnh theo cách này và có thể yêu cầu nỗ lực mở rộng bằng cách sử dụng libtoolhoặc liên kết mã đối tượng và thư viện C bằng tay.

May mắn thay, nhiều thư viện C nhúng như muslcung cấp các tùy chọn liên kết tĩnh cho gần như tất cả nếu không phải tất cả các thư viện của họ.

Bây giờ stracenhị phân bạn đã tạo và bạn có thể thấy rằng không có thư viện nào được truy cập trước khi chương trình bắt đầu:

strace ./simpleprog

Bây giờ so sánh với đầu ra của stracechương trình được liên kết động và bạn sẽ thấy rằng bước của phiên bản được liên kết tĩnh ngắn hơn nhiều!


2

(Tôi không biết C # nhưng thật thú vị khi có khái niệm liên kết tĩnh cho ngôn ngữ VM)

Liên kết động liên quan đến việc biết cách tìm một chức năng cần thiết mà bạn chỉ có một tài liệu tham khảo từ chương trình của mình. Bạn chạy ngôn ngữ hoặc hệ điều hành tìm kiếm một đoạn mã trên hệ thống tập tin, mạng hoặc bộ đệm mã được biên dịch, khớp với tham chiếu và sau đó thực hiện một số biện pháp để tích hợp nó vào hình ảnh chương trình của bạn trong bộ nhớ, như di dời. Tất cả đều được thực hiện trong thời gian chạy. Nó có thể được thực hiện bằng tay hoặc bằng trình biên dịch. Có khả năng cập nhật với nguy cơ gây rối (cụ thể là địa ngục DLL).

Liên kết tĩnh được thực hiện tại thời điểm biên dịch, bạn báo cho trình biên dịch biết tất cả các phần chức năng và hướng dẫn nó tích hợp chúng. Không có tìm kiếm, không có sự mơ hồ, không có khả năng cập nhật mà không cần biên dịch lại. Tất cả các phụ thuộc của bạn là một vật lý với hình ảnh chương trình của bạn.

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.