پرش به محتویات

102.3 - مدیریت کتابخانه‌های مشترک (Shared Libraries)

اهداف یادگیری

در این فصل با موارد زیر آشنا می‌شوید:

  • شناسایی کتابخانه‌های مشترک
  • شناسایی محل‌های معمول کتابخانه‌های سیستم
  • بارگذاری کتابخانه‌های مشترک

کلیدواژه‌ها

ldd, ldconfig, /etc/ld.so.conf, LD_LIBRARY_PATH


Linking (لینک کردن)

هنگام نوشتن برنامه‌ها، از کتابخانه‌ها استفاده می‌کنیم. برای مثال، اگر نیاز به خواندن متن از ورودی استاندارد دارید، باید یک کتابخانه که این را فراهم می‌کند لینک کنید. لینک کردن دو شکل دارد:

  • Static Linking (لینک ایستا): کتابخانه‌ها را به برنامه اجرایی خود اضافه می‌کنید. در این روش، حجم برنامه بزرگ است زیرا همه کتابخانه‌های مورد نیاز را دارد. یک مزیت خوب این است که برنامه می‌تواند بدون وابستگی به برنامه‌ها/کتابخانه‌های دیگر اجرا شود.
  • Dynamic Linking (لینک پویا): فقط اعلام می‌کنید که برنامه به کتابخانه‌های خاصی نیاز دارد. این روش برنامه را کوچک‌تر می‌کند اما باید کتابخانه‌ها را جداگانه نصب کنید. این برنامه‌ها را امن‌تر می‌کند (زیرا کتابخانه‌ها می‌توانند به‌صورت مرکزی به‌روزرسانی شوند، و پیشرفته‌تر هر بهبود در کتابخانه کل برنامه را بهبود می‌بخشد)، و کوچک‌تر.

کتابخانه‌های پویا لینوکس نام‌هایی مانند libLIBNAME.so.VERSION دارند و در مکان‌هایی مانند /lib*/ و /usr/lib*/ قرار دارند. در ویندوز، آنها را Dynamic Linked Libraries (DLLs) می‌نامیم.

نکته

Dynamic linking همچنین shared libraries نامیده می‌شود زیرا همه برنامه‌ها یک کتابخانه مشترک را که جداگانه نصب شده است به اشتراک می‌گذارند.


کتابخانه‌های مورد نیاز من چیست

کتابخانه‌های مربوط به ابزارهای سیستم در /lib و /lib64 (برای کتابخانه‌های 32بیتی و 64بیتی) نصب می‌شوند و کتابخانه‌های نصب شده توسط نرم‌افزارهای دیگر در /usr/lib و /usr/lib64 قرار دارند.

ldd

دستور ldd به شما کمک می‌کند پیدا کنید:

  • آیا برنامه به صورت پویا یا ایستا لینک شده است
  • کتابخانه‌های مورد نیاز برنامه چیست

بیایید به دو فایل نگاه کنیم:

[jadi@fedora ~]$ ldd /sbin/ldconfig
    not a dynamic executable

[jadi@fedora ~]$ ldd /bin/ls
    linux-vdso.so.1 (0x00007ffdd53eb000)
    libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f5cbc7b0000)
    libcap.so.2 => /lib64/libcap.so.2 (0x00007f5cbc7a6000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f5cbc5a5000)
    libpcre2-8.so.0 => /lib64/libpcre2-8.so.0 (0x00007f5cbc50f000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f5cbc813000)

همانطور که می‌بینید، ldd به ما می‌گوید که /sbin/ldconfig به صورت پویا لینک نشده است اما کتابخانه‌های مورد نیاز /bin/ls را نشان می‌دهد.

اگر در حال نوشتن برنامه‌ای هستید و از توابع udev استفاده می‌کنید، از کتابخانه‌ای به نام libudev.so.1 درخواست خواهید کرد. اما یک توزیع لینوکس ممکن است نسخه کتابخانه udev خود را libudev.so.1.4.0 بنامد. چگونه این مشکل را حل کنیم؟ پاسخ symbolic links است؛ درباره آنها در فصل‌های بعدی بیشتر یاد خواهید گرفت اما به اختصار، یک نام نمادین نام جدیدی برای همان فایل است.

ابتدا محل libudev.so.1 روی سیستم خود را پیدا می‌کنم:

# locate libudev.so.1
/lib/i386-linux-gnu/libudev.so.1
/usr/lib64/libudev.so.1.7.3

سپس فایل را چک می‌کنم:

# ls -la /lib/i386-linux-gnu/libudev.so.1
lrwxrwxrwx 1 root root    16 Nov 13 23:05 /lib/i386-linux-gnu/libudev.so.1 -> libudev.so.1.4.0

