Chapter 1 Linux Basis
Linux 简介
GNU: free software + Unix-like OS project, whose kernel is kernel of Linux
GNU + Linux = GNU / Linux OS
composition of Linux OS:
- Kernel
- shell(用户与计算机交流接口)
- file structure
- tools
Linux命令
I/O 重定向:将输出和输入重定向至其他形式(如:stdout 变成 a.txt)
管道:左边的输出作为右边的输入
eg. 终止掉所有名称中带有task的进程:
ps -A | grep task | awk ‘{print $1}’ | xargs kill -9
重定向:把I/O设备改成别的($ cat < inputFile)
Special Linux commands:
- touch:改变文件的时间标签,或者创建一个新文件
- nl:在显示文件(非文件则是标准输出)时添加行号
xargs
awk
sed
grep
ps
kill
Chapter 2 Shell
Shell Basis
shell:命令解释器,可以用于启动、停止、编写程序
是一个用户与Unix/Linux OS内核之间的交互接口
也就是说写完一堆装有机器级代码的脚本文件,然后OS将其交给Shell后Shell解释运行这堆命令
作用
- 主要功能:解释用户在命令提示符下输入的命令
- 提供个性化的用户环境(在Shell的初始化文件完成)
- 是一门解释性的编程语言(Linux命令+程序逻辑结构)
Linux Standard Shell — Bash
支持:向下(OS)兼容,作业(job,相当于一串指令以一定逻辑构成的程序)控制
Shell功能
- 解释命令行
- 启动命令(让内核运行命令)
- I/O重定向
- 管道连接
- 变量维护(定义、使用)
- 环境控制(环境变量:包含OS里一个或多个程序会共同用到的信息)
Shell编程基础
执行方法:
- ch <scriptName> (不支持stdin的读数据功能,不推荐)
- bash <scriptName>
- chmod u+x <scriptName>(更改这个文件的权限为user可执行的) 再 ./<scriptName>
结构:
- 第一行:#!/bin/bash,表示使用的解释器路径
- 不加#!会无法使用Shell内建的命令
- 其他地方(脚本行)写#!会被当作普通注释
- 退出:
- exit (n),n = 0表示执行成功,反之
- 若脚本无exit:脚本返回状态由最后一条命令的执行状态决定
- $?读取最后执行命令的退出码
- 退出码的含义:0表示成功,1-125用户可自己定义,126是文件不可执行,127是没有找到相关命令,128表示接收到一个信号
- 脚本注释:#(#!是个例外)
- 逗号:链接一系列算数操作,结果只保留最右边一个表达式的
变量
- 环境变量:定义和系统工作环境相关的变量
- 用户变量
- 定义变量时,变量名前不需要加$
- 注意定义的时候不能在赋值号左右加上无意义的空格,会认为变量名是个命令
- 不声明变量类型
- 只可读的:readonly <varietyname>
- 使用且与别的东西相连时:${name}


