对于很多linux/ubuntu新手来说,要想在Ubuntu系统上熟练地安装、升级、卸载软件,确实是挺考验人的一件事,会遇到各种各样的问题,有时候会把人搞得很不耐烦,打击人的学习积极性。Ubuntu一般使用apt-get命令 (ubuntu16.04版本以后也可以使用apt) 来安装、升级、卸载软件。比如我们想安装一个软件,常用下面的命令进行安装:

    $  apt-get install xxx
    $  apt-get update
    $  apt-get upgrade

顺利安装还好,但有时候还是会遇到各种各样的问题,虽说有时候能通过互联网解决掉,但是对于 apt-get 如何安装的却不是很清楚,比如什么是软件源,就没有啥概念。本文目的就是跟大家一起了解下Ubuntu环境下 apt-get 的软件安装基本原理。


想要了解apt-get的工作原理,首先我们要明白软件的安装是怎么回事。我们知道一个程序的运行,一般是要先加载(load)到内存(RAM)中,因为RAM存储器支持随机读写,因此CPU可以一条一条地去取指令、翻译指令、运行指令。但是RAM有一个缺点,就是断电后数据会消失,无法保存。因此,我们需要将程序文件保存到一个非易失性存储器上,比如硬盘、NAND Flash上,断电后数据也可以保存。把程序保存到磁盘上的过程其实就是软件的安装过程,早期的单片机裸机环境,程序一般都是使用专门的工具直接烧写到Flash上的,后来有了OS和文件系统,我们可以很方便的通过鼠标,直接将程序文件安装到某个文件目录下面,程序在运行时,系统就可以直接到这个目录下面找到对应的二进制文件,加载到内存,然后就可以直接运行了。


无论是Windows还是Linux,基本流程都是如此:告诉系统你的程序二进制文件存放到哪里了,然后程序在运行时,系统就会到这个指定的路径下面去找你要运行的二进制程序文件,加载到内存,然后运行。在Windows下面,你安装好软件后,在桌面上会有一个快捷方式,其实就是指向你安装路径的软链接。在Linux环境下面,我们要运行的程序一般是放在默认的路径下面的,如:/bin、/sbin、/usr/bin、/usr/sbin等。比如你在shell下面运行:$ ls

命令时,系统就会到这些默认的路径下面去寻找ls文件,在/bin/ls下面找到后,就会加载到内存,然后 ls 程序就可以运行了。

我们写一个简单的helloworld程序,编译为a.out,我们可以直接运行它:

    $ ./a.out

为什么不能直接运行$ a.out呢,因为我们的a.out没有安装到默认路径,因此运行时,你要指定路径信息,否则系统就找不到。你把a.out放到默认的/usr/bin下面,然后就可以直接通过$ a.out文件名去执行程序了。当然,你可以可以把自己编译的a.out二进制文件放在自定义的某个目录下面,然后以环境变量的形式告诉系统这个安装路径,当系统在那些默认路径下面找不到时,会到这个安装路径下查找。


我们在Linux环境下安装软件时,最简单的方法是从网上下载这个二进制程序文件,放到Linux系统中的默认路径下面就可以了。但是有些程序是采用动态链接编译的,运行时需要依赖一些动态共享库,因此需要打包一起安装。我们下载的软件一般很少是一个单纯的二进制文件,而是压缩包的形式,在这个压缩包里有:二进制程序文件、动态链接库、软件文档说明、安装信息、甚至有一些自动安装的脚本等。在 Debian 和 Ubuntu 环境下,这个压缩包格式为deb格式,我们安装软件时,先从网上下载对应的deb包,然后可以使用dpkg工具去解析这个包、安装这个包。

$ dpkg -i xxx.deb   安装xxx.deb软件包
$ dkpg -R /home/xxx   安装xxx目录下的多个deb包
$ dkpg -r xxx   卸载xxx软件包

当然你也可以将自己的二进制程序制作成一个deb安装包,放到网上,供其他人下载安装:

$ apt install checkinstall dh-make
$ ./configure --prefix=/home/tools //配置编译信息
$ make  //编译你的程序
$ checkinstall  //制作deb包
$ dkp -i xxx.deb //安装deb包

因为每个人都可以编译、制作deb包、并随意发布到网上,这就很容易造成混乱,鱼龙混杂、质量得不到保证,甚至有些包还有可能是一个病毒软件。因此Ubuntu系统采用一个软件仓库来管理这些deb软件包,把这些包放到一个官方的网站服务器上,类似于苹果系统的APP store,用户使用apt命令安装软件时,只能到这个服务器上下载软件。考虑到全球各个地方的网络环境差异,往往会在全球各地同时配置几个镜像服务器,这样全球各地的Ubuntu用户都可以根据网络状况到最合适的服务器上去下载和安装deb软件包了。这些服务器我们也称为软件源 (repository) 或者简称为“源”。

