0%

Linux笔记

常用命令

多条命令执行结果不输出到终端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 使用;分隔的多条命令执行结果之间互不干扰
$ ls;lls;ls
Downloads termux-packages
zsh: command not found: lls
Downloads termux-packages

# 使用&&分割的多条命令后一条会在前一条命令执行成功后才执行
$ ls&&lls&&ls
Downloads termux-packages
zsh: command not found: lls

# 使用cmd > /dev/null 2>&1将输出重定向到null,即不输出到终端
# 使用if [ $? -ne 0 ]; then echo 1; else echo 0;fi;可以得到上一条命令的执行结果($?表示上一条命令的执行结果)
$ ls > /dev/null 2>&1;if [ $? -ne 0 ]; then echo 1; else echo 0;fi;
0

# 使用{}包裹起来的多条命令
$ {ls&&ls&&ls} > /dev/null 2>&1;if [ $? -ne 0 ]; then echo 1; else echo 0;fi;
0

# echo指定-e参数可开启转移,添加/c可不换行输出
$ echo -e "please input a value:\c";

后台运行

当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,解决办法有两种途径:

  • 让进程忽略 HUP 信号;
  • 让进程运行在新的会话里从而成为不属于此终端的子进程。

nohup

nohup 的用途就是让提交的命令忽略 hangup 信号,一般我们可在结尾加上”&”来将命令同时放入后台运行,也可用”>filename 2>&1”来更改缺省的重定向文件名。

1
2
3
4
5
6
$ nohup ping www.baidu.com &
[1] 3059
nohup: appending output to 'nohup.out'
$ ps -ef |grep 3059
root 3059 984 0 21:06 pts/3 00:00:00 ping www.baidu.com
root 3067 984 0 21:06 pts/3 00:00:00 grep 3059

setsid

换个角度思考,如果我们的进程不属于接受 HUP 信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了。setsid 就能做到这一点。

1
2
3
4
$ setsid ping www.baidu.com
$ ps -ef |grep www.baidu.com
root 31094 1 0 07:28 ? 00:00:00 ping www.baidu.com
root 31102 29217 0 07:29 pts/4 00:00:00 grep www.baidu.com

进程 ID(PID)为31094,而它的父 ID(PPID)为1(即为 init 进程 ID),并不是当前终端的进程 ID。

&

将一个或多个命名包含在()中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能。

当我们将&也放入()内之后,就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs来查看的。

1
2
3
4
$ (ping www.baidu.com &)
$ ps -ef |grep www.baidu.com
root 16270 1 0 14:13 pts/4 00:00:00 ping www.baidu.com
root 16278 15362 0 14:13 pts/4 00:00:00 grep www.baidu.com

从上例中可以看出,新提交的进程的父 ID(PPID)为1(init 进程的 PID),并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的 HUP 信号的影响了。

查看进程

概述

  • VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存),一个进程总共可访问的地址空间。其大小还包括了可能不在RAM中的内存(比如虽然malloc分配了空间,但尚未写入)。 VSS 很少被用于判断一个进程的真实内存使用量。
  • RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存),一个进程在RAM中真实存储的总内存。但是RSS还是可能会造成误导,因为它仅仅表示该进程所使用的所有共享库的大小,它不管有多少个进程使用该共享库,该共享库仅被加载到内存一次。所以RSS并不能准确反映单进程的内存占用情况。
  • PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存),按比例表示使用的共享库, 例如:如果有三个进程都使用了一个共享库,共占用了30页内存。那么PSS将认为每个进程分别占用该共享库10页的大小。 PSS是非常有用的数据,因为系统中所有进程的PSS都相加的话,就刚好反映了系统中的总共占用的内存。 而当一个进程被销毁之后, 其占用的共享库那部分比例的PSS,将会再次按比例分配给余下使用该库的进程。这样PSS可能会造成一点的误导,因为当一个进程被销毁后,PSS不能准确地表示返回给全局系统的内存(the memory returned to the overall system)。
  • USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存),一个进程所占用的私有内存。即该进程独占的内存。 USS是非常非常有用的数据,因为它反映了运行一个特定进程真实的边际成本(增量成本)。当一个进程被销毁后,USS是真实返回给系统的内存。当进程中存在一个可疑的内存泄露时,USS是最佳观察数据。

一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS

free

1
2
3
4
$ free -h
total used free shared buff/cache available
Mem: 7.9G 6.6G 1.0G 17M 223M 1.1G
Swap: 19G 566M 19G

top

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 得到进程的pid
$ pidof sleep
602

# top指定查看PID
$ top -p 602
top - 11:01:36 up 23 min, 0 users, load average: 0.52, 0.58, 0.59
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.7 us, 0.9 sy, 0.0 ni, 98.2 id, 0.0 wa, 0.2 hi, 0.0 si, 0.0 st
KiB Mem : 8266588 total, 2224708 free, 5812528 used, 229352 buff/cache
KiB Swap: 20680852 total, 19784620 free, 896232 used. 2320328 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
602 hearing 15 42+ 13956 808 688 S 0.0 0.0 0:00.01 sleep

