服务器之家:专注于服务器技术及软件下载分享
分类导航

Linux|Centos|Ubuntu|系统进程|Fedora|注册表|Bios|Solaris|Windows7|Windows10|Windows11|windows server|

服务器之家 - 服务器系统 - Linux - Linux的ldconfig命令的代码原理:ldconfig如何从指定目录中查找动态库文件信息?

Linux的ldconfig命令的代码原理:ldconfig如何从指定目录中查找动态库文件信息?

2023-12-27 17:04未知服务器之家 Linux

ldconfig的代码中,search_dir()函数的关键内容[1] 这部分关键代码,负责查找指定目录的动态库文件,与系统已有的ld.so.cache的记录对比,如果有变化或系统缓存记录中不存在,则将当前动态库的信息加入到缓存信息链表。 /* Add libra

Linux的ldconfig命令的代码原理:ldconfig如何从指定目录中查找动态库文件信息?

ldconfig的代码中,search_dir()函数的关键内容[1]

这部分关键代码,负责查找指定目录的动态库文件,与系统已有的ld.so.cache的记录对比,如果有变化或系统缓存记录中不存在,则将当前动态库的信息加入到缓存信息链表。

/* Add library to list.  将动态库信息添加到链表*/
      struct dlib_entry *dlib_ptr;
      for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
 {
   /* Is soname already in list?  检查SONAME记录在链表中是否已存在,避免同一个库重复添加多次*/
   if (strcmp (dlib_ptr->soname, soname) == 0)
     {
       /* Prefer a file to a link, otherwise check which one
   is newer.  
         SONAME的判定,以常规文件为优先来源,而不优先以链接文件作为SONAME的来源。
         */
       if ((!is_link && dlib_ptr->is_link)
    || (is_link == dlib_ptr->is_link
        && _dl_cache_libcmp (dlib_ptr->name, direntry->d_name) < 0))
  {
    /* It s newer - add it.  */
    /* Flag should be the same - sanity check.  */
    if (dlib_ptr->flag != flag)
      {
        if (dlib_ptr->flag == FLAG_ELF
     && (flag == FLAG_ELF_LIBC5 || flag == FLAG_ELF_LIBC6))
   dlib_ptr->flag = flag;
        else if ((dlib_ptr->flag == FLAG_ELF_LIBC5
    || dlib_ptr->flag == FLAG_ELF_LIBC6)
          && flag == FLAG_ELF)
   dlib_ptr->flag = flag;
        else
   error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."),
          dlib_ptr->name, direntry->d_name,
          entry->path);
      }
    free (dlib_ptr->name);
    dlib_ptr->name = xstrdup (direntry->d_name);
    dlib_ptr->is_link = is_link;
    dlib_ptr->osversion = osversion;
    dlib_ptr->isa_level = isa_level;
  }
       /* Don t add this library, abort loop.  */
       /* Also free soname, since it s dynamically allocated.  */
       free (soname);
       break;
     }
 }
      /* Add the library if it s not already in.  
        如果该动态库没在缓存列表中,则添加到链表记录中。
        这里看到,动态库的信息分为name、SONAME、flag、是否为链接、os版本、isa_level这几个方面
      */
      if (dlib_ptr == NULL)
 {
   dlib_ptr = (struct dlib_entry *)xmalloc (sizeof (struct dlib_entry));
   dlib_ptr->name = xstrdup (direntry->d_name);
   dlib_ptr->soname = soname;
   dlib_ptr->flag = flag;
   dlib_ptr->is_link = is_link;
   dlib_ptr->osversion = osversion;
   dlib_ptr->isa_level = isa_level;
   /* Add at head of list.  */
   dlib_ptr->next = dlibs;
   dlibs = dlib_ptr;
 }
    }

继续处理,下面代码仍然是 search_dir()函数的代码,用于核对得到的缓存信息列表的文件,把那些指向软链接的软链接文件,判断为多余的记录(节点),给予剔除。

