Linux程序设计2023 期末复习
Linux基础
Linux是根据GNU通用公共许可证开发的一种免费Unix类型的操作系统
1991年Linus Torvalds编写了第一个版本的Linux内核
安装、开机、硬件、BIOS、LILO、软件
linux分区

- GRUB:GRand Unified Bootloader

linux文件类型
- 普通文件
- character special file
- block special file
- 网络接口 socket
- 符号链接 symbolic link
- 目录
目录结构:所有目录存储在一个虚拟的统一文件系统中
- 物理设备挂载在挂载点上(软盘、硬盘分区、CD-ROM驱动)
基本命令:
- pwd:print working directory 打印工作目录
- cd:切换目录
- mkdir:创建目录
- rmdir:移除目录
- ls:列出目录内容
- touch:修改文件访问时间或文件修改时间(不存在时会创建)
- cp:拷贝文件
- mv:移动和重命名文件
- ln:链接文件
- rm:移除文件
- cat:打印文件内容
- more/less:按页展示文件
文件权限:
三种访问级别:User、Group、Others
三种全写:read、write、execute
查询文件权限:ls -l
修改权限:示例:chmod u+x filename
另一种办法:
默认权限:文件 -rw-r–r– 644;目录 drwxr-xr-x 755
进程:进程是一个正在执行的程序实例,由执行程序、它的当前值、状态信息以及通过操作系统管理此进程执行情况的资源组成。
- 所有进程都是由其他进程启动的,即父子关系,除了init(PID 1)由内核自己启动
- 进程可以由自己或其他进程的信号关闭
根目录下目录:
- /bin:存放系统的基本命令
- /boot:内核、bootloader的配置,包括引导加载程序相关的文件
- /dev:包含设备文件,这些包括终端设备、USB或连接到系统的任何设备
- /etc:系统的配置文件
- /home: 存放用户主目录
- /lib:系统库
- /mnt:作为暂时挂载文件系统的目录,通常用于挂载其他文件系统
- /proc:存放内存中的虚拟文件系统,提供有关系统和进程的信息
- /root: 存放超级用户(root)的主目录
- /sbin:存放系统管理员使用的命令
- /tmp:存放临时文件,该目录下的文件通常会在系统重启后被删除
- /usr:资源文件夹,存放用户程序和文件,包括系统软件、应用程序、库文件和文档等
- /var:存放系统和应用程序的变量数据,例如日志文件、缓存文件和邮件文件等
- /media: 存放可移动设备(如U盘、CD/DVD等)挂载的目录
- /srv: 存放服务的数据目录,例如网站的文件目录
- /opt: 存放第三方软件的安装目录
重定向:
- 0:标准输入,1:标准输出,2:标准错误
- <:重定向输入
- >:重定向输出,>>:重定向追加输出
- 2>
管道:一个进程的输出作为另一个进程的输入
1
2
3ls | wc -l
ls -IF | grep^d
ar t /usr/lib/libc.a | grep print | pr -4 -t正则表达式
Shell编程
基础
Shell定义:用户和操作系统之间的接口,作为核外程序而存在
执行脚本:
- sh script_file 新建进程
- chmod +x script_file 新建进程
- ./script_file 新建进程
- source script_file 使用当前进程
- . script_file 使用当前进程
用户环境:.bash_profile .bash_logout .bashsrc这三个文件
用户变量:用户在shell脚本里定义的变量
1
2var=value 或
read varread命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18echo -n "Enter your name:" #-n:不换行
read name
echo "hello $name, weclome to my program"
exit 0
read -p "Enter your name:" name #-p:直接在read中指定一个提示
if read -t 5 -p "Enter your name:" name #-t:指定一个回复时间5秒
then
echo "hello,$name"
else
echo "too slow"
fi
exit 0
read -n1 -p "Do you want to continue [Y/N]?" answer #-n1:限制输入长度为1
read -s -p "Enter your password:" password #-s:输入不显示在屏幕(字体与背景同色)引号
- 单引号会保持字符原本含义
- 双引号会解析 $ `` \
环境变量:Shell环境提供的变量。通常使用大写字母做名字
参数变量和内部变量
shell语句
字符串比较
1
2
3
4str1 = str2
str1 != str2
-z str
-n str算数比较
1
2
3
4
5
6exp1 -eq exp2
exp1 -ne exp2
exp1 -gt exp2 #大于
exp1 -ge exp2
exp1 -lt exp2 #小于
exp1 -le exp2文件条件测试
1
2
3
4
5-e file #文件存在
-d file #目录
-f file #普通文件
-s file #长度不为零
-r file -w file -x file #读写可执行逻辑操作
1
2
3!exp
exp1 -a exp2 #and
exp1 -o exp2 #orif语句
1
2
3
4
5
6
7
8
9if ["$answer" = "yes"];then
echo "yes"
elif ["$answer" = "no"];then
echo "no"
else
echo "sorry"
exit 1
fi
exit 0case语句
1
2
3
4
5case str in
str1 | str2 | str3) statements1;;
str4 | str5) statements2;;
*) statements6;;
esacfor语句
1
2
3
4for file in $(ls f*.sh);do
lpr $file
done
exit 0while语句和until语句
select语句
1
2
3
4
5
6
7
8
9clear
select item in Continue Finish
do
case "$item" in
Continue);;
Finish) break;; #只有进入break才会退出
*) echo "Wrong choice!";;
esac
done命令表
- command1;command2;… 按顺序执行,前面失败后面继续执行
- command1 && command2 && … 按顺序执行,前面成功才能继续执行
- command1 || command2 || … 按顺序执行,前面失败才会执行下一个
语句块 { }
函数
1
2
3
4
5
6
7
8
9
10
11yesno(){
msg="$1"
def="$2"
while true; do
echo ""
echo "$msg"
echo $def
done
return $def
}
调用函数:func para1 para2杂项命令
算数扩展 $((…))
1
2x=0
x=$(($x+1))参数扩展
1
2
3
4
5
6
7批处理文件 1_tmp,2_tmp,...
i=1
while ["$i" -ne 10];do
touch "${i}_tmp"
i=$(($i+1))
done
exit 0
编译链接
GCC/G++ options
- -E:到预处理
- -S:到编译
- -c:到汇编
- 不加-E-S-c时,到链接
- -o:指定输出文件名
- -g:产生调试工具必需的符号信息
- -O/On:在程序编译、链接过程中进行优化处理
- -Wall:显示所有的警告信息
- -Idir: 指定额外的头文件搜索路径
- -Ldir: 指定额外的库文件搜索路径
- -lname: 链接时搜索指定的库文件
- -DMACRO[=DEFN]: 定义MACRO宏
文件后缀
- .c:C源码
- .h:C头文件
- .i:经过预处理的C源码
- .s:汇编代码
- .S:未预处理的汇编代码
- .o:目标文件(汇编后)
- .a:静态库文件
- .so:动态库文件
- .exe:可执行文件
GDB
静态库与动态库
二者的不同点在于代码被载入的时刻不同:
- 静态库的代码在编译过程中已经被载入可执行程序,因此体积比较大。
- 动态库(共享库)的代码在可执行程序运行时才载入内存,在编译过程中仅简单的引用,因此代 码体积比较小。
- 不同的应用程序如果调用相同的库,那么在内存中只需要有一份该动态库(共享库)的实例。
- 静态库和动态库的最大区别:静态情况下,把库直接加载到程序中,而动态库链接的时候,它 只是保留接口,将动态库与程序代码独立,这样就可以提高代码的可复用度,和降低程序的耦 合度。
静态库:编译链接时,把库文件的代码全部加入可执行文件中,因此生成的文件比较大,但是运行 时也就不需要库文件了,后缀名一般为 .a
- 为什么需要静态库:通过静态库的方式降低复杂度,在升级更新时尽量做到增量更新,但是静 态库会导致复用性降低,磁盘占用高。
动态库:在编译链接时并没有把库文件的代码加入可执行文件中,而是在程序执行时由运行时链接 文件加载库,这样可以节省系统的开销,后缀名一般为 .so
- 动态库的作用: 库文件不在可执行文件中,放置在外侧;升级更新会方便快捷;动态库会存在冲突(版本问题)
gcc/g++在编译时默认使用动态库。无论静态库还是动态库,都是由.o文件构成的
make & Makefile
格式:make [-f Makefile] [option] [target]
hello : main.o kbd.o gcc -o hello main.o kbd.o main.o : main.c defs.h cc -c main.c kbd.o : kbd.c defs.h command.h cc -c kbd.c clean : rm edit main.o kbd.o
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
3. 执行次序:
- make会在当前目录下找名字叫“Makefile” 或 “makefile” 的文件
- 查找文件中的第一个目标文件(target),举例中的hello
- 如果hello文件不存在,或是hello所依赖的文件修改时间要比hello新,就会执行后面所定义的命令来生成hello文件
- 如果hello所依赖的.o文件不存在,那么make会在当前文 件中找目标为.o文件的依赖性,如果找到则再根据那一个 规则生成.o文件。(类似一个堆栈的过程)
- make根据.o文件的规则生成 .o 文件,然后再用 .o 文件生成hello文件
4. 伪目标
- “伪目标”并不是一个文件,只是一个标签,所以make无法生成它的依赖关系和决定它是否要执行,只能通过显示地指明这个“目标”才能让其生效
- “伪目标”的取名不能和文件名重名
- 为了避免和文件重名的这种情况,可以使用一个特殊 的标记“.PHONY”来显示地指明一个目标是“伪目标 ”,向make说明,不管是否有这个文件,这个目标就是“伪目标”
- 伪目标一般没有依赖的文件,但也可以为伪目标指定所依赖的文件
- 伪目标同样可以作为“默认目标”,只要将其放在第一个
5. 多目标:当多个目标同时依赖于一个文件,并且其生成的命令大体类 似,可以使用一个自动化变量“$@”表示着目前规则中所有的目标的集合
```makefile
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
等价于
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput预定义变量:
- $< 第一个依赖文件的名称
- $? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚
- $+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件
- $^ 所有的依赖文件,以空格分开,不包含重复的依赖文件
- $* 不包括扩展名的目标文件名称
- $@ 目标的完整名称
- $% 如果目标是归档成员,则该变量表示目标的归档成员名称
函数
- $(subst from,to,text) 替换text中from为to
- $(strip string) 去除首尾空格和多余空格
- $(dir names…) 获取文件的目录名部分
- $(basename names) 去除文件后缀名后部分
- $(foreach var,list,text) 对list中元素执行text操作
- $(if condition,then-part,else-part) 条件语句
- $(call expression,parm1,parm2,….) 创建一个函数
文件系统
基础
- VFS:Virtual File system Switch :采用标准的Unix系统调用读写位于不同物理介质上的不同文件系统, 使得open()等系统调用不用关心底层的存储介质和文件类型
- 组成:
- 超级块:某一个磁盘的某一个分区的文件系统的信息,记录了文件系统类型和参数
- i-node对象::记录真正的文件,文件存储在磁盘上时是按照索引号访问文件的,软 链接是不同的文件,但是硬链接是相同的inode号,同一个文件
- 文件对象:记录了文件描述符、索引号,不对应真正的文件,文件打开会创建出文件对 象,文件关闭才会释放内核中的文件对象。记录了文件的读写状态
- 目录对象::维护了目录中的逻辑关系,若要通过目录来查找文件,都需要这个对 象。在路径上,无论是目录还是文件,都是一个dentry对象对应到目录包含的i-node上,目录项包 括索引节点编号,目录项名称长度以及名称
- 硬链接:hard link
- 不同的文件名对应于同一个inode
- 不能跨越文件系统
- 对应系统调用link
- 软连接:symbolic link
- 存储被链接文件的文件名(而不是inode)实现链接
- 可跨越文件系统
- 对应系统调用symlink
系统调用
系统调用:Linux内核的对外接口;用户程序和内核之间唯一的接口;提供最小接口
库函数:依赖于系统调用;提供较复杂功能;例如标准I/O库
文件描述符:
- 一个非负整数: int fd;
- 其中0为标准输入,1为标准输出,2为标准错误(即0、1、2被占用)
open/create
1
2
3
4
5
6
7
8
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int open(const char *pathname, mode_t mode);
//成功返回fd,失败返回-1flags为文件访问模式,有O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_TRUNC,O_CREAT,O_EXCL
mode为使用权限,如下:
umask:一种文件保护机制,用法: mode & ~umask
close
1
2
3
int close(int fd);
//关闭成功返回0,失败返回-1read/write
1
2
3
4
5
6
ssize_t read(int fd, void *buf, size_t count);
//返回读到的字节数,读到文件尾为0,出错返回-1
ssize_t write(int fd, const void *buf, size_t count);
//成功返回已写的字节数,出错返回-1例:
1
2
3
4
5
6mycat.c
while ((n=read(STDIN_FILENO, buf, BUFSIZE))>0)
if (write(STDOUT_FILENO, buf, n)!=n)
err_sys("write error");
if (n<0)
err_sys("read_error");lseek:重写定位读写偏移量
1
2
3
4
off_t lseek(int fd, off_t offset, int whence);
//成功返回结果偏移位置,失败返回-1whence有SEEK_SET(文件开始偏移)、SEEK_CUR(从现在位置开始偏移)、SEEK_END(文件结尾偏移)
dup/dup2
1
2
3
4
int dup(int oldfd);
int dup2(int oldfd, int newfd);
//成功返回新fd,失败返回-1用于重定向
fnctl:操作文件描述符
1
2
3
4
5
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);cmd有F_DUPFD、F_GETFD/F_SETFD、F_GETFL/F_SETFL、F_GETOWN/F_SETOWN、F_GETLK/F_SETLK/F_SETLKW(获取/设置文件锁)
ioctl
标准IO库
File Steam
Stream Buffering操作
Stream open/close
输入单个字符:getc,fgetc,getchar
输出单个字符:putc,fputc,putchar
输入一行字符串:fgets,gets
输出一行字符串:fputs,puts
二进制流IO:fread/fwrite
格式化IO:scanf,fscanf,sscanf
格式化IO:printf,fprintf,sprintf
重定位流:fseek(移动指针),ftell(获取指针位置),rewind(将指针移动到开头),fgetpos(获取指针位置),fsetpos(移动指针位置)
刷新流:fflush
流与文件描述符
临时文件
文件属性
获取文件status:stat,fstat,lstat(前两个对于链接文件,返回实际文件信息,第三个返回链接自身信息)
stat结构:
SUID、SGID、Sticky bit
- SUID:会认为文件的执行者是文件拥有者(而不是执行的人),这样运行该程序的用户就可以以文件拥有者的身份执行程序,从而获得文件拥有者的特权。如 su
- SGID:认为文件的执行组是文件所在组(而不是执行的人所在的组);对于一个目录,在该目录下创建文件,会认为是目录拥有者创建。如 sudo
- Sticky bit:用于防止用户删除其他用户拥有的文件或目录,只有该目录的拥有者、文件拥有者和root用户才能删除该目录中的文件。
测试文件权限:access
改变文件权限:chmod/fchmod
改变文件所有者:chown/fchown/lchown
设置文件权限屏蔽字:umask
硬链接:link/unlink
软连接:symlink/readlink
目录处理
目录结构:
创建与删除目录:mkdir/rmdir
切换工作目录:chdir,fchdir
获取路径:getcwd
打开、关闭、读、定位
示例:目录扫描
1
2
3
4
5
6
7
8
9
10
11
12
13Dir *dp;
struct dirent *entry;
if ((dp=opendir(dir)) == NULL)
err_sys("wrong");
while ((entry = readdir(dp)) != NULL){
lstat(entry->d_name,&statbuf);
if (S_ISDIR(statbuf.st_mode))
...
else
...
}
closedir(dp);
文件锁
文件锁分类
- 记录锁:按记录加锁(可以只锁文件的一部分)
- 劝告锁:检查、加锁由应用程序自己控制,不会强制应用程序不允许访问,只是提醒
- 强制锁:检查、加锁由内核控制,影响open、read、write
- 共享锁:可以读
- 排他锁:读写均不可
flock结构
记录锁:fcntl
lockf
linux内核
层次结构
驱动:
- 许多常见驱动的源代码集成在内核源码里
- 也有第三方开发的驱动,可以单独编译成模块.ko
- 编译需要内核头文件的支持
加载模块
- 底层命令
- insmod(装载一个模块,只有超级用户可以使用该命令)
- rmmod(卸载一个模块)
- 高层命令
- modprobe(相当于多次insmod,自动加载依赖)
- modprobe -r (-r:模块闲置不用时,自动卸载模块)
- 底层命令
模块依赖
模块通讯:模块是为了完成某种特定任务而设计的。 其功能比较的单一,为了丰富系统的功能 ,所以模块之间常常进行通信。其之间可以共享变量,数据结构,也可以调用对方提供的功能函数。
模块相关命令:
Linux内核模块与应用程序的区别
注意点:
- 不能使用C库来开发驱动程序
- 没有内存保护机制
- 小内核栈
- 并发上的考虑
例子,最简单的内核模块例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static int __init hello_init(void){
printk(KERN_INFO "Hello world\n");
return 0;
}
srarcit void __exit hello_exit(void){
printk(KERN_INFO "GoodBye\n");
}
module_init(hello_init);
module_exit(hello_exit);
openEuler操作系统
鲲鹏处理器:基于ARMv8-64指令集开发的通用处理器,作为一款现代处理器,它早已经不是仅仅包含ALU的运算单元了。在其物理架构上包含SoC、Chip、DIE、cluster、 core等概念。
鲲鹏920具有L1、L2、L3共三级cache,L1和L2两级cache由各个CPU core独享
鲲鹏处理器支持CPU Core虚拟化、内存虚拟化、中断虚拟化以及SMMU等多项虚拟化技术。
鲲鹏处理器与openEuler操作系统有着非常紧密的联系,openEuler天然地支持鲲鹏 处理器,并能够充分发挥处理器的各种特性。
openEuler出现于2019年年底,是一款通用服务器操作系统,支持x86和ARM等多种处理器架构, 适用于数据库、大数据、云计算、人工智能等各种应用场景。
openEuler是一款基于Linux内核的通用操作系统;为了充分发挥鲲鹏处理器的优势,openEuler在多核调用技术、软硬件协同、轻 量级虚拟化、指令级优化和智能优化引擎等方面做了增强。
多核调度:openEuler使用了先进先出、轮转调度、优先级调度以及CFS 调度算法:
- CFS调度算法使用了时间片和优先级的概念,并且引入了虚拟运行时间,使得操作系统按照当前系统的负载和普通进程的优先级给该进程分配CPU使用的比例,从而确保了普通进程的相对公平
- 在openEuler中,每个处理器都有一个迁移线程(称为migration/CPUID),每个迁移线程都有一个由函数组成的停机工作队列
NUMA-aware Qspinlock:openEuler采用CAN(Compact NUMA-aware Lock)队列代替Qspinlock中的MCS队列:
- CNA队列是MCS队列的一种变体
- MCS将等待获取锁的线程组织在一个队列中,而CNA则将等待获取锁的线程组织为 两个队列:一个主队列,一个辅助队列
- 主队列的线程队头运行在相同的NUMA节点上。辅助队列的线程与主队列队头运行 在不同NUMA节点上
KAE:openEuler通过提供鲲鹏加速引擎(Kunpeng Accelerator Engine,KAE)插件,使能 Kunpeng硬件加速能力,包括:
- 对称/非对称加密
- 数字签名
- 压缩解压缩等算法,用于加速SSL/TLS应用和数据压缩
iSula:iSula为全量的容器软件栈,包括引擎、网络、存储、工具集与容器OS:
- iSulad是一个轻量级容器引擎,采用C/C++语言实现,相比其它容器引擎,它的内存 开销更小,并发性能更高
- iSulad容器引擎主要包括通信模块、镜像模块、运行时模块
A-Tune:自调优工具A-Tune旨在让操作系统能够满足不同应用场景的性能诉求,降低性能调优过程中反复调参的人工成本,提升性能调优效率
- A-Tune整体上是一个C/S架构
- A-Tune目前主要提供智能决策和自动调优两个能力