# top查看某些指定的PID
$ top -p 602,666,1001
  • 键入 P 按照CPU排序输出
  • 键入 M 按照内存排序输出

ps

ps是显示瞬间进程的状态,并不动态连续;如果想对进程进行实时监控应该用top命令。如果输出较多可以结合less命令和管道来使用,即 ps -ef | less

  • -A:所有的进程均显示出来,与 -e 具有同样的效用
  • -a:显示现行终端机下的所有进程,包括其他用户的进程
  • -u:以用户为主的进程状态/以针对用户的格式输出
  • -x:通常与 a 这个参数一起使用,可列出较完整信息
  • -l:较长、较详细的将该PID 的的信息列出;
  • -j:工作的格式 (jobs format)
  • -f:做一个更为完整的输出。
1
2
3
4
5
6
7
8
# 根据CPU使用率来升序排序
$ ps -aux --sort -pcpu | less

# 根据内存使用率来升序排序
$ ps -aux --sort -pmem | less

# 合并命令,并通过管道显示前10个结果
$ ps -aux --sort -pcpu,+pmem | head -n 10

可以使用 -C 参数查看指定进程的信息:

1
2
3
$ ps -f -C sleep
UID PID PPID C STIME TTY TIME CMD
hearing 348 7 0 10:45 tty1 00:00:00 sleep 555

也可以结合watch命令实时查看信息:

1
2
# 每秒执行一次ps -aux --sort -pmem, -pcpu
$ watch -n 1 'ps -aux --sort -pmem, -pcpu'

proc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ cat /proc/$pid/status
Name: main
State: S (sleeping)
Tgid: 24154
Pid: 24154
PPid: 1
TracerPid: 0
Uid: 2000 2000 2000 2000
Gid: 2000 2000 2000 2000
FDSize: 32
Groups: 1004 1007 1011 1015 1028 3001 3002 3003 3006
VmPeak: 777484 kB
VmSize: 777484 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 26960 kB
VmRSS: 26960 kB
VmData: 11680 kB
VmStk: 8192 kB
VmExe: 12 kB
VmLib: 52812 kB
VmPTE: 134 kB
VmSwap: 0 kB
Threads: 13
SigQ: 0/6947
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000001204
SigIgn: 0000000000000001
SigCgt: 00000002000094f8
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000000000c0
Seccomp: 0
Cpus_allowed: f
Cpus_allowed_list: 0-3
voluntary_ctxt_switches: 18
nonvoluntary_ctxt_switches: 76

Android

procrank

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
rocrank
PID Vss Rss Pss Uss cmdline
1078 59840K 59708K 42125K 39344K com.csr.BTApp
2683 59124K 59040K 37960K 33032K com.android.launcher
1042 51572K 51488K 35686K 33604K android.process.acore
782 32808K 32748K 16775K 14716K system_server
667 20560K 17560K 12739K 8940K /system/bin/surfaceflinger
851 30124K 30036K 12085K 7996K com.android.systemui
2999 27680K 27596K 9929K 7040K com.baidu.input
959 20764K 20676K 5522K 3788K com.android.phone
3468 21892K 21800K 4591K 1920K com.apical.dreamthemetime
982 19880K 19792K 4438K 2644K com.csr.csrservices
668 19592K 19480K 3525K 1360K zygote
670 2960K 2960K 2407K 2356K /system/bin/mediaserver
663 1784K 1784K 1209K 1116K /system/bin/synergy_service
756 3404K 1348K 1133K 1124K /usr/bin/gpsexe
669 1468K 1468K 959K 928K /system/bin/drmserver
675 692K 692K 692K 692K /bin/sh
3482 656K 652K 456K 444K procrank
1 164K 164K 144K 144K /init
------ ------ ------
195031K 163724K TOTAL

RAM: 480380K total, 3624K free, 732K buffers, 299788K cached, 264844K shmem, 7632K slab

dumpsys

1
2
3
4
5
6
7
dumpsys [options]
meminfo 显示内存信息
cpuinfo 显示CPU信息
account 显示accounts信息
activity 显示所有的activities的信息
window 显示键盘,窗口和它们的关系
wifi 显示wifi信息

可以在其后通过包名或者进程pid展示指定进程的信息。

UNIX与Linux

Linux 是一个类似 Unix 的操作系统(类UNIX系统),Unix 要早于 Linux,Linux 的初衷就是要替代 UNIX,并在功能和用户体验上进行优化,所以 Linux 模仿了 UNIX(但并没有抄袭 UNIX 的源码),使得 Linux 在外观和交互上与 UNIX 非常类似。

二者的区别:

  • UNIX 系统大多是与硬件配套的,也就是说,大多数 UNIX 系统如 AIX、HP-UX 等是无法安装在 x86 服务器和个人计算机上的,而 Linux 则可以运行在多种硬件平台上;
  • UNIX 是商业软件,而 Linux 是开源软件,是免费、公开源代码的。