ldconfig的帮助文档[2]说,/etc/ld.so.cache 文件的内容是有序列表,记录了/etc/ld.so.conf配置文件(及/etc/ld.so.conf.d/*.conf 这些子配置文件)中指定的目录,这些所有目录下的动态库文件的有效信息。除了这些目录,还包含了 通过命令行指定的目录、以及系统必要的目录 /lib、 /lib64 中的动态库文件信息。

/* Now dlibs contains a list of all libs - add those to the cache
     and created all symbolic links.  
     dlibs 包含了指定目录下查找到的所有动态库信息。将用于添加到缓存,并创建软链接(SONAME 文件)。
     */
  struct dlib_entry *dlib_ptr;
  for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
    {
      /* The cached file name is the soname for non-glibc-hwcaps
  subdirectories (relying on symbolic links; this helps with
  library updates that change the file name), and the actual
  file for glibc-hwcaps subdirectories.  
     /etc/ld.so.cache 文件的
     */
      const char *filename;
      if (entry->hwcaps == NULL)
 {
   /* Don t create links to links.  
      不创建指向链接文件的链接。
      */
   if (dlib_ptr->is_link == 0)
     create_links (dir_name, entry->path, dlib_ptr->name,
     dlib_ptr->soname);
   filename = dlib_ptr->soname;
 }
      else
 {
   /* Do not create links in glibc-hwcaps subdirectories, but
      still log the cache addition.  
         glibc 的 hwcaps 是2.33 版本开始引入的功能,
         支持hwcaps意味着除了常规编译的库之外,现在还可以使用来自最新 CPU 
         的扩展 cpu 指令集来安装库,glibc 将自动选择针对当前使用的
         cpu 优化的版本。ldconfig对于glibc-hwcaps子目录下的动态库,
         不会创建对应的SONAME,但仍会记录记录到/etc/ld.so.cache文件内。
         以被Linux在运行时按需使用。
         */
   if (opt_verbose)
     printf ("\t%s -> %s\n", dlib_ptr->soname, dlib_ptr->name);
   filename = dlib_ptr->name;
 }
      if (opt_build_cache)
 add_to_cache (entry->path, filename, dlib_ptr->soname,
        dlib_ptr->flag, dlib_ptr->osversion,
        dlib_ptr->isa_level, hwcap, entry->hwcaps);
    }

从上面代码出现的hwcaps相关的处理,在此补充一下关于glibc-hwcaps的作用说明 Hackweek 20: glibc-hwcaps in openSUSE[3];glibc-hwcaps 与AMD CPU的性能优化的关系[4],指出glibc-hwcaps 是基础设施的开始,以便能够更容易地根据硬件功能交付优化的库/共享对象。这是在库级别,用于提供插入式优化库,而不是 GCC 的 FMV(函数多版本控制)之类的库,后者在构建时试图提供优化的函数,然后在运行时根据 CPU 主机进行选择。

以上代码最后看到的 add_to_cache(……),的用法是这样的:

add_to_cache (entry->path, filename, dlib_ptr->soname,
        dlib_ptr->flag, dlib_ptr->osversion,
        dlib_ptr->isa_level, hwcap, entry->hwcaps);

从名称上看,就是把 找到的一个动态库的记录,添加到缓存数据结构(链表)中。这个是在for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)的for 每次循环过程的最后执行的,也就是把有效的动态库信息添加到链表。

从上层看,概要的,ldconfig 生成缓存文件,有这样的大体流程:

main() {……
    search_dirs (void)//获取所有待查找的目录列表
    {
      struct dir_entry *entry;
      for (entry = dir_entries; entry != NULL; entry = entry->next)
        search_dir (entry); // 进入每个目录,查找动态库,并记录到链表中
      } 
    }
     if (opt_build_cache)//若命令行参数要求生成缓存文件
    {
      save_cache (cache_file); //保存最终的缓存结果数据到 指定的缓存文件
                               //不指定文件路径则默认是/etc/ld.so.cache 文件。
      if (aux_cache_file)// aux_cache_file是辅助缓存文件。
     save_aux_cache (aux_cache_file);
      }
}

看一看这个流程,就对ldconfig如何将最新从/etc/ld.so.conf文件读取的动态库目录下的所有动态库的缓存信息,生成为/etc/ld.so.cache缓存文件,有了整体的认识。

看完这些代码,说几点印象:

  • 这些代码考虑到很多方面,不是一次成型的,通过git提交记录,以及changelog文件,我们看到Linux动态库的版本格式从glib4/5/6 有发展演化。
  • glibc 的ldconfig的代码是C语言实现的,其生成的/etc/ld.so.cache缓存文件,对于Linux下的依赖动态库的可执行文件的正常运行,关系重大。所以为了优化,和确保软件更新后的稳定性,会有一套命名标准。比如SONAME的命名机制。对软链接是否有效的判断,以及对新动态库的软链接信息的更新。所以总体流程还是比较繁杂的。要考虑到的细节比较多。从git commit历史看,代码维护者的名称大部分是国外开发者。
  • ldconfig 的C语言风格有编码排版约定,可读性尚可。也做了模块拆分(如 ldconfig的功能,划分到了多个文件,如入口文件 ldconfig.c 、缓存的读写逻辑实现文件 cache.c 、SONAME文件辅助功能文件readlib.c 、 chroot功能文件 chroot_canon.c )
  • ldconfig 的代码存在的不足,也是很多C项目的问题,使用了很多全局变量,各个函数到处在直接操作全局变量。虽然功能上没问题,高手写的代码也不会有运行异常,但总感觉可读性和内聚程度降低了。如果是我来实现ldcnfig,我会在内聚性和代码注释方面做一些改进。减少全局静态变量的使用;改成main()内部的变量,且通过main向被调用的函数传参指针参数的方式传入待读写的变量。以此提高可读性。

