Linux软件发布时的glibc not found问题
在使用和发布Linux软件时,最痛苦的莫过于提示glibc错误。
比如
version GLIBC 2.xx not found
这是一个使用Linux软件/环境中很容易遇到的问题。
它代表的是,用户系统里的glibc版本,和软件需要的glibc版本不匹配(一半是用户系统比编译发布的系统旧)。
这篇文章会向您介绍,glibc是什么,发布Linux软件时怎么避免 GLIBC错误。
什么是glibc
glibc是常用的GUN发布的libc库。
glibc有一下几个特点:
- 大部分应用软件会需要libc库与内核交互,加载so库,以及调用各种系统功能。
- 大部分Linux系统会使用glibc作为标准的系统库。
- glibc的版本和内核没有强匹配关系。
- glibc库采用了LGPL,即修改需要GPL开源,静态链接需要GPL开源,有一定的传染性。
这几个特点会影响到我们实际使用glibc和避免glibc错误,划重点,要考.
Linux和glibc是什么关系
从理论上,linux和glibc没什么直接关系。
从现实来说,linux和glibc是长期合作相互成就的革命伙伴关系。
首先,我们先要确认linux是什么。一般语境下,linux有两个含义,linux内核及基于linux内核的操作系统,以及GNU/LINUX发行版。
对于linux内核来说,gnu不是必需品,比如目前占有率追高的Android设备,就是采用了linux内核,然后为了避免glibc的传染性,采用了bionic作为自己的libc库,一样占据了很高的占有率。
对于gnu来说,linux内核也不是必需品,gnu还有gnu/bsd和gnu/hurd的发行版,只是只有Linux系统比较成功。
所以,从某种角度来说,GNU比Linux更依赖于对方。某些情况下我们能绕开GNU/GLIBC,这也是重要的Linux软件发布策略。
事实上,GNU背后的自由软件基金会(FSF)和Linux对于开源协议的态度也不完全一致。
GNU 对于广泛使用的GPLV2协议觉得不够严格,还推出了更严格的GPLV3协议甚至AGPL协议。
Linux作者的Linus本人坚守更宽松的GPLV2,并对商业化表示了一定的善意。
作为作者,我们只能说,我们敬佩GNU的坚持,我们感恩Linus的慷慨。
我们为什么要解决Linux下的glibc问题
因为GNU/LINUX是一个十分成熟和重要的生态。Linux的生态本质上分为Android/服务器端/桌面端3块。后两者是GNU/LINUX的天下,除了极少数发行版,比如Docker容器中插件的Alpine,都是使用glibc的。
Linux软件开发,本质上是 为服务器端和桌面端的Linux发行版开发软件,所以无法避开事实标准glibc。
怎么解决glibc错误
解决glibc错误其实很简单,主要方法是避开glibc,打包glibc,锁定glibc。
具体实施时,有多种具体的方案,可以选择一个或多个策略。
不依赖任何libc
部分编程语言,在不使用到高级系统功能时,可以完全避开libc的时候。
典型就是golang,在golang的常见使用场景服务编程中,可以通过禁用CGO,用纯golang实现,使用golang自己的net库的方式,不依赖任何libc。
当然,这个方案有很大的局限性,只有在不依赖系统库的服务编程,不引入各种连接库的形式才能实行。一旦需要使用第三方库和各种系统高级功能,比如做GUI软件,则基本无法使用。
使用协议更轻量更宽松的libc
Linux有个轻量级,Mit协议的libc库musl。
虽然在性能和功能上和glibc有一定的差距,但是它具有易于静态编译和更宽松的协议,所以很适合做为glibc的替代品。
当然,使用musl也会有一定的限制。
主要是同时加载glibc编译出的so库文件会有冲突,以及编译时必须有musl版的头文件。
静态编译
静态编译对于解决版本冲突可以说是黄金方案,只可惜,glibc的协议是LGPL。
所以,除非你是内部使用,不公开发布,或者源程序本身就是GPL协议,只要你公开发布了,都会被传染。
哪怕你只是为了规避glibc本身的版本冲突问题,而不是为了复用它的代码。但传染就是传染,不听你的解释。
容器发布
之前说过,内核和glibc并不需要匹配。
所以,Docker中共用内核,能部署不同版本的glibc的发布形式显得特别的好用。某种角度来说,Docker是一种另类的静态发布,只是不把程序静态编译,而是把整个运行环境都静态打包了。
当然,docker程序还是有各问题,就是和系统环境太过隔离,作为服务应用非常强打,对于用户系统日常使用的软件发布十分不便。
旧系统编译
这个是最传统,也是最无可奈何的选择了。
一般来说,需要公开发布的linux程序,最合适的方式就是找主够老旧的,还在支持的发行版进行打包(发文时为止,我们使用的打包发行版是Debian 10)
一般来说,我们可以通过虚拟机和Docker来实现这个需求。对,由于内核和glibc版本无关,Docker是非常适合打包的一个环境,写一个脚本 docker exec -ti /bin.bash,可以通过来挂载各种库和头文件,通过-u可以指定编译的用户。
而虚拟机可以用来验证打包成功后程序是否能正常运行。当然,特别复杂的环境,或者不愿意安装docker,使用虚拟机编译打包也是各很好的选择。
写在最后
写了这么长一篇文,很明显,作者是觉得割裂的glibc版本在很多时候带来了糟糕的体验。
希望Linux环境以后能更统一,但在以自由为主流的Linux环境,似乎这一切有点不切实际。
又或者,希望能有个标准的打包系统,足够轻,被大部分语言良好支持,打包出来的程序能被同一时代所有的还在维护期主流发行版支持,可能更有希望一点。