UNIX/Linux系统结构:可以粗糙地抽象为 3 个层次:底层是 UNIX/Linux 操作系统,即系统内核(Kernel);中间层是 Shell 层,即命令解释层;高层则是应用层。

  1. 内核层:内核层是 UNIX/Linux 系统的核心和基础,它直接附着在硬件平台之上,控制和管理系统内各种资源(硬件资源和软件资源),有效地组织进程的运行,从而扩展硬件的功能,提高资源的利用效率,为用户提供方便、高效、安全、可靠的应用环境。Linux 内核由如下几部分组成:内存管理、进程管理、设备驱动程序、文件系统和网络管理等。
  2. Shell层:Shell 层是与用户直接交互的界面。用户可以在提示符下输入命令行,由 Shell 解释执行并输出相应结果或者有关信息,所以我们也把 Shell 称作命令解释器,利用系统提供的丰富命令可以快捷而简便地完成许多工作。目前主要有下列版本的shell:
    • Bourne Shell(sh):贝尔实验室开发的  
    • Bourne Again Shell(bash):GNU操作系统上默认的shell,大部分linux的发行套件使用的都是这种shell
    • Korn Shell:是对Bourne SHell的发展,在大部分内容上与Bourne Shell兼容
    • C Shell:是SUN公司Shell的BSD版本
  3. 应用层:应用层提供基于 X Window 协议的图形环境。X Window 协议定义了一个系统所必须具备的功能,可系统能满足此协议及符合 X 协会其他的规范,便可称为 X Window。

Linux内核与发行版

Linux内核是计算机操作系统的核心。一个完整的 Linux发行版包括了内核与一些其他与文件相关的操作,用户管理系统,和软件包管理器等一系列软件。每个工具都是整个系统的一小部分。这些工具通常都是一个个独立的项目,有相应的开发者来开发及维护。

Linux的众多发行版可能是基于不同的内核版本的。例如:流行的 RHEL6发行版是基于很老但是很稳定的 2.6.32 版本的Linux内核的。其他的一些发行版可能会很快的更新以适应最新的内核版本。需要特别注意的一点是,内核并不是一个非此即彼的命题,例如RHEL6就在2.6.32的内核中引进了新版本内核的许多改进。

各发行版提供的其他基本工具和组成部分还有包括以下的内容:C/C++编译器,gdbdebugger 调试工具,核心系统库应用程序,用于在屏幕上绘图的底层接口以及高级的桌面环境,以及供安装和更新包括内核在内的众多组建的系统

Debian是包括Ubuntu在内许多发行版的上游,而Ubuntu又是Linux Mint及其他发行版的上游。Debian在服务器和桌面电脑领域都有着广泛的应用。Debian是一个纯开源计划并着重在一个关键点上,稳定性。它同时也提供了最大的和完整的软件仓库给用户。

Android与Linux

  • Android采用Linux作为内核
  • Android对Linux内核做了修改,目的是适应在移动设备上使用
  • Android开始作为Linux的一个分支,后来由于无法并入Linux的主开发树,已被Linux Kernel小组从开发树中删除

Android是在Linux内核基础上运行的,提供的核心系统服务包括安全、内存管理、进程管理、网络组和驱动模型等内容。在硬件层和系统中其他软件之间添加了硬件抽象层(HAL),严格上来说Android不算是Linux系统

Android内核是有标准的Linux内核修改而来的,继承了Linux内核的诸多优点,保留了Linux内核的主题框架,同时Android按照移动设备的要求,在文件系统、内存管理、进程间通信机智和电源管理方面进行了修改,添加了相关的驱动程序和必要的新功能。Android在很大程度上保留了Linux的基本架构。

GNU

GPL:GNU通用公共许可协议(英语:GNU General Public License,缩写GNU GPL 或 GPL),是被广泛使用的自由软件许可证,给予了终端用户运行、学习、共享和修改软件的自由。

GNU是一个自由的操作系统,其内容软件完全以GPL方式发布。这个操作系统是GNU计划的主要目标,名称来自GNU’s Not Unix!的递归缩写,因为GNU的设计类似Unix,但它不包含具著作权的Unix代码。GNU的创始人,理查德·马修·斯托曼,将GNU视为“达成社会目的技术方法”。

作为操作系统,GNU的发展仍未完成,其中最大的问题是具有完备功能的内核尚未被开发成功。GNU的内核,称为Hurd,是自由软件基金会发展的重点,但是其发展尚未成熟。在实际使用上,多半使用Linux内核、FreeBSD等替代方案,作为系统核心,其中主要的操作系统是Linux的发行版。Linux操作系统包涵了Linux内核与其他自由软件项目中的GNU组件和软件,可以被称为GNU/Linux。

GNU/Linux命名争议,是在自由及开放源代码软件社群成员内的,关于是应该把使用GNU软件与Linux内核组合之操作系统称为“GNU/Linux”还是“Linux”的争议。

