一、计算机操作系统
1. 并发
- 并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令
- 并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统
- 操作系统通过引入进程和线程,使得程序能够并发运行
2. 共享
- 共享是指系统中的资源可以被多个并发进程共同使用
- 有两种共享方式:互斥共享和同时共享
- 互斥共享的资源称为临界资源,例如打印机等,在同一时刻只允许一个进程访问,需要用同步机制来实现互斥访问
3. 虚拟
- 把一个物理实体转换为多个逻辑实体
- 主要有两种虚拟技术:时(时间)分复用技术和空(空间)分复用技术
- 虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间,每个进程都有各自的地址空间
- 地址空间的页被映射到物理内存,地址空间的页并不需要全部在物理内存中,当使用到一个没有在物理内存的页时,执行页面置换算法,将该页置换到内存中
4. 异步
进程不是一次性执行完毕,而是走走停停,以不可知的速度向前推进
5. OS基本功能
1)进程管理
- 进程控制、进程同步、进程通信、死锁处理、处理机调度等
2)内存管理
- 内存分配、地址映射、内存保护与共享、虚拟内存等
3)文件管理
- 文件存储空间的管理、目录管理、文件读写管理和保护等
4)设备管理
- 完成用户的 I/O 请求,方便用户使用各种设备,并提高设备的利用率
- 包括缓冲管理、设备分配、设备处理、虛拟设备等
6. 系统调用
如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成(int 0x80)
- OS通过接口实现系统用户态 ----> 内核态的转换
Linux 的系统调用主要有以下这些:
7. 宏内核和微内核
1)宏内核
- 宏内核是将操作系统功能作为一个紧密结合的整体放到内核
- 由于各模块共享信息,因此有很高的性能
2微内核
- 由于操作系统不断复杂,因此将一部分操作系统功能移出内核,从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务,相互独立
- 在微内核结构下,操作系统被划分成小的、定义良好的模块,只有微内核这一个模块运行在内核态,其余模块运行在用户态
- 因为需要频繁地在用户态和核心态之间进行切换,所以会有一定的性能损失
8. 操作系统的五大功能:
- 作业管理
- 文件管理
- 存储管理
- 输入输出设备管理
- 进程及处理机管理
8. 中断分类
1)外中断
- 由 CPU 执行指令以外的事件引起,如 I/O 完成中断,表示设备输入/输出处理已经完成,处理器能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等
2)异常(内部异常中断)
- 由 CPU 执行指令的内部事件引起,如非法操作码、地址越界、算术溢出等
- 计算机硬件异常或故障引起的中断
3)陷入(软中断)
- 在用户程序中使用系统调用
9. 中断优先级:
- 中断的优先级说明的是当一个中断正在被处理的时候,处理器能接受的中断的级别
- 中断的优先级也表明了中断需要被处理的紧急程度
- 当处理器在处理某一中断的时候,只有比这个中断优先级高的中断可以被处理器接受并且被处理;优先级比这个当前正在被处理的中断优先级要低的中断将会被忽略
典型的中断优先级如下所示:
- 当发生软件中断时,其他所有的中断都可能发生并被处理
- 当发生磁盘中断时,只有时钟中断和机器错误中断能被处理
机器错误 > 时钟 > 磁盘 > 网络设备 > 终端 > 软件中断
二、Linux系统
面试常见问题:
- 能简单使用 cat,grep,cut 等命令进行一些操作
- 文件系统相关的原理,inode 和 block 等概念,数据恢复
- 硬链接与软链接
- 进程管理相关,僵尸进程与孤儿进程,SIGCHLD
1.常用操作以及概念
快捷键:
- Tab:命令和文件名补全;
- Ctrl+C:中断正在运行的程序;
- Ctrl+D:结束键盘输入(End Of File,EOF)
求助:
- --help:指令的基本用法与选项介绍
- man: manual 的缩写,将指令的具体信息显示出来
- man date:有 DATE(1) 出现,其中的数字代表指令的类型,常用的数字及其类型如下:
- info:info 与 man 类似,但是 info 将文档分成一个个页面,每个页面可以跳转
- doc:/usr/share/doc 存放着软件的一整套说明文件
关机:
- who:在关机前需要先使用 who 命令查看有没有其它用户在线
-
sync:为了加快对磁盘文件的读写速度,位于内存中的文件数据不会立即同步到磁盘,因此关机之前需要先进行 sync 同步操作
-
shutdown
## shutdown [-krhc] 时间 [信息]
-k : 不会关机,只是发送警告信息,通知所有在线的用户
-r : 将系统的服务停掉后就重新启动
-h : 将系统的服务停掉后就立即关机
-c : 取消已经在进行的 shutdown
PATH:
- 可以在环境变量 PATH 中声明可执行文件的路径,路径之间用 “:”分隔
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
sudo:
- sudo 允许一般用户使用 root 可执行的命令,不过只有在 /etc/sudoers 配置文件中添加的用户才能使用该指令
包管理工具:
RPM 和 DPKG 为最常见的两类软件包管理工具:
- RPM 全称为 Redhat Package Manager,最早由 Red Hat 公司制定实施,随后被 GNU 开源操作系统接受并成为许多 Linux 系统的既定软件标准。YUM 基于 RPM,具有依赖管理和软件升级功能。
- 与 RPM 竞争的是基于 Debian 操作系统的 DEB 软件包管理工具 DPKG,全称为 Debian Package,功能方面与 RPM 相似
2.磁盘
磁盘接口
- IDE:IDE(ATA)全称 Advanced Technology Attachment,接口速度最大为 133MB/s,因为并口线的抗干扰性太差,且排线占用空间较大,不利电脑内部散热,已逐渐被 SATA 所取代
-
SATA:
-
全称 Serial ATA,使用串口的 ATA 接口,抗干扰性强,且对数据线的长度要求比 ATA 低很多,支持热插拔等功能
-
SATA-II 的接口速度为 300MB/s,SATA-III 标准可达到 600MB/s 的传输速度
-
SATA 的数据线也比 ATA 的细得多,有利于机箱内的空气流通,整理线材也比较方便
-
-
SCSI(Small Computer System Interface(小型机系统接口)):
-
SCSI 硬盘广为工作站、个人电脑以及服务器所使用
-
采取序列式技术以获得更高的传输速度
-
碟片转速 15000rp/m 的高转速,且传输时 CPU 占用率较低,但是单价也比相同容量的 ATA 及 SATA 硬盘更加昂贵
-
-
SAS(Serial Attached SCSI):
-
是新一代的 SCSI 技术,采取序列式技术以获得更高的传输速度,可达到 6Gb/s(和SATA硬盘相同)
-
也通过缩小连接线改善系统内部空间等
-
磁盘文件名:
- Linux 中每个硬件都被当做一个文件,包括磁盘
- 磁盘以磁盘接口类型进行命名,常见磁盘的文件名如下:
- IDE 磁盘:/dev/hd[a-d]
- SATA/SCSI/SAS 磁盘:/dev/sd[a-p]
3.分区
分区表(磁盘分区表主要有两种格式):
- 一种是限制较多的 MBR 分区表
- 一种是较新且限制较少的 GPT 分区表
MBR(第一个扇区最重要):
- 第一个扇区包括:
- 主要开机记录(Master boot record, MBR),446 bytes
- 分区表(partition table),占 64 bytes,最多只能存储 4 个分区,这 4 个分区为主分区(Primary)和扩展分区(Extended)
- 扩展分区只有一个,它使用其它扇区来记录额外的分区表,因此通过扩展分区可以分出更多分区,这些分区称为逻辑分区
GPT:
- 扇区是磁盘的最小存储单位,旧磁盘的扇区大小通常为 512 bytes,而最新的磁盘支持 4K
- GPT 为了兼容所有磁盘,在定义扇区上使用逻辑区块地址(Logical Block Address, LBA),LBA 默认大小为 512 bytes
- GPT 第 1 个区块记录了主要开机记录(MBR),紧接着是 33 个区块记录分区信息,并把最后的 33 个区块用于对分区信息进行备份
- 这 33 个区块第一个为 GPT 表头纪录,这个部份纪录了分区表本身的位置与大小和备份分区的位置,同时放置了分区表的校验码 (CRC32),操作系统可以根据这个校验码来判断 GPT 是否正确。若有错误,可以使用备份分区进行恢复
- GPT 没有扩展分区概念,都是主分区,每个 LBA 可以分 4 个分区,因此总共可以分 4 * 32 = 128 个分区
- MBR,支持最大卷为2 TB(Terabytes),因此不支持 2.2 TB 以上的硬盘,GPT 则最多支持到 233 TB = 8 ZB
开机检测程序:
- BIOS 不可以读取 GPT 分区表,而 UEFI 可以
4.文件系统
- 对分区进行格式化是为了在分区上建立文件系统
- 一个分区通常只能格式化为一个文件系统
- 磁盘阵列技术(RAID技术)可以将一个分区格式化为多个文件系统
- 原理是利用数组方式来作磁盘组,配合数据分散排列的设计,提升数据的安全性
RAID 0
- RAID 0是最早出现的RAID模式,需要2块以上的硬盘,可以提高整个磁盘的性能和吞吐量
- RAID 0没有提供冗余或错误修复能力,其中一块硬盘损坏,所有数据将遗失
RAID 1
- RAID 1就是镜像,其原理为在主硬盘上存放数据的同时也在镜像硬盘上写一样的数据
- 当主硬盘(物理)损坏时,镜像硬盘则代替主硬盘的工作。因为有镜像硬盘做数据备份,所以RAID 1的数据安全性在所有的RAID级别上来说是最好的
RAID 2
- RAID 0的改良版,以海明码(Hamming Code)的方式将数据进行编码后分区为独立的比特,并将数据分别写入硬盘中。因为在数据中加入了错误修正码(ECC,Error Correction Code),所以数据整体的容量会比原始数据大一些
- RAID2最少要三台磁盘驱动器方能运作
RAID 3
- 采用Bit-interleaving(数据交错存储)技术,它需要通过编码再将数据比特分割后分别存在硬盘中,而将同比特检查后单独存在一个硬盘中,但由于数据内的比特分散在不同的硬盘上,因此就算要读取一小段数据资料都可能需要所有的硬盘进行工作,所以这种规格比较适于读取大量数据时使用
RAID 4
- 它与RAID 3不同的是它在分区时是以区块为单位分别存在硬盘中,但每次的数据访问都必须从同比特检查的那个硬盘中取出对应的同比特数据进行核对,由于过于频繁的使用,所以对硬盘的损耗可能会提高。(块交织技术,Block interleaving)
RAID 5
RAID Level 5是一种储存性能、数据安全和存储成本兼顾的存储解决方案。它使用的是Disk Striping(硬盘分区)技术
RAID 5至少需要三块硬盘,RAID 5不是对存储的数据进行备份,而是把数据和相对应的奇偶校验信息存储到组成RAID5的各个磁盘上,并且奇偶校验信息和相对应的数据分别存储于不同的磁盘上
RAID 5 允许一块硬盘损坏。
实际容量
Size = (N-1) * min(S1, S2, S3 ... SN)
RAID 6
与RAID 5相比,RAID 6增加第二个独立的奇偶校验信息块。两个独立的奇偶系统使用不同的算法,数据的可靠性非常高
两块磁盘同时失效也不会影响数据的使用。
RAID 6 至少需要4块硬盘。
实际容量
Size = (N-2) * min(S1, S2, S3 ... SN)
RAID 10/01(RAID 1+0,RAID 0+1)
- RAID 10(镜像+实际;镜像+实际)是先镜射再分区数据,再将所有硬盘分为两组,视为是RAID 0的最低组合,然后将这两组各自视为RAID 1运作
- RAID 01(实际+实际;镜像+镜像)则是跟RAID 10的程序相反,是先分区再将数据镜射到两组硬盘。它将所有的硬盘分为两组,变成RAID 1的最低组合,而将两组硬盘各自视为RAID 0运作
- 当RAID 10有一个硬盘受损,其余硬盘会继续运作
- RAID 01只要有一个硬盘受损,同组RAID 0的所有硬盘都会停止运作,只剩下其他组的硬盘运作,可靠性较低
- 如果以六个硬盘建RAID 01,镜射再用三个建RAID 0,那么坏一个硬盘便会有三个硬盘脱机
- RAID 10远较RAID 01常用,零售主板绝大部份支持RAID 0/1/5/10,但不支持RAID 01
- RAID 10 至少需要4块硬盘,且硬盘数量必须为偶数
- Disk0和Disk1不能同时损坏,否则数据不能正常读取
- Disk2和Disk3不能同时损坏,否则数据也不能正常读取
- RAID10常被应用到高负载的数据库中,因为它不需要进行校验计算,所以能够进行高速的I/O处理,带来更好的性能
- Disk0和Disk2不能同时故障,此时两个RAID0都故障,数据不能读取
- Disk0和Disk3不能同时故障,此时两个RAID0都故障,数据不能读取
- Disk1和Disk2不能同时故障,此时两个RAID0都故障,数据不能读取
- Disk1和Disk3不能同时故障,此时两个RAID0都故障,数据不能读取???????
文件系统组成:
- 最主要的几个组成部分如下:
- inode:一个文件占用一个 inode,记录文件的属性,同时记录此文件的内容所在的 block 编号;
- block:记录文件的内容,文件太大时,会占用多个 block
-
除此之外还包括:
-
superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等
- block bitmap:记录 block 是否被使用的位图
-
5.文件属性:
1)用户分为三种(对不同的用户有不同的文件权限):
- 文件拥有者
- 群组
- 其它人
使用 ls 查看一个文件时,会显示一个文件的信息
例如
drwxr-xr-x 3 root root 17 May 6 00:14 .config
,对这个信息的解释如下:
- drwxr-xr-x:文件类型以及权限,第 1 位为文件类型字段,后 9 位为文件权限字段
- 3:链接数
- root:文件拥有者
- root:所属群组
- 17:文件大小
- May 6 00:14:文件最后被修改的时间
- .config:文件名
常见的文件类型及其含义有:
- d:目录
- -:文件
- l:链接文件
文件时间有以下三种:
- modification time (mtime):文件的内容更新就会更新
- status time (ctime):文件的状态(权限、属性)更新就会更新
- access time (atime):读取文件时就会更新
9 位的文件权限字段中,每 3 个为一组,共 3 组,每一组分别代表对文件拥有者、所属群组以及其它人的文件权限。一组权限中的 3 位分别为 r、w、x 权限,表示可读、可写、可执行
文件与目录的基本操作:
ls:列出文件或者目录的信息,目录的信息就是其中包含的文件
# ls [-aAdfFhilnrRSt] file|dir
-a :列出全部的文件
-d :仅列出目录本身
-l :以长数据串行列出,包含文件的属性与权限等等数据cd:更换当前目录
# cd [相对路径或绝对路径]
mkdir:创建目录
# mkdir [-mp] 目录名称
-m :配置目录权限
-p :递归创建目录rmdir: 删除目录,目录必须为空
# rmdir [-p] 目录名称
-p :递归删除目录touch:更新文件时间或者建立新文件
# touch [-acdmt] filename
-a : 更新 atime
-c : 更新 ctime,若该文件不存在则不建立新文件
-m : 更新 mtime
-d : 后面可以接更新日期而不使用当前日期,也可以使用 --date="日期或时间"
-t : 后面可以接更新时间而不使用当前时间,格式为[YYYYMMDDhhmm]cp:复制文件。如果源文件有两个以上,则目的文件一定要是目录才行
# cp [-adfilprsu] source destination
-a :相当于 -dr --preserve=all
-d :若来源文件为链接文件,则复制链接文件属性而非文件本身
-i :若目标文件已经存在时,在覆盖前会先询问
-p :连同文件的属性一起复制过去
-r :递归复制
-u :destination 比 source 旧才更新 destination,或 destination 不存在的情况下才复制
--preserve=all :除了 -p 的权限相关参数外,还加入 SELinux 的属性, links, xattr 等也复制了rm:删除文件
# rm [-fir] 文件或目录
-r :递归删除mv:移动文件
# mv [-fiu] source destination
# mv [options] source1 source2 source3 .... directory
-f : force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖
2)修改权限
- 可以将一组权限用数字来表示,此时一组权限的 3 个位当做二进制数字的位,从左到右每个位的权值为 4、2、1,即每个权限对应的数字权值为 r : 4、w : 2、x : 1
例:我们将rwx看成二进制数,如果有则有1表示,没有则有0表示,那么rwx r-x r- -则可以表示成为:111 101 100
例:将 .bashrc 文件的权限修改为 -rwxr-xr--
# chmod [-R] xyz dirname/filename
# chmod 754 .bashrc
- 符号来设定权限
# chmod [ugoa] [+-=] [rwx] dirname/filename
- u:拥有者
- g:所属群组
- o:其他人
- a:所有人
- +:添加权限
- -:移除权限
- =:设定权限例:为 .bashrc 文件的所有用户添加写权限
# chmod a+w .bashrc
3)默认权限:
- 文件默认权限:文件默认没有可执行权限,因此为 666,也就是 -rw-rw-rw- 。
- 目录默认权限:目录必须要能够进入,也就是必须拥有可执行权限,因此为 777 ,也就是 drwxrwxrwx
- 可以通过 umask 设置或者查看默认权限,通常以掩码的形式来表示,例如 002 表示其它用户的权限去除了一个 2 的权限,也就是写权限,因此建立新文件时默认的权限为 -rw-rw-r--
4)目录的权限:
- 拥有文件的 w 权限并不能对文件名进行修改,因为文件名不是存储在一个文件的内容中,而是存储在一个文件所在的目录中
- 目录存储文件列表,一个目录的权限也就是对其文件列表的权限
- 目录的 r 权限表示可以读取文件列表;w 权限表示可以修改文件列表,具体来说,就是添加删除文件,对文件名进行修改;x 权限可以让该目录成为工作目录
- 目录的x 权限是 r 和 w 权限的基础,如果不能使一个目录成为工作目录,也就没办法读取文件列表以及对文件列表进行修改
5)链接:
# ln [-sf] source_filename dist_filename
-s :默认是实体链接,加 -s 为符号链接
-f :如果目标文件存在时,先删除目标文件
6) 实体链接:
- 在目录下创建一个条目,记录着文件名与 inode 编号,这个 inode 就是源文件的 inode
- 删除任意一个条目,文件还是存在,只要引用数量不为 0
- 有以下限制:不能跨越文件系统、不能对目录进行链接
# ln /etc/crontab .
# ll -i /etc/crontab crontab
34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 crontab
34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 /etc/crontab
7)符号链接:
- 符号链接文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为 Windows 的快捷方式
- 当源文件被删除了,链接文件就打不开了
- 因为记录的是路径,所以可以为目录建立符号链接
# ll -i /etc/crontab /root/crontab2
34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 /etc/crontab
53745909 lrwxrwxrwx. 1 root root 12 Jun 23 22:31 /root/crontab2 -> /etc/crontab
8)获取文件内容
cat:取得文件内容
# cat [-AbEnTv] filename
-n :打印出行号,连同空白行也会有行号,-b 不会tac:是 cat 的反向操作,从最后一行开始打印
more:和 cat 不同的是它可以一页一页查看文件内容,比较适合大文件的查看
less:和 more 类似,但是多了一个向前翻页的功能
head:取得文件前几行
# head [-n number] filename
-n :后面接数字,代表显示几行的意思tail: head 的反向操作,只是取得是后几行
od:以字符或者十六进制的形式显示二进制文件
9)指令与文件搜索:
which:指令搜索
# which [-a] command
-a :将所有指令列出,而不是只列第一个whereis:文件搜索。速度比较快,因为它只搜索几个特定的目录
# whereis [-bmsu] dirname/filename
locate:文件搜索。可以用关键字或者正则表达式进行搜索
- locate 使用 /var/lib/mlocate/ 这个数据库来进行搜索,它存储在内存中,并且每天更新一次,所以无法用 locate 搜索新建的文件
- 可以使用 updatedb 来立即更新数据库。
# locate [-ir] keyword
-r:正则表达式find:文件搜索。可以使用文件的属性和权限进行搜索
# find [basedir] [option]
example: find . -name "shadow*"
10)与时间有关的选项:
-mtime n :列出在 n 天前的那一天修改过内容的文件
-mtime +n :列出在 n 天之前 (不含 n 天本身) 修改过内容的文件
-mtime -n :列出在 n 天之内 (含 n 天本身) 修改过内容的文件
-newer file : 列出比 file 更新的文件
+4、4 和 -4 的指示的时间范围如下:
11)与文件拥有者和所属群组有关的选项
-uid n
-gid n
-user name
-group name
-nouser :搜索拥有者不存在 /etc/passwd 的文件
-nogroup:搜索所属群组不存在于 /etc/group 的文件
12)与文件权限和名称有关的选项
-name filename
-size [+-]SIZE:搜寻比 SIZE 还要大 (+) 或小 (-) 的文件。这个 SIZE 的规格有:c: 代表 byte,k: 代表 1024bytes。所以,要找比 50KB 还要大的文件,就是 -size +50k
-type TYPE
-perm mode :搜索权限等于 mode 的文件
-perm -mode :搜索权限包含 mode 的文件
-perm /mode :搜索权限包含任一 mode 的文件
六、压缩与打包
压缩文件名
- Linux 底下有很多压缩文件名,常见的如下
扩展名 | 压缩程序 |
---|---|
*.Z | compress |
*.zip | zip |
*.gz | gzip |
*.bz2 | bzip2 |
*.xz | xz |
*.tar | tar 程序打包的数据,没有经过压缩 |
*.tar.gz | tar 程序打包的文件,经过 gzip 的压缩 |
*.tar.bz2 | tar 程序打包的文件,经过 bzip2 的压缩 |
*.tar.xz | tar 程序打包的文件,经过 xz 的压缩 |
压缩指令:
1)gzip:
- gzip 是 Linux 使用最广的压缩指令,可以解开 compress、zip 与 gzip 所压缩的文件
- 经过 gzip 压缩过,源文件就不存在了
- 有 9 个不同的压缩等级可以使用
- 可以使用 zcat、zmore、zless 来读取压缩文件的内容
$ gzip [-cdtv#] filename
-c :将压缩的数据输出到屏幕上
-d :解压缩
-t :检验压缩文件是否出错
-v :显示压缩比等信息
-# : # 为数字的意思,代表压缩等级,数字越大压缩比越高,默认为 6
2)bzip2:
- 提供比 gzip 更高的压缩比
- 查看命令:bzcat、bzmore、bzless、bzgrep
$ bzip2 [-cdkzv#] filename
-k :保留源文件
3)xz:
- 提供比 bzip2 更佳的压缩比
- 压缩比越高,压缩的时间也越长
- 查看命令:xzcat、xzmore、xzless、xzgrep
$ xz [-dtlkc#] filename
打包:
- 压缩指令只能对一个文件进行压缩,而打包能够将多个文件打包成一个大文件
- tar 不仅可以用于打包,也可以使用 gzip、bzip2、xz 将打包文件进行压缩
$ tar [-z|-j|-J] [cv] [-f 新建的 tar 文件] filename... ==打包压缩
$ tar [-z|-j|-J] [tv] [-f 已有的 tar 文件] ==查看
$ tar [-z|-j|-J] [xv] [-f 已有的 tar 文件] [-C 目录] ==解压缩
-z :使用 zip;
-j :使用 bzip2;
-J :使用 xz;
-c :新建打包文件;
-t :查看打包文件里面有哪些文件;
-x :解打包或解压缩的功能;
-v :在压缩/解压缩的过程中,显示正在处理的文件名;
-f : filename:要处理的文件;
-C 目录 : 在特定目录解压缩
使用方式 | 命令 |
---|---|
打包压缩 | tar -jcv -f filename.tar.bz2 要被压缩的文件或目录名称 |
查 看 | tar -jtv -f filename.tar.bz2 |
解压缩 | tar -jxv -f filename.tar.bz2 -C 要解压缩的目录 |
7. Bash:
- 可以通过 Shell 请求内核提供服务,Bash 正是 Shell 的一种
1)特性:
- 命令历史:记录使用过的命令
- 命令与文件补全:快捷键:tab
- 命名别名:例如 ll 是 ls -al 的别名
- shell scripts
- 通配符:例如 ls -l /usr/bin/X* 列出 /usr/bin 下面所有以 X 开头的文件
2)变量操作:
- =:对一个变量赋值直接使用
- $或${}:对变量取用需要在变量前加上 $
- echo 命令:输出变量使用
$ x=abc
$ echo $x
$ echo ${x}
- 变量内容如果有空格,必须使用双引号或者单引号
- 双引号内的特殊字符可以保留原本特性
例如 x="lang is $LANG",则 x 的值为 lang is zh_TW.UTF-8;
- 单引号内的特殊符就是特殊字符本身
例如 x='lang is $LANG',则 x 的值为 lang is $LANG
- 可以使用 `指令` 或者指令的方式将指令的执行结果赋值给变量
version=(uname -r),则 version 的值为 4.15.0-22-generic
- 可以使用 export 命令将自定义变量转成环境变量,环境变量可以在子程序中使用,所谓子程序就是由当前 Bash 而产生的子 Bash
- Bash 的变量可以声明为数组和整数数字
- 数字类型没有浮点数,如果不进行声明,默认是字符串类型,变量的声明使用 declare 命令:
$ declare [-aixr] variable
-a : 定义为数组类型
-i : 定义为整数类型
-x : 定义为环境变量
-r : 定义为 readonly 类型
- 使用 [ ] 来对数组进行索引操作:
$ array[1]=a
$ array[2]=b
$ echo ${array[1]}
3) 指令搜索顺序:
- 以绝对或相对路径来执行指令,例如 /bin/ls 或者 ./ls ;
- 由别名找到该指令来执行;
- 由 Bash 内置的指令来执行;
- 按 $PATH 变量指定的搜索路径的顺序找到第一个指令来执行
4)数据流重定向:
- 重定向指的是使用文件代替标准输入、标准输出和标准错误输出
- 有一个箭头的表示以覆盖的方式重定向
- 有两个箭头的表示以追加的方式重定向
- 可以将不需要的标准输出以及标准错误输出重定向到 /dev/null,相当于扔进垃圾箱
- 如果需要将标准输出以及标准错误输出同时重定向到一个文件,需要将某个输出转换为另一个输出,例如 2>&1 表示将标准错误输出转换为标准输出
$ find /home -name .bashrc > list 2>&1
1 | 代码 | 运算符 |
---|---|---|
标准输入 (stdin) | 0 | < 或 << |
标准输出 (stdout) | 1 | > 或 >> |
标准错误输出 (stderr) | 2 | 2> 或 2>> |
8.管道指令
- 管道是将一个命令的标准输出作为另一个命令的标准输入,在数据需要经过多个步骤的处理之后才能得到我们想要的内容时就可以使用管道
- 在命令之间使用“|”分隔各个管道命令
$ ls -al /etc | less
1)提取指令:
cut:对数据进行切分,取出想要的部分;切分过程一行一行地进行
$ cut
-d :分隔符
-f :经过 -d 分隔后,使用 -f n 取出第 n 个区间
-c :以字符为单位取出区间例:
$ last: 显示登入者的信息,取出用户名
root pts/1 192.168.201.101 Sat Feb 7 12:35 still logged in
root pts/1 192.168.201.101 Fri Feb 6 12:13 - 18:46 (06:33)
root pts/1 192.168.201.254 Thu Feb 5 22:37 - 23:53 (01:16)$ last | cut -d ' ' -f 1
例:
将export 输出的信息,取出第 12 字符以后的所有字符串
$ export
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/home/dmtsai"
declare -x HOSTNAME="study.centos.vbird"
.....(其他省略).....$ export | cut -c 12-
2)排序指令
sort :用于排序
$ sort [-fbMnrtuk] [file or stdin]
-f :忽略大小写
-b :忽略最前面的空格
-M :以月份的名字来排序,例如 JAN,DEC
-n :使用数字
-r :反向排序
-u :相当于 unique,重复的内容只出现一次
-t :分隔符,默认为 tab
-k :指定排序的区间例:
/etc/passwd 文件内容以 : 来分隔,要求以第三列进行排序
$ cat /etc/passwd | sort -t ':' -k 3
root:x:0:0:root:/root:/bin/bash
dmtsai:x:1000:1000:dmtsai:/home/dmtsai:/bin/bash
alex:x:1001:1002::/home/alex:/bin/bash
arod:x:1002:1003::/home/arod:/bin/bash
uniq:将重复的数据只取一个
$ uniq [-ic]
-i :忽略大小写
-c :进行计数例:
取得每个人的登录总次数
$ last | cut -d ' ' -f 1 | sort | uniq -c
1
6 (unknown
47 dmtsai
4 reboot
7 root
1 wtmp
3)双向输出重定向
- 输出重定向会将输出内容重定向到文件中,而 tee 不仅能够完成这个功能,还能保留屏幕上的输出
- tee 指令:一个输出会同时传送到文件和屏幕上
$ tee [-a] file
4)字符转换指令
- tr 用来删除一行中的字符,或者对字符进行替换
$ tr [-ds] SET1 ...
-d : 删除行中 SET1 这个字符串例:将 last 输出的信息所有小写转换为大写
$ last | tr '[a-z]' '[A-Z]'
- col 将 tab 字符转为空格字符
$ col [-xb]
-x : 将 tab 键转换成对等的空格键
- expand 将 tab 转换为一定数量的空格,默认是 8 个
$ expand [-t] file
-t :tab 转为空格的数量
- join 将有相同数据的那一行合并在一起
$ join [-ti12] file1 file2
-t :分隔符,默认为空格
-i :忽略大小写的差异
-1 :第一个文件所用的比较字段
-2 :第二个文件所用的比较字段
- paste 直接将两行粘贴在一起
$ paste [-d] file1 file2
-d :分隔符,默认为 tab
5)分区指令
- split 将一个文件划分成多个文件
$ split [-bl] file PREFIX
-b :以大小来进行分区,可加单位,例如 b, k, m 等
-l :以行数来进行分区。
- PREFIX :分区文件的前导名称
9.正则表达式:
1)grep
- g/re/p(globally search a regular expression and print),使用正则表示式进行全局查找并打印
$ grep [-acinv] [--color=auto] 搜寻字符串 filename
-c : 统计匹配到行的个数
-i : 忽略大小写
-n : 输出行号
-v : 反向选择,也就是显示出没有 搜寻字符串 内容的那一行
--color=auto :找到的关键字加颜色显示例1:把含有 the 字符串的行提取出来(注意默认会有 --color=auto 选项,因此以下内容在 Linux 中有颜色显示 the 字符串)
$ grep -n 'the' regular_express.txt
8:I can't finish the test.
12:the symbol '*' is represented as start.
15:You are the best is mean you are the no. 1.
16:The world Happy is the same with "glad".
18:google is the best tools for search keyword例2:正则表达式 a{m,n} 用来匹配字符 a m~n 次,这里需要将 { 和 } 进行转义,因为它们在 shell 是有特殊意义的
$ grep -n 'a\{2,5\}' regular_express.txt
2)printf
- 用于格式化输出。它不属于管道命令,在给 printf 传数据时需要使用 $( ) 形式
$ printf '%10s %5i %5i %5i %8.2f \n' $(cat printf.txt)
DmTsai 80 60 92 77.33
VBird 75 55 80 70.00
Ken 60 90 70 73.33
3)awk
-
awk 每次处理一行,处理的最小单位是字段
-
每个字段的命名方式为:$n,n 为字段号,从 1 开始,$0 表示一整行
例1:取出最近五个登录用户的用户名和 IP。首先用 last -n 5 取出用最近五个登录用户的所有信息,可以看到用户名和 IP 分别在第 1 列和第 3 列,我们用 $1 和 $3 就能取出这两个字段,然后用 print 进行打印
$ last -n 5
dmtsai pts/0 192.168.1.100 Tue Jul 14 17:32 still logged in
dmtsai pts/0 192.168.1.100 Thu Jul 9 23:36 - 02:58 (03:22)
dmtsai pts/0 192.168.1.100 Thu Jul 9 17:23 - 23:36 (06:12)
dmtsai pts/0 192.168.1.100 Thu Jul 9 08:02 - 08:17 (00:14)
dmtsai tty1 Fri May 29 11:55 - 12:11 (00:15)$ last -n 5 | awk '{print $1 "\t" $3}'
dmtsai 192.168.1.100
dmtsai 192.168.1.100
dmtsai 192.168.1.100
dmtsai 192.168.1.100
dmtsai Fri
- 可以根据字段的某些条件进行匹配,例如匹配字段小于某个值的那一行数据
$ awk '条件类型 1 {动作 1} 条件类型 2 {动作 2} ...' filename
例1:/etc/passwd 文件第三个字段为 UID,对 UID 小于 10 的数据进行处理
$ cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t " $3}'
root 0
bin 1
daemon 2
- awk 变量:
变量名称 | 代表意义 |
---|---|
NF | 每一行拥有的字段总数 |
NR | 目前所处理的是第几行数据 |
FS | 目前的分隔字符,默认是空格键 |
例1:显示正在处理的行号以及每一行有多少字段
$ last -n 5 | awk '{print $1 "\t lines: " NR "\t columns: " NF}'
dmtsai lines: 1 columns: 10
dmtsai lines: 2 columns: 10
dmtsai lines: 3 columns: 10
dmtsai lines: 4 columns: 10
dmtsai lines: 5 columns: 9
10.进程管理:
1)查看进程
- ps:查看某个时间点的进程信息
查看自己的进程:
# ps -l
查看系统所有进程:
# ps aux
查看特定的进程:
# ps aux | grep threadx
- pstree:查看进程树
示例:查看所有进程
# pstree -A
- top:实时显示进程信息
两秒钟刷新一次
# top -d 2
- netstat:查看占用端口的进程
查看特定端口的进程
# netstat -anp | grep port
进程状态
状态 | 说明 |
---|---|
R | running or runnable (on run queue) 正在执行或者可执行,此时进程位于执行队列中。 |
D | uninterruptible sleep (usually I/O) 不可中断阻塞,通常为 IO 阻塞。 |
S | interruptible sleep (waiting for an event to complete) 可中断阻塞,此时进程正在等待某个事件完成。 |
Z | zombie (terminated but not reaped by its parent) 僵死,进程已经终止但是尚未被其父进程获取信息。 |
T | stopped (either by a job control signal or because it is being traced) 结束,进程既可以被作业控制信号结束,也可能是正在被追踪。 |
2)SIGCHLD
当一个子进程改变了它的状态时(停止运行,继续运行或者退出),有两件事会发生在父进程中:
- 得到 SIGCHLD 信号;
- waitpid() 或者 wait() 调用会返回。
其中子进程发送的 SIGCHLD 信号包含了子进程的信息,比如进程 ID、进程状态、进程使用 CPU 的时间等。
在子进程退出时,它的进程描述符不会立即释放,这是为了让父进程得到子进程信息,父进程通过 wait() 和 waitpid() 来获得一个已经退出的子进程的信息。
-
wait()
pid_t wait(int *status)
- 父进程调用 wait() 会一直阻塞,直到收到一个子进程退出的 SIGCHLD 信号,之后 wait() 函数会销毁子进程并返回
- 如果成功,返回被收集的子进程的进程 ID;如果调用进程没有子进程,调用就会失败,此时返回 -1,同时 errno 被置为 ECHILD
- 参数 status 用来保存被收集的子进程退出时的一些状态,如果对这个子进程是如何死掉的毫不在意,只想把这个子进程消灭掉,可以设置这个参数为 NULL。
-
waitpid()
pid_t waitpid(pid_t pid, int *status, int options)
- 作用和 wait() 完全相同,但是多了两个可由用户控制的参数 pid 和 options
- pid 参数指示一个子进程的 ID,表示只关心这个子进程退出的 SIGCHLD 信号。如果 pid=-1 时,那么和 wait() 作用相同,都是关心所有子进程退出的 SIGCHLD 信号
- options 参数主要有 WNOHANG 和 WUNTRACED 两个选项,WNOHANG 可以使 waitpid() 调用变成非阻塞的,也就是说它会立即返回,父进程可以继续执行其它任务
3)孤儿进程
- 一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程
- 孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。
- 由于孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。
4)僵尸进程
- 一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程
- 僵尸进程通过 ps 命令显示出来的状态为 Z(zombie)
- 系统所能使用的进程号是有限的,如果产生大量僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程
- 要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 进程所收养,这样 init 进程就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程
三、内存管理基础:
1.程序可执行文件的结构:
- 只读部分:包括程序代码(.text)和程序中的常量(.rodata)
- 可读写部分(也就是变量)大致可以分成下面几个部分:
- .data:初始化了的全局变量和静态变量???????
- .bss:即 Block Started by Symbol, 未初始化的全局变量和静态变量
- heap:堆,使用 malloc, realloc, 和 free 函数控制的变量,堆在所有的线程,共享库,和动态加载的模块中被共享使用
- .stack: 栈,函数调用时使用栈来保存函数现场,自动变量(即生命周期限制在某个 scope 的变量)也存放在栈中
2.各个分区:
- data 和 bss 区(用来存储全局变量和静态变量):
- data 区存放的是初始化过的
- bss 区存放的是没有初始化过的
- 不会占用可执行文件的空间(一般只存储这个区域的长度),但是却会占用内存空间
- 这些变量没有定义,因此可执行文件中不需要存储(也不知道)它们的值,在程序启动过程中,它们的值会被初始化成 0 ,存储在内存中
int val = 3; char string[] = "Hello World";
- 这两个变量的值一开始被存储在 .text 中(因为值是写在代码里面的),在程序启动时会拷贝到 .data 去区中
static int i;
- 不初始化的变量就会被放在 bss 区中
静态变量和全局变量:
全局变量:在一个代码文件(具体说应该一个 translation unit/compilation unit))当中,一个变量要么定义在函数中,要么定义在在函数外面。
- 当定义在函数外面时,这个变量就有了全局作用域,成为了全局变量。全局变量不光意味着这个变量可以在整个文件中使用,也意味着这个变量可以在其他文件中使用(这种叫做 external linkage)
当有如下两个文件时:
a.c:
#include <stdio.h>int a;int compute(void);int main() {a = 1;printf("%d %d\n", a, compute());return 0; }
b.c:
int a;int compute(void) {a = 0;return a; }
- 在 Link 过程中会产生重复定义错误,因为有两个全局的 a 变量,Linker 不知道应该使用哪一个。为了避免这种情况,就需要引入 static
静态变量:指使用 static 关键字修饰的变量,static 关键字对变量的作用域进行了限制,具体的限制如下:
- 在函数外定义:全局变量,但是只在当前文件中可见(叫做 internal linkage)
- 在函数内定义:全局变量,但是只在此函数内可见(同时,在多次函数调用中,变量的值不会丢失)
- (C++)在类中定义:全局变量,但是只在此类中可见
对于全局变量来说,为了避免上面提到的重复定义错误,我们可以在一个文件中使用 static,另一个不使用。这样使用 static 的就会使用自己的
a
变量,而没有用 static 的会使用全局的a
变量。当然,最好两个都使用 static,避免更多可能的命名冲突。extern:是C 语言中另一个关键字,用来指示变量或函数的定义在别的文件中,使用 extern 可以在多个源文件中共享某个变量
static和extern同时使用的两种情况:
static int m; extern int m;
- 运行正常
extern int m; static int m;
- 这种情况的行为是未定义的,编译器也会给出警告
3.栈:
- 栈是用于存放本地变量,内部临时变量以及有关上下文的内存区域
- 程序在调用函数时,操作系统会自动通过压栈和弹栈完成保存函数现场等操作,不需要程序员手动干预
- 栈是一块连续的内存区域,栈顶的地址和栈的最大容量是系统预先规定好的。能从栈获得的空间较小
- 如果申请的空间超过栈的剩余空间时,例如递归深度过深,将提示stackoverflow
- 栈有2种分配方式:静态分配和动态分配
- 静态分配是编译器完成的,比如局部变量的分配
- 动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现
4.堆:
- 堆是用于存放除了栈里的东西之外所有其他东西的内存区域
- 当使用malloc和free就是在操作堆中的内存
- 对于堆来说,释放工作由程序员控制,容易产生memory leak
- 堆是向高地址扩展的数据结构,是不连续的内存区域
- 堆的大小受限于计算机系统中有效的虚拟内存
- 对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,永远都不可能有一个内存块从栈中间弹出
- 堆都是动态分配的,没有静态分配的堆
5.内存分配:
- 虚拟地址:用户编程时将代码(或数据)分成若干个段,每条代码或每个数据的地址由段名称 + 段内相对地址构成,这样的程序地址称为虚拟地址
- 逻辑地址:虚拟地址中,段内相对地址部分称为逻辑地址
-
物理地址:实际物理内存中所看到的存储地址称为物理地址
-
逻辑地址空间:在实际应用中,将虚拟地址和逻辑地址经常不加区分,通称为逻辑地址。逻辑地址的集合称为逻辑地址空间
- 线性地址空间:CPU地址总线可以访问的所有地址集合称为线性地址空间
-
物理地址空间:实际存在的可访问的物理内存地址集合称为物理地址空间
-
MMU(Memery Management Unit内存管理单元):实现将用户程序的虚拟地址(逻辑地址) → 物理地址映射的CPU中的硬件电路
- 基地址:在进行地址映射时,经常以段或页为单位并以其最小地址(即起始地址)为基值来进行计算
- 偏移量:在以段或页为单位进行地址映射时,相对于基地址的地址值
虚拟地址先经过分段机制映射到线性地址,然后线性地址通过分页机制映射到物理地址