1.Linux|如何安装和运行多个 glibc 库
2.系统调用的码目实现细节(用户态)
3.可以在多线程环境中使用mktime和localtime_r吗?
4.å¦ä½å®è£
glibc-2.15.tar
5.升级 GLIBC 后想回退,遇到 __resolv_context_ 相关符号报错
6.glibc源码分析(二)系统调用
Linux|如何安装和运行多个 glibc 库
在Linux环境中,码目遇到需要支持不同应用程序或保持与旧软件兼容性的码目情况时,如何安装和管理多个glibc库就显得尤为重要。码目本文将引导您通过几个简单步骤来实现这一目标。码目
首要任务是码目源码视频解析确定为何需要多个glibc版本,通常是码目由于特定应用程序要求旧版本库中的特定功能或特性。在安装之前,码目确保系统是码目最新的,并根据您的码目发行版(Debian或Red Hat)准备相应的软件包或开发工具包。
首先,码目从官方库或可信源下载所需的码目glibc版本,如glibc 2.和2.。码目使用wget命令下载并解压源代码,码目创建包含glibc源代码的码目目录。然后,进入该目录并配置构建环境,执行编译安装,这样可以避免库之间的冲突。
安装后,管理多个glibc版本的关键在于设置LD_LIBRARY_PATH环境变量,这有助于指定应用程序使用特定版本的1101源码库。将相关设置添加到bashrc或profile文件中,确保更改持久生效。最后,检查每个版本的glibc是否正确安装。
运行依赖特定glibc版本的应用程序时,使用LD_PRELOAD环境变量。例如,若要使用glibc 2.,只需设置相应的LD_PRELOAD值。通过这些步骤,您可以在Linux系统上顺利安装和运行需要不同glibc版本的应用程序,同时保持系统的库环境整洁。
系统调用的实现细节(用户态)
本文以Ubuntu ..4 LTS环境和x_架构的glibc为例,详细解析了系统调用的实现细节。以一个具体的事例说明,即在编写应用程序时如何链接系统调用。
编译test.c后,链接libc.so动态库中的fork函数,其实现位于glibc源代码中。然而直接在glibc代码中找不到fork()的实现。通过实验,javabc源码将应用程序静态链接libc.a生成可执行文件,反汇编后发现实际调用的是__libc_fork。
__libc_fork在glibc工程sysdeps/nptl/fork.c路径下实现,调用系统功能的代码通过ARCH_FROK宏实现,此宏在glibc工程sysdeps/unix/sysv/linux/x_/arch-fork.h目录下。实现过程中,使用了__weak_alias实现在glibc工程include/libc-symbols.h路径下。
进一步分析__libc_fork函数,它通过调用ARCH_FROK宏实现调用系统功能,具体通过INLINE_SYSCALL宏调用clone,此宏定义在glibc工程sysdeps/unix/sysv/linux/x_/sysdep.h头文件中,与体系结构相关。内部调用流程涉及INTERNAL_SYSCALL定义和SYS_ify宏定义。具体实现中,__NR_##syscall_name宏定义在ubuntu系统的/usr/include/x_-linux-gnu/asm/unistd_.h文件中,表示系统调用编号,如fork系统调用实际通过__NR_clone标号传参。
通过内部_syscall##nr宏在glibc工程sysdeps/unix/sysv/linux/x_/sysdep.h定义,实现系统调用,从用户态到核心态。不同体系架构的besttrace 源码系统调用流程基本相似,但汇编指令各不相同。
本文旨在提供系统调用实现的详细解析,水平有限,欢迎指正批评,如有疑问欢迎私信交流。
可以在多线程环境中使用mktime和localtime_r吗?
localtime和mktime是用于在时间分量和时间秒数之间转换的标准c函数.
在glibc文档描述中,localtime的实现使用内部静态缓存来保存结果,因此这是一个API,不适用于多线程环境. glibc提供了线程安全的localtime_r版本. mktime没有这个问题.
因此,根据glibc文档,在多线程环境中使用localtime_r和mktime是安全的localtime 线程安全。
mktime和localtime_r的实现都考虑了时区的转换,并且时区的计算应使用全局变量tzname / timezone / daylight. 这本质上是线程不安全的.
请参阅glibc-2.3.2的源代码(下面的源代码位置均相对于源目录)
--------- time / localtime.c和time / tzset.c
在localtime_r中,调用tzset_internal来设置时区. 输入参数始终为0,因此从理论上讲,只要第一次结束,就无需初始化. 请参阅下面的代码.
但是,由于在多线程环境中引入了静态变量is_initialized,因此该实现代码存在问题. 无法保证并发执行环境的正确性.
----(时间/ tzset.c)-------
/ *解释TZ变量. * /
静态无效
内部功能
tzset_internal(始终)
int始终;
{
static int is_initialized;
注册const char * tz;
寄存器size_t l;
char * tzbuf;
unsigned short int hh,mmlocaltime 线程安全,ss;
无符号短整数规则;
如果(始终被is_initialized &&!源码付费)
返回;
is_initialized = 1;
.
}
但是mktime不是这样的
----(时间/ mktime.c)-------
/ *将* TP转换为time_t值. * /
time_t
mktime(tp)
struct tm * tp;
{
#ifdef _LIBC
/ * POSIX.1 8.1.1要求,每当调用mktime()时。
外部变量`tzname'中包含的
时区名称
设置
就像调用tzset()函数一样. * /
__ tzset();
#endif
返回__mktime_internal(tp,my_mktime_localtime_r和localtime_offset);
}
由于定义了_LIBC,每次都会调用tzset,因此tzset的代码是这样的
----(时间/ tzset.c)-------
无效
__ tzset(无效)
{
__ libc_lock_lock(tzset_lock);
tzset_internal(1);
如果(!____ use_tzfile)
{
/ *设置`tzname'. * /
__ tzname [0] =(char *)tz_rules [0] .name;
__ tzname [1] =(char *)tz_rules [1] .name;
}
__ libc_lock_unlock(tzset_lock);
}
每次都会调用tzset_internal,并且每次都将重写时区信息.
应注意,前面的宏__libc_lock_lock在sysdeps / generic / bits / libc-lock.h中定义为:
#define __libc_lock_lock(名称)
是无操作操作,因此它不能用作同步线程.
因此,您可以看到上述glibc代码实现中存在两个问题:
1. tzset_internal is_initialized中使用的静态变量
(这可以通过在程序中定义一个无用的全局变量并程开始工作之前在其初始化中调用mktime来克服)
2. mktime必须每次都重写全局变量tzname / timezone / daylight
(这个问题基本上是没有办法解决的)
因此mktime和localtime_r不适合多线程应用程序.
有两种解决方案:
1. 自己实现mktime和localtime_r,但是时区的计算很麻烦. 当然,您不能使用时区信息,也不能使用固定时区,例如北京时区.
2. 使用pthread的互斥锁来锁定mktime和localtime_r,但是以这种方式使用pthread库,可移植性不够好.
å¦ä½å®è£ glibc-2..tar
ç¼è¯æ¥éª¤ï¼
ä¸è½½glibc-2..tar.gzåè¡¥ä¸å glibc-ports-2..tar.gz
解å
$mv glibc-ports-2. glibc-2./ports
$mkdir glibc-build-2. &&cd glibc-build-2.
$ ../glibc-2./configure \
--prefix=/usr/local/glibc_mips \
CC=mipsel-linux-gcc \
--host=mipsel-linux \
--build=i-pc-linux-gnu \
--enable-add-on=nptl \
libc_cv_forced_unwind=yes \
libc_cv_c_cleanup=yes \
libc_cv_mips_tls=yes \
libc_cv_gnu_inline=yes
ok,没é®é¢
$make &&make install
大ååæ
##########################################################################
ä¸é¢æ¯æç¼è¯æ¶çè¿ç¨åéå°çé®é¢å解å³ï¼
##########################################################################
$tar xvf glibc-2..0.tar.bz2
$cd glibc-2..0
$./configure --prefix=/usr/local/glibc //å ä¸å å ¶ä»é项ï¼é¤äºå®è£ è·¯å¾ï¼ä¸åé»è®¤ï¼ç½ä¸ä¸è¬é ç½®armçé项å¦ä¸ --prefix=$HOME/usr/arm --with-headers=$HOME/usr/arm/glibc/arm-linux-glibc/include --with-libs=$HOME/usr/arm/glibc/arm-linux-glibc/lib
æ¥éï¼
configure: error: you must configure in a separate build directory
å¾å¥æªçé®é¢ï¼å¿ é¡»é ç½®ä¸ä¸ªæ建ç®å½ï¼åå¼å§ä»¥ä¸ºæ¯å®è£ ç®å½ä¸ºå建
$mkdir /usr/local/glibc
é®é¢ä»ç¶åå¨ï¼ç¾åº¦ä¹
$mkdir ../glibc-build && cd ../glibc-build
$../glibc-2..0/configure --prefix=/usr/local/glibc
åºç°æ°çé®é¢ï¼
configure: WARNING:
*** These auxiliary programs are missing or incompatible versions: msgfmt
*** some features will be disabled.
*** Check the INSTALL file for required versions.
checking LD_LIBRARY_PATH variable... contains current directory
configure: error:
*** LD_LIBRARY_PATH shouldn't contain the current directory when
*** building glibc. Please change the environment variable
*** and run configure again.
第ä¸ä¸ªè¦åä¸ç¨ç®¡å®ï¼ç¬¬äºä¸ªLD_LIBRARY_PATYä¹ä¼æéï¼æçè¿ä¸ªè·¯å¾ç¨äºå¤å°å¤©äºãä»ç»çæ示ï¼ä¸åºå å«å½åè·¯å¾ãæå¼~/.bash_profile
$cat ~/.bash_profile
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib export LD_LIBRARY_PATH
è¿ä¹æ²¡å½åè·¯å¾åãè¿æ¯ç¾åº¦å§ã
ä¸ä¸ªå å¼ç解éæ¯è¿æ ·âLD_LIBRARY_PATHä¸è½ä»¥ç»ç»ç¬¦ä½ä¸ºå¼å§åæåä¸ä¸ªå符ï¼ä¸è½æ2个ç»ç»ç¬¦è¿å¨ä¸èµ·ï¼æçLD_LIBRARY_PATH为 :/usr/local/firefox:/usr/local/firefox,åªè¦å¨åé¢å ä¸ä¸ä¸ªè·¯å¾ï¼ä¸è®©ï¼åºç°å¨ç¬¬ä¸ä¸ªå符就å¯ä»¥äº â
åæ¥å¦æ¤ï¼ç¬¬ä¸ä¸ªå符ä¸è½æ¯":"ï¼ä¿®æ¹~/.bash_profile
export LD_LIBRARY_PATH=/usr/local/lib export LD_LIBRARY_PATH
$../glibc-2..0/configure --prefix=/usr/local/glibc
lsä¸ä¸ï¼åç°ï¼å½åç®å½çæäºMakefileçä¸å ä¸è¥¿
$make && make install
没é®é¢
ä¸ä¸æ¥å¼å§äº¤åç¼è¯
$mkdir ../glibc-build-mips && cd ../glibc-build-mips
$ ../glibc-2..0/configure --prefix=/usr/local/glibc_mips CC=mipsel-linux-gcc --host=mips
åºç°æ°çé®é¢ï¼
configure: running configure fragment for add-on libidn
configure: running configure fragment for add-on nptl
*** The GNU C library is currently not available for this platform.
*** So far nobody cared to port it and if there is no volunteer it
*** might never happen. So, if you have interest to see glibc on
*** this platform visit
*** piler. */
# if !defined __CHAR_TYPE__ || !defined __CHAR_TYPE__
# if defined __STDC_VERSION__ && __STDC_VERSION__ < L
# error "<uchar.h> requires ISO C mode"
# else
# error "definitions of __CHAR_TYPE__ and/or __CHAR_TYPE__ missing"
# endif
# endif
æç½äºï¼åæ¥æ¯éè¦cæ¯æï¼mipsel-linux-gcc -vä¸ä¸ï¼æçæ¯æc.åæ¥å¦æ¤ãææ¶æ²¡æäºï¼æè¿åä¸å°ä¿®æ¹cçæ¯æï¼åªå©ä¸¤ä¸ªåæ³ï¼ä¸ç¨è¿ä¸ªglibcçæ¬æè éæ°ç¼è¯ä¸ä¸ªæ¯æcç交åç¼è¯å¨ãç¼è¯å¨éè¦åçæ¯è¾å¤ï¼ææ¶å æ¢ä¸ªä½ç¹ççæ¬å§ã
ä¸è½½galibc-2.çæ¬
éå¤ä¸é¢æ¥éª¤ï¼è§£åtarå
解åportså
$mv glibc-ports-2. glibc-2./ports
$mkdir glibc-build-2. &&cd glibc-build-2.
$ ../glibc-2./configure \
--prefix=/usr/local/glibc_mips \
CC=mipsel-linux-gcc \
--host=mipsel-linux \
--build=i-pc-linux-gnu \
--enable-add-on=nptl \
libc_cv_forced_unwind=yes \
libc_cv_c_cleanup=yes \
libc_cv_mips_tls=yes \
libc_cv_gnu_inline=yes
ok,没é®é¢
$make &&make install
åºå·²ç»ç¼å¥½äºï¼ä½æ¯ä¸è½ç´æ¥ä½¿ç¨ï¼å¿ é¡»åç¨æ°çåºéç¼ä¸éç¼è¯å¨æè¡ã
ä¸ä¸ç¯
升级 GLIBC 后想回退,遇到 __resolv_context_ 相关符号报错
在处理GLIBC升级后回退问题时,遇到了与__resolv_context_相关符号的链接错误。本文将详细分析这一现象及其解决方案。
首先,升级GLIBC后,若用户希望回退到原有版本,却发现编译出的可执行文件和依赖的动态库在链接时遇到错误。这是由于新版GLIBC的libc.so干扰了链接过程。在Ubuntu .服务器上,GLIBC版本为2.,但用户为了运行Open3D,安装了GLIBC 2.。
当使用新版GLIBC后,GCC在链接时会寻找libc.so,而在PATH环境变量中指定了新的目录,使得GCC在链接时使用了新的libc.so文件。为解决此问题,可以临时使用export命令修改PATH,排除/usr/local/bin。
链接错误通常与libc.so的加载有关。GCC在链接可执行文件时,会查找依赖的动态库,如OpenCV的libopencv_videoio.a静态库和ffmpeg的动态库。在/etc/ld.so.conf.d/libc.so中,配置了将/usr/local/lib目录作为默认查找目录。使用ldconfig -p验证了这一配置,并显示了在旧版本GLIBC中找到的三个库。
错误符号__resolv_context_get与libresolv.so.2库关联,但实际上这是与符号解析有关的问题。在GLIBC代码中找到了这些符号的描述。为解决此问题,可以修改/usr/local/lib目录为/usr/local/lib-old,并验证先前链接器报告的未找到符号现在位于GLIBC 2.中。
若希望在回退GLIBC版本时保持可执行文件和动态链接阶段的libc.so不变,需要确保当前GCC使用的是预期的GLIBC版本。正确的卸载和安装GLIBC方式是关键。一种策略是在外部目录构建并安装GLIBC,避免直接修改系统的默认配置。
在处理已经安装到/usr/local的GLIBC问题时,应谨慎处理,避免影响其他文件或系统稳定。正确的做法是使用与安装配套的卸载工具,或通过目录改名等方法进行回退。本文建议在执行卸载操作时,使用外部目录构建的GLIBC版本,以避免直接在glibc源码目录下执行构建带来的风险。
总之,升级GLIBC后遇到回退问题时,关键在于理解链接过程中的依赖关系和环境变量的影响,以及采取适当的策略来管理和修改这些依赖,以确保系统稳定性和兼容性。
glibc源码分析(二)系统调用
在glibc源码中,许多系统调用被使用了.c封装的方式进行封装。这一过程借助嵌入式汇编,严格遵循系统调用封装规则。以stat函数为例,其实现揭示了.c封装的奥秘。
在源代码中,stat系统调用被INLINE_SYSCALL宏所封装。该宏首先调用INTERNAL_SYSCALL宏,执行系统调用并把返回值存入resultvar变量中。接下来,通过判断系统调用是否成功执行,采取相应的后续操作。若执行错误,则调用__syscall_error设置errno并返回-1;若执行成功,则返回resultvar。
在处理系统调用参数个数nr时,INTERNAL_SYSCALL宏发挥了关键作用。根据nr的不同,宏会调用不同的内部函数进行处理。例如,当nr为0时,调用INTERNAL_SYSCALL_MAIN_0宏,设置eax寄存器为系统调用号,执行*_dl_sysinfo函数进行系统调用。当nr为1时,宏将参数1存入ebx寄存器,同时设置eax寄存器为系统调用号,并执行系统调用。
类似的,nr为2、3、4、5或6时,宏分别会将参数2至6存入ecx、edx、esi、edi或ebp寄存器中,并与系统调用号相结合,执行*_dl_sysinfo函数。通过这一系列的嵌入式汇编操作,.c文件成功封装了系统调用,实现了高效、精确的调用过程。
总的来说,glibc中.c封装的实现展示了汇编语言的强大功能,以及在系统调用处理中的应用。通过精确的汇编指令和灵活的参数传递,封装过程确保了系统调用的执行效率和正确性。