GNU/Linux这一名称是由自由软件基金会的创立者与GNU计划的发起人理查德·斯托曼所提出的。GNU的开发者与其支持者,希望以该名称来作为此操作系统的正式名称。他们认为,此操作系统,包括了GNU系统软件包与Linux kernel,使用GNU/Linux这个名称,可以良好概括它的主要内容。况且,GNU项目原本就是以发展一个自由的操作系统为远程项目,但迟迟没有完成。而Linux kernel的出现刚好可以补足这个缺口。

Linux内核本身并不是GNU计划的一部分,GNU/Linux这个名称在Linux社群中并没有得到一致认同。一些发行版社群例如Debian采用了GNU/Linux这一名称,但许多Linux社群中的成员认为使用Linux这一名称是更好的,为此提出了数项理由,主张Linux这个名称朗朗上口,且在公众与媒体中更为通用。Linux内核项目的发起人林纳斯·托瓦兹(Linus Torvalds)偏好于使用Linux,但对于GNU/Linux这个名字并不强烈反感。

一切皆文件

描述

linux/unix下的哲学核心思想是一切皆文件。它指的是,对所有文件(目录、字符设备、块设备、套接字、打印机、进程、线程、管道等)操作,读写都可用fopen()/fclose()/fwrite()/fread()等函数进行处理,屏蔽了硬件的区别,所有设备都抽象成文件,提供统一的接口给用户,虽然类型各不相同,但是对其提供的却是同一套操作界面,更进一步,对文件的操作也可以跨文件系统执行。

操作一个已经打开的文件:使用文件描述符(file descriptor),简称fd,它是一个对应某个已经打开的文件的索引(非负整数)。

Windows的内部实现也近似于“一切皆文件”的思想,当然,这一切都只在内核里才有,下载一个WinObj这软件就可以看到,Windows上各种设备、分区、虚拟对象都是挂载到根“\”下的,通过这个树可以访问各种设备、驱动、文件系统等。

Windows与Linux不同的就是把这些对象又重新封装了一层WindowsAPI,对外以设备、盘符、文件等等表现出来,重新封装WindowsAPI的目的是为了兼容性,而设备、盘符、文件这些是为了让普通用户更好理解。

文件类型

Linux文件类型

Inode

概述

inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础。

文件储存在硬盘上,硬盘的最小存储单位叫做”扇区”(Sector)。每个扇区储存512字节(相当于0.5KB)。操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个”块”(block)。这种由多个扇区组成的”块”,是文件存取的最小单位。”块”的大小,最常见的是4KB,即连续八个 sector组成一个 block。

文件数据都储存在”块”中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。

inode的内容

inode包含文件的元信息,具体来说有以下内容:

  • 文件的字节数
  • 文件拥有者的User ID
  • 文件的Group ID
  • 文件的读、写、执行权限
  • 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
  • 链接数,即有多少文件名指向这个inode
  • 文件数据block的位置

可以用stat命令,查看某个文件的inode信息:

1
2
3
4
5
6
7
8
9
$ stat README.md
File: README.md
Size: 15 Blocks: 0 IO Block: 4096 regular file
Device: fh/15d Inode: 30680772461790950 Links: 1
Access: (0777/-rwxrwxrwx) Uid: ( 1000/ hearing) Gid: ( 1000/ hearing)
Access: 2019-08-16 20:56:58.121431000 +0800
Modify: 2019-08-16 20:56:58.121431000 +0800
Change: 2019-08-16 20:56:58.121431000 +0800
Birth: -

总之,除了文件名以外的所有文件信息,都存在inode之中。至于为什么没有文件名,下文会有详细解释。

inode的大小

inode也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。

每个inode节点的大小,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

查看每个硬盘分区的inode总数和已经使用的数量,可以使用df -i命令。

查看每个inode节点的大小,可以用如下命令:sudo dumpe2fs -h /dev/hda | grep "Inode size"

由于每个文件都必须有一个inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况。这时,就无法在硬盘上创建新文件。

inode号码

每个inode都有一个号码,操作系统用inode号码来识别不同的文件。Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。

表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。

使用ls -i命令,可以看到文件名对应的inode号码:

1
2
$ ls -i README.md
30680772461790950 README.md

目录文件

Unix/Linux系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。

目录文件的结构非常简单,就是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的inode号码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ls命令只列出目录文件中的所有文件名:
$ ls
HybridAccessory README.md _config.yml db.json gulpfile.js node_modules package-lock.json package.json scaffolds source themes

# ls -i命令列出整个目录文件,即文件名和inode号码:
$ ls -i
281474976845551 HybridAccessory 5348024557503872 db.json 281474976858064 package-lock.json 281474976858853 source
30680772461790950 README.md 281474976845560 gulpfile.js 281474976858065 package.json 281474976859117 themes
1125899906905575 _config.yml 281474976845561 node_modules 281474976858849 scaffolds