Linux的ldconfig命令的代码原理:ldconfig如何从指定目录中查找动态库文件信息?

  • 部分函数的单函数代码行数过长,比如search_dir(),还可以再拆分成多个函数。也是为了可读性能好一些。
  • 部分关键结构体的成员变量定义时,没有注释说明其作用。虽然通过看调用代码能知道,但这样的struct定义降低了可读性。

总之,通过本系列文章对Linux 的ldconfig命令的源码,从读动态缓存文件、打印库缓存信息、查找/etc/ld.so.conf 指定的目录的动态库信息等功能做了原理分析。希望对读者理解ldconfig的原理些许帮助。

参考资料:

  • [1]ldconfig的代码中,search_dir()函数的关键内容: http://www.zzvips.com/uploads/allimg/1hqbdtsviyj data-id="ld70c578-v7EavERA">[2]ldconfig的帮助文档: https://www.man7.org/linux/man-pages/man8/ldconfig.8.html
  • [3]Hackweek 20: glibc-hwcaps in openSUSE: https://antlarr.io/tag/glibc-hwcaps/
  • [4]glibc-hwcaps 与AMD CPU的性能优化的关系: http://www.zzvips.com/uploads/allimg/btgishasx03

延伸 · 阅读

精彩推荐
  • Linuxssh 登录很慢该如何解决

    ssh 登录很慢该如何解决

    这篇文章主要介绍了ssh 登录很慢该如何解决的相关资料,这里提供了两种方法,DNS反向解析及关闭ssh的gssapi认证的解决办法,需要的朋友可以参考下...

    linuxeye9922021-12-16
  • Linuxlinux设置tomcat自启动的方法

    linux设置tomcat自启动的方法

    这篇文章主要介绍了linux设置tomcat自启动的方法,需要的朋友可以参考下...

    Linux教程网8512021-10-10
  • Linux将 Linux 终端与 Nautilus 文件管理器结合起来

    将 Linux 终端与 Nautilus 文件管理器结合起来

    Nautilus 是 GNOME 桌面环境中的图形化文件浏览器。你可以使用它来访问和管理系统中的文件和文件夹。 尽管并非所有人都喜欢使用终端来管理文件和目录,...

    未知812023-08-08
  • Linux在Linux系统中创建新的亚马逊AWS访问密钥的方法

    在Linux系统中创建新的亚马逊AWS访问密钥的方法

    如何在Linux系统中创建新的亚马逊AWS访问密钥?我在配置一个需要访问我的亚马逊AWS帐号的应用时被要求提供AWS访问密钥ID和秘密访问密钥,我怎样创建一个...

    Linux教程网6182019-10-30
  • Linux理解 Linux/Unix 登录脚本的技巧

    理解 Linux/Unix 登录脚本的技巧

    有一些常见的情况,例如从Debian的包管理程序到Iaas的管理中,很多任务需要设置环境变量才能正常运行。 有时,程序通常只需要在 登陆时运行一次,例如...

    未知1042023-05-12
  • Linuxlinux top命令详解

    linux top命令详解

    这篇文章主要介绍了linux top命令详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    sparkdev5622022-03-01
  • Linuxlinux驱动程序开发详细介绍

    linux驱动程序开发详细介绍

    前提,一般来说内核代码的错误可能会引起一个用户进程的死亡,或者整个系统的瘫痪,更严重的后果,可能导致磁盘损伤~因此建议最好有一台实验机进行...

    Linux教程网5392019-12-17
  • LinuxLinux系统下无法卸载挂载的目录怎么办?

    Linux系统下无法卸载挂载的目录怎么办?

    我们在日常运维中经常性会遇到需要进行磁盘的扩容、卸载、挂载等操作,但是有时候这个系统上跑的应用并没有停止或者有其他的运维同事在操作这个目...

    今日头条10302020-12-30