- 内部变量
- 只能使用而无法被修改或定义
- 就是一堆$开头的东西,比如$#, $*, $$(当前进程的进程号)
- $@和$*都是当前脚本的外部参数,区别在于前者用了IFS作为划分符
- 引号们
- 单引号内所有字符都当作普通字符
- 双引号内除了$ \ ‘ “ 四个字符以外其他都当作普通字符
- 倒引号`所括内容先被执行,然后其结果代替原命令的位置
- 位置参数变量
- 在文件内部用的,代表外面运行时传递的一堆参数
- $ ./test a b c
- $n(n from 0 to 9)就是文件名和后面9个参数
- shift命令可以让2-9位置的参数分别变成1-8然后右边再加一个9位置的参数(重新分配命令参数位置,除了$0全部左移)
- 置换参数
- 用途:根据参数的不同情况来给变量赋值
- name=${parameter:-default} : 当parameter已被设置则用parameter赋值,否则用default赋值
- = : 同-,再加上一个给parameter也赋一个default
- +:如果parameter已设置,则用default置换变量,否则不进行置换而使用null字符串
- ? :如果没有设置parameter,则显示default并从Shell中退出
- Bash变量的使用
- 不区分数据类型
- 定义依赖上下文
- 可不可以做算术和比较操作取决于变量是否只由数字构成
- $(command)命令型变量的使用风格
- 等价于倒引号,更推荐使用小括号
表达式
- $((exp)):表示表达式的计算( eg. a=$(($a+1)) )
- $(…):表示执行指令或者获取输出
- expr命令:将它的参数作为一个表达式进行求值
- eg. x = $(expr $x + 1) or `expr $x + 1`
- 注意!!算术运算时一定要把运算数和运算符分开
- 注意:*要用转义符写成 \*
- test condition 或者 [condition] 进行条件测试
- 字符串、数值比较
- 文件操作
- 逻辑操作
- 条件表达式
- 字符串比较
- = / != 表示相等与否
- 单个变量:表示变量是否为空
- -n str 当str长度大于0时返回真
- -z str 长度==0
- 整数比较
- int1 -le/ge/eq/ne(not equal)/gt/lt int2
- 文件操作(见PPT)
- 逻辑操作
- !exp
- exp1 -a exp2 (and)
- exp1 -o exp2 (or)
- 字符串
- 字符串长度
- ${#str}
- expr length $str
- expr “$str” : ‘.*’ #注意冒号前后都有空格
- 正则表达式
- 从str开始位置开始匹配其有多少个字符符合regex的格式:
- expr match ${str} ${regex}
- expr ${str} : ${regex}
- 在str里找substr第一次出现的位置
- expr index str substr
- 提取子串
- ${str:position}
- 如果str是@或者*: 提取从position开始的位置参数
- ${str:position:length}
- 从position位置开始提取length长度的子串
- position默认从0开始
- length > 0: 就是长度; length < 0: 变成了最后一个字符的index
- ${str:(-5):(-3)}: 从倒数第五个开始取,取到倒数第三个。记得要加()
- expr substr $str $position $length(同上)
- 削除子串
- ${str%substr} 削除第一个匹配的
- ${str%%substr} 削除最后一个匹配的
- 子串替换
- ${str/substr/replacement}
- 用replacement换掉str里的第一个substr
- ${str//substr/replacement}
- 用replacement换掉str里的所有substr
- ${str/#substr/replacement} → /%
- 替换前缀/后缀
- …


流程控制
- 分支流程
#注意!左右方括号与内容之间要有一个空格!里面的参数和参数提示符(-le什么的)也要有 if [ $1 -le 10 ];then #... elif [ $1 -le 20 ];then #... fi
case variable in expr1) #case expr1 ;; expr2) #case expr2 ;; *) #default ;; esac
- 循环流程
while [ ... ];do #... done
until [ ... ]; do #if ... == false: do these commands done
for arg in [list]; do #every cycle: take variables in list in sequence and store it in arg done
函数
#define [ function ] <funcname> () {funcbody} [redirection] #funcbody里用“位置参数”的形式访问参数,$0指的是函数名 #必须在调用前定义 #call funcname <parameters list>
数组
#define declare -a var_name #define1 a[11]=23 #index 11 of array: 23, others are initialized with NULL #define2 a=( XXX YYY ZZZ... ) #initialize from 0 #define3 a=([idx1]=XXX, [idx2]=YYY ...) #call echo ${a[11} #"{}" is needed
调试 
sh -n scriptname
- 检测脚本语法错误
-v
- 执行命令前打印命令
-x
- 打印每个命令的执行结果
trap命令
trap <command> <signal>
- 必须放在脚本段的第一行(#!下面)
- 捕捉程序对应的signal,一旦捕捉到就执行command

Chapter 3 Linux programming Env
gcc: GNU Compiler Collection
支持多种编程语言
gcc选项
- 预处理
- -E,直接将输入文件预处理后输出到stdout
- -D name=value: 预定义程序中的name宏
- 编译和警告
- -S, 对输入文件进行预处理和编译处理
- -c, 预处理、编译、汇编
- 不带参数:一步到可执行文件
- -Wall: 警告
- 链接库:-I
- -L dir:指定库所在的目录
- -l library:指定库
- -static:强制使用静态库
- -shared:创建共享库
- 静态库 V.S. 共享库
- 静态库(.a .lib):编译时被链接到目标代码中参与编译,被链接时将库完整拷贝到可执行文件中。
- 占用资源多(每个文件都要拷贝一份)
- 不易于更新(每个使用静态库的程序都需要更新)
- 动态(共享)库(.so .dll):程序运行时由系统动态加载到内存,供程序调用。
Chapter 4 File Operation
文件操作(系统调用):产生一个异常,将用户空间的文件信息传给内存,内存对其进行操作
ext2文件系统

Super Block:描述整个块组的信息,一般只有第一个Block Group有
Block Group: Block在逻辑上被分为一个个组
Inode:一种数据结构,存放文件的数据指针和各属性,存在了一个表里面
Dentry(目录项):包含目录之下的各文件的文件名和Inode号(索引,不是真的Inode)
操作
- open操作:打开或创建一个文件
- Prototype:
#include<fcntl.h> int open(const char* pathname, int flags, ...)
flag: 表示文件的一些属性
eg.

- 文件描述符:已打开文件的索引,通过该值可以在fd_array表中检索相应的文件对象
- unlink操作:删除文件。若是软链接则删除链接,若不是则等待当前进程对其的调用结束之后再删掉。
文件描述符:已打开文件的索引
文件描述符属性控制:fcntl
文件锁:保护共享资源(主要是指线程)的一种机制
文件链接:硬链接和符号链接
- 硬链接:使新文件与源文件指向同一个Inode,当且仅当每一个向Inode的指针都被删除则文件被删除。所有文件内容一致,改一个另一个也要被改
- 软链接(符号链接):新文件指向的是源文件的“路径”,新文件与旧文件的Inode不同,且新文件Inode的内容是旧文件的路径。删除源文件后软链接失效,新文件也被清空。但是软链接依然是存在的,访问软链接的新文件并编辑后源文件又会复活
- 用ln命令进行链接操作,删除链接就直接删新文件就可以
文件权限:
- st_mode
- 0-8: user, group, others访问权限(顺序为rwx),也对应9个字母的表示:rwxr--r--,即st_mode = 111100100
- 9-11: 用户ID,组ID,文件粘住位
- 12-15:7种文件类型
- umask:四位八进制数,后三位表示当前系统默认禁用的权限位(高到低分别对应user, group, others)
- 在设置权限的时候,会自动减去umask
- eg. 创建权限为777的文件,umask0245,则文件的权限变为(777 - 245) = 532
文件扩展名:OS用于标识文件类型的机制。
- 在Linux种,除GCC等一些编译器以外,Linux并不通过文件扩展名,而是文件头部信息来标识文件类型。文件执行权限和文件扩展名无关。
文件I/O
- 标准文件I/O:不依赖于系统内核(so可移植性强),仅对缓冲区进行操作,当缓存区满足一定条件之后再执行系统调用。
- 好处是减少了系统调用及对块设备(以块为单元的设备)的读写操作
- 系统调用I/O:直接跟操作系统交互。
- 好处是少拷贝一次内存,提高系统效率
Chapter 5 Process Management
进程:计算机分配资源的单位
Linux进程的主要类型:
- 交互进程:由Shell启动的进程,在前台面对用户
- 守护进程:一堆后台进程,主要在后台干活,与终端无关
- 批处理进程:多个进程,与终端无联系
进程创建:pid_ fork(void)函数,头文件#include <unistd.h>
返回值:-1: 失败
0: 说明此时进入了子进程
> 0: 此时还在父进程里,返回子进程的进程序号pid
子进程与父进程是相同的一份代码,但是子进程开始的时间点在fork函数之后,即进程变成父与子进程后依次运行
fork调用一次,返回两次
代码的全局数据段只是针对此进程的全局变量
exec族函数:调用内部的可执行文件,也就是说执行一个Linux的shell命令
进程退出: exit() or _exit()
前者调用退出处理函数,然后清除I/O缓存(保证进程的东西写到磁盘上)
后者不干这两件事儿,强制执行exit系统调用
终端:I/O设备的抽象
守护进程不会被exit,一直存在
守护进程与终端无关,在一般进程所在的终端被关闭的时候被创建
进程组:多个进程的集合,由进程组PID表示
控制进程:一个终端的控制进程为由它发起的一系列进程的组长进程
会话期:由终端发起的一个或多个进程组的集合,在终端运行期间所有进程属于会话期
创建守护进程:
- 父进程退出,创建子进程
- 在子进程中创建会话期(setsid();)
- 将当前目录更改为根目录(chdir(“/”);)
- 重设文件掩码(umask(0);)
- 关闭文件描述符(?)
僵尸进程:子进程退出而父进程还没退出,且父进程由于没有苏醒等原因没有回收子进程的资源,则子进程变成僵尸进程,没有任何代码和数据,但是会占一个进程位
pid_t wait(int* status)暂停父进程,等待子进程结束并获取子进程返回状态
status: 保存子进程的返回状态
pid_t: 子进程的pid
进程返回状态:成功,失败,死亡
status被分为三个部分:exit value, core dump flag, signal number
waitpid():非阻塞式wait,用于并发程序,判断子进程完事儿没

重定向
tty:进程的终端设备的抽象接口
程序默认将结果写到文件描述符1(stdout),错误信息写到2(stderr)
Shell来做重定向,而不是程序
重定向输出:将默认文件描述符 > 其他文件描述符
如果不想输出到任何地方:重定向到/dev/null
1 (2)>filename:stdout > filename
重定向符号和文件名不传给文件名
打开文件的时候:系统为进程安排的描述符为此进程对应的struct_fd数组里最小可用的fd
stdin重定向到文件
dup(int fd):将最小可用的fd指向fd指向的文件(有种硬链接的美)
dup2(int oldfd, int newfd): 关掉newfd然后直接再dup(oldfd)(感觉不一定返回的就是输入的newfd)
方法一:close(0)然后open(filename, O_RDONLY),自动给它分配到最小可用的fd:0
方法二:fd = open重定向目标文件,然后close(0),再dup(fd),然后close(fd)
管道通信
管道特点:单向单工,字节流FIFO传输
命名管道:任意进程
匿名管道:父子进程

父子进程共用一个struct_fd数组,并且会自动在子进程里复制现有管道