# ls -l命令列出文件的详细信息:
$ ls -l
total 280
drwxrwxrwx 1 hearing hearing 4096 Jul 22 14:06 HybridAccessory
-rwxrwxrwx 1 hearing hearing 15 Aug 16 20:56 README.md
-rwxrwxrwx 1 hearing hearing 2189 Aug 19 12:34 _config.yml
-rwxrwxrwx 1 hearing hearing 174 Aug 27 17:55 db.json
-rwxrwxrwx 1 hearing hearing 1596 Jul 22 14:06 gulpfile.js
drwxrwxrwx 1 hearing hearing 4096 Jul 22 14:06 node_modules
-rwxrwxrwx 1 hearing hearing 277830 Jul 22 14:06 package-lock.json
-rwxrwxrwx 1 hearing hearing 686 Jul 22 14:06 package.json
drwxrwxrwx 1 hearing hearing 4096 Jul 22 14:06 scaffolds
drwxrwxrwx 1 hearing hearing 4096 Jul 29 11:35 source
drwxrwxrwx 1 hearing hearing 4096 Aug 16 20:51 themes

目录文件的读权限(r)和写权限(w),都是针对目录文件本身。由于目录文件内只有文件名和inode号码,所以如果只有读权限,只能获取文件名,无法获取其他信息,因为其他信息都储存在inode节点中,而读取inode节点内的信息需要目录文件的执行权限(x)。

查看目录大小:

1
2
$ du -h --max-depth=1
$ du -h -d 1

硬链接

一般情况下,文件名和inode号码是”一一对应”关系,每个inode号码对应一个文件名。但是,Unix/Linux系统允许,多个文件名指向同一个inode号码。

这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为”硬链接”(hard link)。

ln命令可以创建硬链接:ln 源文件 目标文件

1
2
3
4
5
6
7
8
9
10
11
12
$ ls -li
total 0
16044073672577795 drwxrwxrwx 1 hearing hearing 4096 Aug 27 18:07 test
14918173765727074 -rw-rw-rw- 1 hearing hearing 0 Aug 27 18:06 test.txt

$ ln test.txt test1

$ ls -li
total 0
16044073672577795 drwxrwxrwx 1 hearing hearing 4096 Aug 27 18:07 test
14918173765727074 -rw-rw-rw- 2 hearing hearing 0 Aug 27 18:06 test.txt
14918173765727074 -rw-rw-rw- 2 hearing hearing 0 Aug 27 18:06 test1

运行上面这条命令以后,源文件与目标文件的inode号码相同,都指向同一个inode。inode信息中有一项叫做”链接数”,记录指向该inode的文件名总数,这时就会增加1。反过来,删除一个文件名,就会使得inode节点中的”链接数”减1。当这个值减到0,表明没有文件名指向这个inode,系统就会回收这个inode号码,以及其所对应block区域。

创建目录时,默认会生成两个目录项:”.”和”..”。前者的inode号码就是当前目录的inode号码,等同于当前目录的”硬链接”;后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的”硬链接”。

1
2
3
4
5
$ ls -ai
9570149208327005 . 1970324837227736 .. 16044073672577795 test 4785074604151560 test2

$ ls -i ../
9570149208327005 Downloads

目录不能创建硬链接(hard link not allowed for directory)。

软链接(快捷方式)

文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B。因此,无论打开哪一个文件,最终读取的都是文件B。这时,文件A就称为文件B的”软链接”(soft link)或者”符号链接(symbolic link)。

这意味着,文件A依赖于文件B而存在,如果删除了文件B,打开文件A就会报错:”No such file or directory”。这是软链接与硬链接最大的不同:文件A指向文件B的文件名,而不是文件B的inode号码,文件B的inode”链接数”不会因此发生变化。

ln -s命令可以创建软链接:ln -s 源文文件或目录 目标文件或目录

1
2
3
4
5
6
7
$ ln -s test2 test1

$ ls -l
total 0
drwxrwxrwx 1 hearing hearing 4096 Aug 27 18:07 test
lrwxrwxrwx 1 hearing hearing 5 Aug 27 18:17 test1 -> test2
-rw-rw-rw- 1 hearing hearing 5 Aug 27 18:09 test2

inode的特殊作用

由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。

  1. 有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用。
  2. 移动文件或重命名文件,只是改变文件名,不影响inode号码。
  3. 打开一个文件以后,系统就以inode号码来识别这个文件,不再考虑文件名。因此,通常来说,系统无法从inode号码得知文件名。

第3点使得软件更新变得简单,可以在不关闭软件的情况下进行更新,不需要重启。因为系统通过inode号码,识别运行中的文件,不通过文件名。更新的时候,新版文件以同样的文件名,生成一个新的inode,不会影响到运行中的文件。等到下一次运行这个软件的时候,文件名就自动指向新版文件,旧版文件的inode则被回收。

文件描述符

概念

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。

Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。

文件描述符、文件、进程间的关系

1.描述:

  • 每个文件描述符会与一个打开的文件相对应
  • 不同的文件描述符也可能指向同一个文件
  • 相同的文件可以被不同的进程打开,也可以在同一个进程被多次打开