همانطور که می‌بینید، این یک symbolic link است که به نسخه libudev نصب شده من (1.4.0) اشاره می‌کند بنابراین حتی اگر نرم‌افزاری بگوید نیاز به libudev.so.1 دارد، سیستم من از libudev.so.1.4.0 استفاده خواهد کرد.

پیکربندی و cache کتابخانه‌های پویا

مانند اکثر ابزارهای دیگر لینوکس، linking پویا نیز با یک فایل پیکربندی متنی پیکربندی می‌شود. این در /etc/ld.so.conf قرار دارد. روی یک سیستم Ubuntu فقط به فایل‌های پیکربندی دیگر در /etc/ld.so.conf.d/ اشاره می‌کند اما همه آن خطوط می‌توانند در فایل اصلی نیز گنجانده شوند:

[jadi@fedora ~]$ cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
[jadi@fedora ~]$ ls /etc/ld.so.conf.d/
llvm13-x86_64.conf  pipewire-jack-x86_64.conf
[jadi@fedora ~]$ cat /etc/ld.so.conf.d/llvm13-x86_64.conf
/usr/lib64/llvm13/lib

دستور ldconfig همه این فایل‌ها را پردازش می‌کند تا بارگذاری کتابخانه‌ها سریع‌تر شود. این دستور ld.so.cache را ایجاد می‌کند تا فایل‌هایی که باید به صورت پویا بارگذاری و لینک شوند را پیدا کند.

نکته

اگر ld.so.conf (یا زیرشاخه‌ها) را تغییر دهید، باید ldconfig را اجرا کنید. با سوئیچ -v امتحان کنید تا پیشرفت/داده را ببینید.

برای پایان این بخش، بیایید ldconfig را با سوئیچ -p اجرا کنیم تا ببینیم چه چیزی در ld.so.cache ذخیره شده است:

[jadi@fedora ~]$ ldconfig -p | head
1373 libs found in cache `/etc/ld.so.cache'
    libzstd.so.1 (libc6,x86-64) => /lib64/libzstd.so.1
    libzmf-0.0.so.0 (libc6,x86-64) => /lib64/libzmf-0.0.so.0
    libzip.so.5 (libc6,x86-64) => /lib64/libzip.so.5
    libzhuyin.so.13 (libc6,x86-64) => /lib64/libzhuyin.so.13
    libzck.so.1 (libc6,x86-64) => /lib64/libzck.so.1
    libz.so.1 (libc6,x86-64) => /lib64/libz.so.1
    libyui.so.15 (libc6,x86-64) => /lib64/libyui.so.15
    libyui-mga.so.15 (libc6,x86-64) => /lib64/libyui-mga.so.15
    libyelp.so.0 (libc6,x86-64) => /lib64/libyelp.so.0

همانطور که می‌بینید، این فایل به کرنل می‌گوید که اگر کسی از libzstd.so.1 درخواست کند، فایل /lib64/libzstd.so.1 باید بارگذاری و استفاده شود.

سیستم عامل کتابخانه‌های پویا را از کجا پیدا می‌کند

وقتی برنامه‌ای نیاز به یک shared library دارد، سیستم فایل‌ها را به این ترتیب جستجو می‌کند:

  1. متغیر محیطی LD_LIBRARY_PATH
  2. PATH برنامه
  3. /etc/ld.so.conf (که ممکن است فایل‌های بیشتری از /etc/ld.so.conf.d/ در ابتدای یا انتهای خود بارگذاری کند)
  4. /lib/, /lib64/, /usr/lib/, /usr/lib64/

در برخی موارد، ممکن است نیاز به override کردن کتابخانه‌های پیش‌فرض سیستم داشته باشید. برخی مثال‌ها:

  • اجرای نرم‌افزار قدیمی که نیاز به نسخه قدیمی کتابخانه دارد.
  • توسعه یک shared library و می‌خواهید آن را بدون نصب تست کنید
  • اجرای برنامه خاصی (مثلاً از opt) که نیاز به دسترسی به کتابخانه‌های خود دارد

در این موارد، می‌توانید متغیر محیطی LD_LIBRARY_PATH را به کتابخانه مورد نیاز خود اشاره دهید و سپس برنامه خود را اجرا کنید. یک لیست جدا شده با کولون (:) از دایرکتوری‌ها به برنامه شما می‌گوید کجا برای کتابخانه‌های مورد نیاز جستجو کند قبل از چک کردن کتابخانه‌ها در /etc/ld.so.cache.

برای مثال، اگر این دستور را بدهید:

export LD_LIBRARY_PATH=/usr/lib/myoldlibs:/home/jadi/lpic/libs/

تمرین‌ها

تمرین 1: بررسی وابستگی‌های برنامه

ldd /bin/ls

تمرین 2: بررسی cache کتابخانه‌ها

ldconfig -p | grep libz

تمرین 3: استفاده از LD_LIBRARY_PATH

export LD_LIBRARY_PATH=/custom/libs
ldd /path/to/program

نکات کلیدی برای آزمون

  • تفاوت static و dynamic linking
  • استفاده از ldd برای بررسی وابستگی‌ها
  • نقش ldconfig و ld.so.cache
  • ترتیب جستجوی کتابخانه‌ها
  • استفاده از LD_LIBRARY_PATH

مثال:

ldd /sbin/ldconfig
# not a dynamic executable

ldd /bin/ls
    libselinux.so.1 => /lib64/libselinux.so.1
    libc.so.6 => /lib64/libc.so.6
    ...

گاهی نسخه‌های مختلف یک کتابخانه با نام‌های متفاوت وجود دارند. برای سازگاری، از لینک‌های نمادین استفاده می‌شود.

مثال:

ls -la /lib/i386-linux-gnu/libudev.so.1
lrwxrwxrwx 1 root root 16 Nov 13 23:05 libudev.so.1 -> libudev.so.1.4.0

نکته

حتی اگر برنامه‌ای به libudev.so.1 نیاز داشته باشد، سیستم نسخه نصب‌شده (libudev.so.1.4.0) را استفاده می‌کند.


پیکربندی و کش کتابخانه‌ها

  • فایل پیکربندی: /etc/ld.so.conf
  • در اوبونتو: شامل فایل‌های /etc/ld.so.conf.d/

مثال:

cat /etc/ld.so.conf
include ld.so.conf.d/*.conf

دستور ldconfig

  • پردازش فایل‌های پیکربندی
  • ایجاد کش (ld.so.cache) برای دسترسی سریع‌تر به کتابخانه‌ها

مشاهده کش:

ldconfig -p | head

هشدار

پس از تغییر فایل‌های پیکربندی باید ldconfig اجرا شود تا کش به‌روزرسانی شود.


مسیر جستجوی کتابخانه‌ها

ترتیب جستجو:

  1. متغیر محیطی LD_LIBRARY_PATH
  2. مسیر برنامه
  3. فایل /etc/ld.so.conf و زیرشاخه‌های آن
  4. مسیرهای پیش‌فرض: /lib, /lib64, /usr/lib, /usr/lib64

مثال استفاده از LD_LIBRARY_PATH:

export LD_LIBRARY_PATH=/usr/lib/myoldlibs:/home/user/libs/

کاربرد

این روش برای تست نسخه‌های قدیمی یا توسعه کتابخانه‌ها بدون نصب دائمی استفاده می‌شود.


بارگذاری پویا (Dynamic Loading)

برنامه‌ها توسط Dynamic Loader اجرا می‌شوند (معمولاً ld-linux).

یافتن Loader:

locate ld-linux
/usr/lib64/ld-linux-x86-64.so.2

اجرای برنامه با Loader:

/usr/lib64/ld-linux-x86-64.so.2 /usr/bin/ls

ترفند

حتی اگر فایل اجرایی بیت اجرا (x) نداشته باشد، می‌توانید آن را با ld-linux اجرا کنید!


تمرین‌های عملی

تمرین 1: بررسی وابستگی‌ها

ldd /bin/ls

تمرین 2: مشاهده کش کتابخانه‌ها

ldconfig -p | less

تمرین 3: تغییر مسیر کتابخانه‌ها

export LD_LIBRARY_PATH=/home/user/libs
./myprogram

تمرین 4: اجرای برنامه با Loader

/usr/lib64/ld-linux-x86-64.so.2 ./myprogram

خلاصه

در این فصل یاد گرفتیم:

  • تفاوت لینک ایستا و پویا
  • محل ذخیره کتابخانه‌های سیستمی و نرم‌افزاری
  • استفاده از دستور ldd برای بررسی وابستگی‌ها
  • نقش لینک‌های نمادین در سازگاری نسخه‌ها
  • پیکربندی و کش کتابخانه‌ها با ld.so.conf و ldconfig
  • ترتیب جستجوی کتابخانه‌ها در سیستم
  • استفاده از LD_LIBRARY_PATH برای override کتابخانه‌ها
  • اجرای برنامه‌ها با Dynamic Loader (ld-linux)

نکات کلیدی برای آزمون

  • تفاوت Static و Dynamic Linking
  • مسیرهای /lib, /usr/lib برای کتابخانه‌ها
  • دستورهای ldd و ldconfig
  • نقش LD_LIBRARY_PATH
  • مفهوم Symbolic Links برای کتابخانه‌ها