这些服务器的网络地址保存在/etc/apt/source.list文件中。文件内容如下所示:

deb http://us.archive.ubuntu.com/ubuntu/ xenial universe

当我们使用apt install安装软件时,apt工具就会根据这个source.list文件中的网络地址,选择最合适的服务器去下载软件包。

一般Ubuntu默认的软件源是Ubuntu官方网站,打开上面的网址,你会发现上面存有很多软件包的信息。

跟涛哥一起学嵌入式第12集:关于 apt-get 软件安装那些事儿_第1张图片

对于国内用户来说,访问国外的网站速度可能会慢很多、甚至无法访问。国内高校和互联网公司其实也有很多服务器提供下载,大家百度搜索一下:阿里云软件源、中科大软件源,一般都会搜索到很多服务器地址,可以选择其中几个,添加到/etc/apt/source.list文件中,以后使用apt-get安装软件时,就可以直接从国内的服务器上直接下载deb包了,速度会快很多。

修改好/etc/apt/source.list文件后,你还需要使用$ apt-get update命令更新一下。这个命令的作用访问 /etc/apt/source.list 文件中的每一个服务器,读取可以支持下载安装的软件列表,并保存到本地电脑中(/var/lib/apt/lists)。这个列表就像饭店里的菜单一样,要按照菜单去点菜,直接使用apt install xxx 就可以直接安装软件了。如果你要安装的软件如果不在软件列表中,很可能就安装失败。

软件列表的另一个作用是可以帮助你软件更新,因为服务器上的软件版本也会不断更新,你本地已经安装的软件如果跟软件列表中的软件版本不一致,它可能就会提示你软件需要更新,就像我们PC中的软件管家一样,它会提示你,你的电脑中有多少个软件可以更新。

跟涛哥一起学嵌入式第12集:关于 apt-get 软件安装那些事儿_第2张图片

当然,你也可以使用apt list命令去查看具体需要更新的软件包:

跟涛哥一起学嵌入式第12集:关于 apt-get 软件安装那些事儿_第3张图片

接下来,如果你想更新这些已经安装的软件,就可以通过$ apt-get upgrade命令来完成。这个命令会将本地已经安装的软件与刚刚使用update命令下载到本地的软件列表进行对比,如果发现版本不一致,就会重新安装最新的版本。如果你的系统需要更新的软件包太多,这个可能需要一定的时间,耐心等待就可以了,升级成功后,一般会有提示信息,你升级了多少个软件包...


使用apt安装软件的另一个好处是可以自动处理依赖关系。比如你想安装一个B,需要依赖A,那么你安装B的同时,B所依赖的A软件包也会自动安装上了。在/var/lib/dpkg/available文件中,有详细的软件包信息,包括软件版本、软件依赖的包等。包括在视频教程《使用QEMU搭建嵌入式U-boot+Linux+NFS开发环境》中,很多学员反馈说,为什么我的环境跟视频中的不一样?这是因为每个Ubuntu版本不同、安装的软件不同,安装的库、头文件也不一样,大家在编译的时候如果遇到一些无法识别的命令、缺少一些头文件,直接安装对应的工具和库就可以了,随着你的Ubuntu系统安装的一些依赖库、工具越来越多,以后再编译其它软件时,一般都可以顺利编译了,因为很多依赖的库文件、头文件、工具、命令等都安装得差不多了。


对于一个新手。在使用apt-get安装软件时,经常遇到的问题很多,比如权限不够,使用sudo、或切换到root安装可以解决这个问题。无法获取软件包,网络访问不了,可以在/etc/apt/source.list文件中添加国内的一些镜像服务器地址(软件源)。还有一个经常遇到的问题是:

E: 无法获得锁 /var/cache/apt/archives/lock

直接删除这个文件就可以了,对应的还有 /var/lib/dpkg/lock文件,顺便也删除掉,一般就可以解决问题。在安装的过程中,如果遇到其它问题,建议先到百度里搜一搜,你遇到的问题、踩到的坑,前面可能已经有无数人遇到过了,看看它们是怎么解决的,一般都可以解决问题。


最后,当你安装的软件在服务器中确实没有时,这就需要你手动下载源码、编译安装了,一般在Linux系统中,可以直接下载GNU工程开源代码,然后直接通过三步走,就可以直接编译和安装了:

$ ./configure$ make$ make install