2.系统为维护文件描述符,建立了三个表

  • 进程级的文件描述符表
  • 系统级的文件描述符表
  • 文件系统的i-node表
  • 在进程A中,文件描述符1和30都指向了同一个打开的文件句柄(#23),这可能是该进程多次对执行打开操作
  • 进程A中的文件描述符2和进程B的文件描述符2都指向了同一个打开的文件句柄(#73),这种情况有几种可能,1.进程A和进程B可能是父子进程关系;2.进程A和进程B打开了同一个文件,且文件描述符相同(低概率事件=_=);3.A、B中某个进程通过UNIX域套接字将一个打开的文件描述符传递给另一个进程。
  • 进程A的描述符0和进程B的描述符3分别指向不同的打开文件句柄,但这些句柄均指向i-node表的相同条目(#1936),换言之,指向同一个文件。发生这种情况是因为每个进程各自对同一个文件发起了打开请求。同一个进程两次打开同一个文件,也会发生类似情况。

文件描述符限制

“文件描述符”也是一种资源,系统中的每个进程都需要有“文件描述符”才能进行一些操作。

永久修改用户级限制时有三种设置类型:

  • soft 指的是当前系统生效的设置值
  • hard 指的是系统中所能设定的最大值
  • - 指的是同时设置了 soft 和 hard 的值

检查某个进程的文件描述符相关内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 查看指定进程的限制:在 Max open files 那一行,可以看到当前设置中最大文件描述符的数量为1024
$ cat /proc/803/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 8041 8041 processes
Max open files 1024 4096 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 8041 8041 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 40 40
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us

# 查看该进程占用了多少个文件描述符
$ ll /proc/803/fd | wc -l
5

$ ll /proc/803/fd
total 0
lrwx------ 1 hearing hearing 0 Aug 27 20:14 0 -> /dev/tty3
lrwx------ 1 hearing hearing 0 Aug 27 20:14 1 -> /dev/tty3
lrwx------ 1 hearing hearing 0 Aug 27 20:14 10 -> /dev/tty3
lrwx------ 1 hearing hearing 0 Aug 27 20:14 2 -> /dev/tty3

总结

实际应用过程中,如果出现“Too many open files” , 可以通过增大进程可用的文件描述符数量来解决,但往往故事不会这样结束,很多时候,并不是因为进程可用的文件描述符过少,而是因为程序bug,打开了大量的文件连接(web连接也会占用文件描述符)而没有释放。程序申请的资源在用完后及时释放,才是解决“Too many open files”的根本之道。

证书&CA

  • 证书(“digital certificate”或“public key certificate”):它是用来证明某某东西确实是某某东西的东西
  • CA(Certificate Authority):证书授权中心,负责管理和签发证书的第三方机构
  • CA 证书:就是CA颁发的证书

apt&dpkg

概述

在Linux平台下使用源代码进行软件编译可以具有定制化的设置,但对于Linux distribution的发行商来说,毕竟不是每个人都会进行源代码编译的,这个问题将会严重的影响linux平台上软件的发行与推广。

为了解决上述的问题,厂商先在他们的系统上面编译好了用户所需要的软件,然后将这个编译好并可执行的软件直接发布给用户安装。不同的 Linux 发行版使用不同的打包系统,一般而言,大多数发行版分别属于两大包管理技术阵营:Debian 的”.deb”,和 Red Hat的”.rpm”。

dpkg

简介

dpkg - package manager for Debian。dpkg是Debian的一个底层包管理工具,主要用于对已下载到本地和已安装的软件包进行管理。

通过dpkg的机制,Debian提供的软件就能够简单的安装起来,同时能提供安装后的软件信息,只要派生于Debian的其它Linux distributions大多使用dpkg这个机制来管理,包括B2D,Ubuntu,Deepin等。

deb软件包名规则

格式为:Package_Version-Build_Architecture.deb,如:nano_1.3.10-2_i386.deb:

  • 软件包名称(Package Name): nano
  • 版本(Version Number):1.3.10
  • 修订号(Build Number):2
  • 平台(Architecture):i386

dpkg软件包相关文件

  • /etc/dpkg/dpkg.cfg:dpkg包管理软件的配置文件【Configuration file with default options】
  • /var/log/dpkg.log:dpkg包管理软件的日志文件【Default log file (see /etc/dpkg/dpkg.cfg(5) and option –log)】
  • /var/lib/dpkg/available:存放系统所有安装过的软件包信息【List of available packages.】
  • /var/lib/dpkg/status:存放系统现在所有安装软件的状态信息
  • /var/lib/dpkg/info:记安装软件包控制目录的控制信息文件

dpkg数据库

dpkg 使用文本文件作为数据库来维护系统中软件,包括文件清单, 依赖关系, 软件状态, 等等详细的内容,通常在 /var/lib/dpkg 目录下。通常在 status 文件中存储软件状态和控制信息。在 info/ 目录下备份控制文件,并在其下的 .list 文件中记录安装文件清单,其下的 .mdasums 保存文件的 MD5 编码。

使用手册

1
2
3
4
5
6
7
8
9
10
11
# 安装软件包,必须是deb包的完整名称。(软件的安装可被拆分为两个对立的过程“解包”和“配置”)
dpkg -i package-name.deb

# “解包”:解开软件包到系统目录但不配置,如果和-R一起使用,参数可以是一个目录
dpkg --unpack package-name.deb

# “配置”:配置软件包
dpkg --configure package-name.deb

# 列出 deb 包的内容
dpkg -c package-name.deb

安装相关选项:

  • -R:递归地指向特定目录的所有安装包,可以结合-i, -A, –install, –unpack 与 –avail一起使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# --remove,移除软件包,但保留其配置文件
dpkg -r package-name

# --purge,清除软件包的所有文件(removes everything,including conffiles)
dpkg -P package-name

# --list, 查看系统中软件包名符合pattern模式的软件包
dpkg -l package-name-pattern

# --listfiles, 查看package-name对应的软件包安装的文件及目录
dpkg -L package-name

# --print-avail, 显示包的具体信息
dpkg -p package-name

# --status, 查看package-name(已安装)对应的软件包信息
dpkg -s package-name

# --search,从已经安装的软件包中查找包含filename的软件包名称 (Tip:也可使用子命令dpkg-query来进行查询操作)
dpkg -S filename-search-pattern

# 解包
dpkg -X pkg.deb

# 例:列出系统上安装的与dpkg相关的软件包
dpkg -l \*dpkg*

# 例:查看dpkg软件包安装到系统中的文件
dpkg -L dpkg

apt

apt简介

虽然我们在使用dpkg时,已经解决掉了软件安装过程中的大量问题,但是当依赖关系不满足时,仍然需要手动解决,而apt这个工具解决了这样的问题,linux distribution 先将软件放置到对应的服务器中,然后分析软件的依赖关系,并且记录下来,然后当客户端有安装软件需求时,通过清单列表与本地的dpkg以存在的软件数据相比较,就能从网络端获取所有需要的具有依赖属性的软件了。

工作原理

Debian 采用集中式的软件仓库机制,将各式各样的软件包分门别类地存放在软件仓库中,进行有效地组织和管理。然后,将软件仓库置于许许多多的镜像服务器中,并保持基本一致。这样,所有的 Debian 用户随时都能获得最新版本的安装软件包。因此,对于用户,这些镜像服务器就是他们的软件源(Reposity)

然而,由于每位用户所处的网络环境不同,不可能随意地访问各镜像站点。为了能够有选择地访问,在 Debian 系统中,使用软件源配置文件/etc/apt/sources.list列出最合适访问的镜像站点地址。

apt udpate过程:

  1. 执行apt-get update
  2. 程序分析/etc/apt/sources.list
  3. 自动连网寻找list中对应的Packages/Sources/Release列表文件,如果有更新则下载之,存入/var/lib/apt/lists/目录
  4. 然后 apt-get install 相应的包,下载并安装。

用户可以使用“apt-get update”命令刷新软件源,建立更新软件包列表。它会扫描每一个软件源服务器,并为该服务器所具有软件包资源建立索引文件,存放在本地的/var/lib/apt/lists/目录中。使用apt执行安装、更新操作时,都将依据这些索引文件,向软件源服务器申请资源。

apt相关文件

  • var/lib/dpkg/available 文件的内容是软件包的描述信息, 该软件包括当前系统所使用的Debian 安装源中的所有软件包,其中包括当前系统中已安装的和未安装的软件包.
  • /etc/apt/sources.list 记录软件源的地址(当你执行 sudo apt-get install xxx 时,Ubuntu 就去这些站点下载软件包到本地并执行安装)
  • /var/cache/apt/archives 已经下载到的软件包都放在这里(用 apt-get install 安装软件时,软件包的临时存放路径)
  • /var/lib/apt/lists 使用apt-get update命令会从/etc/apt/sources.list中下载软件列表,并保存到该目录

创建apt软件源

概述

以下是我搭建的软件源目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
hearing/
├── bootstraps
│   ├── bootstrap-aarch64
│   ├── bootstrap-aarch64.zip
│   ├── bootstrap-arm.zip
│   ├── bootstrap-i686.zip
│   └── bootstrap-x86_64.zip
├── repository
│   ├── dists
│ | └── stable
│ | ├── InRelease
│ | ├── main
│ | │   ├── binary-aarch64
│ | │   │   └── Packages
│ | │   ├── binary-all
│ | │   │   └── Packages
│ | │   ├── binary-arm
│ | │   │   └── Packages
│ | │   ├── binary-i686
│ | │   │   └── Packages
│ | │   └── binary-x86_64
│ | │   └── Packages
│ | ├── Release
| | ├── Release.gpg
| | └── repo.asc
│   └── pool
│   ├── aarch64
│    | ├── xxx.deb
│   ├── all
│    | ├── xxx.deb
│   ├── arm
│    | ├── xxx.deb
│   ├── i686
│    | ├── xxx.deb
│   └── x86_64
│    ├── xxx.deb
└── tmp
├── xxx.tar.gz
  • bootstraps目录下放的是最终编译成功的zip文件
  • repository是自己制作的软件仓库
  • tmp放置的是上一步编译中下载失败的一些依赖

引用

根据https://wiki.debian.org/DebianRepository/Setup中的说法,仓库分为两种,一种比较简单的是trivial archive,而另外一种复杂的仓库称为official archive。在一个official archive中,典型特征是顶层有个 dists 目录和 pool 目录。这样的好处是:

  • 将所有类型CPU的包列表(Packages或者Packages.gz文件)放在一个文件里面,这样每个机器要获取的包列表就比较小。
  • 不同套件/不同CPU可共用的deb包(主要是那些 _all.deb)和源代码包,也只在 pool/all目录下存放一份。
  • 源代码包(.dsc,orig.tar.xz)有路径存放,这样 dget / apt source 可以取到源代码包。

对应的/etc/apt/sources.list配置如下:

1
deb http://192.168.56.47:80/hearing/repository stable main

配置软件源的格式为:

1
deb|deb-src uri distribution [component1] [component2] [...]

生成Packages

Packages文件包括每个deb包的位置,描述,版本等信息,生成命令:

1
2
dpkg-scanpackages all/ /dev/null| gzip -9c > dists/stable/main/binary-all/Packages.gz
dpkg-scanpackages all/ /dev/null| > dists/stable/main/binary-all/Packages

生成Release

Release文件里面包含了 Packages 等文件的大小和校验和(包含MD5/SHA1/SHA256/SHA512 多种值),如果这个文件里面所描述的 Packages 大小与校验和与实际读取到的文件不一致,apt 会拒绝这个仓库。生成命令:

1
apt-ftparchive release $dir > Release

生成Release.gpg 和 InRelease 文件

Release.gpg 是一个签名文件,随同 Release 一起出现的,比较老的客户端只认这两个文件,而 InRelease 是内嵌签名的(也就是说,将原来 Release 的内容和 Release.gpg 的内容揉到一起了,注意这里不是简单地拼到一起),新的客户端才支持这个这个文件,观察一下 Debian 和 Ubuntu 的仓库 ( http://mirrors.ustc.edu.cn/debian/dists/jessie/, http://mirrors.ustc.edu.cn/ubuntu/dists/xenial/ ) ,可以看到 Debian 的仓库只有 Release 和 Release.gpg 这两个文件,而 Ubuntu 仓库里面这三个文件都有。

如何生成这两个文件:

  • 生成自己的gpg key: gpg --list-keys || gpg --gen-key
  • 生成Release.gpg: gpg --armor --detach-sign --sign -o Release.gpg Release
  • 生成InRelease: gpg --clearsign -o InRelease Release

导入公钥

当运行apt update的时候,会出现警告:The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 722D2AFAD8BAD548

也就是说 InRelease / Release.gpg 虽然签名了,但由于这个签名所用的公钥没有被接受,因此还是不能正常使用,有三种解决方法:

  1. 服务端将公钥导出,然后提供给客户端导入:

    1
    2
    3
    4
    5
    # 导出
    gpg --export --armor 722D2AFAD8BAD548 -o my-repo.gpg-key.asc

    # 导入
    sudo apt-key add my-repo.gpg-key.asc
  2. 客户端在执行apt update的时候,添加--allow-insecure-repositories选项;在执行apt install pkg的时候,添加--allow-unauthenticated选项。

  3. 用户修改仓库的配置,改为deb [trusted=yes] http://192.168.56.47:80/hearing/repository stable main

使用手册

  1. apt-get update 更新源
  2. apt-get dist-upgrade 升级系统到相应的发行版(根据 source.list 的配置)
  3. apt-get upgrade 更新所有已经安装的软件包
  4. apt-get install package_name 安装软件包(加上 –reinstall重新安装),apt-get install package_name=version 安装指定版本的软件包
  5. apt-get remove package_name 卸载一个已安装的软件包(保留配置文件)
  6. apt-get purge package_name 移除软件包(删除配置信息)或apt-get –purge remove packagename
  7. apt-get check 检查是否有损坏的依赖
  8. apt-get autoclean:删除你已经删掉的软件(定期运行这个命令来清除那些已经卸载的软件包的.deb文件。通过这种方式,可以释放大量的磁盘空间。可以使用apt-get
    clean以释放更多空间。这个命令会将已安装软件包裹的.deb文件一并删除)
  9. apt-get clean 把安装的软件的备份也删除,不过这样不会影响软件的使用

apt-cache对APT的程序包缓存执行各种操作。apt-cache不会操纵系统状态,但会提供操作以从包元数据中搜索并生成输出。

  1. apt-cache depends packagename 了解使用依赖
  2. apt-cache rdepends packagename 是查看该包被哪些包依赖
  3. apt-cache search packagename 搜索包
  4. apt-cache show packagename 获取包的相关信息,如说明、大小、版本等
  5. apt-cache showpkg packagename 显示软件包的大致信息