Linux程序设计考试重点
2023-08-09 14:53:19 # NJU # Linux程序设计

ch1-1 linux basics

谈谈linux理解

linux是根据 GNU GPL 开发的免费类 Unix 操作系统。开源,受欢迎,支持大多数可用平台

创始人:Linus

发行版包括ubuntu、centos、debian、red hat等

mbr,gpt

mbr

  • 在基于Intel的计算机上必须进行分区
  • 最多四个主分区
  • 一个主分区可以是扩展分区
  • 扩展分区可以容纳无限数量的逻辑分区(Linux:最大59)

gpt

  • 保留的 MBR 是为了在较老的机器上也能读取到磁盘上的信息
  • GPT 是 MBR 不够用了才会选择使用
  • 支持上百个主分区,不需要扩展分区(支持超过128,但是windows限制128)
  • Header记录Entry信息,Entry记录Partition信息,形成三级结构
  • Secondary GPT 是备份,因此 GPT 相较于 MBR 方式会有更多的空间无法使用

image-20230214162723879

文件系统概念

  • 操作系统中负责存取和管理文件的部分
  • VFS, EXT2, EXT3, FAT32

bootloader只要求grub

  • 程序存储在 MBR (第一阶段) 和 /boot/grub (第1.5和第二阶段) 中
  • 了解文件系统结构;无需像LILO一样激活配置
  • 配置文件:/boot/grub/grub.cfg
    • 最重要就是配置内核和init文件的位置
  • 通过grub-install安装在MBR中
1
2
3
4
title Ubuntu, kernel 2.6.20-16-generic
root (hd0,1)
kernel /boot/vmlinuz-2.6.20-16-generic root=UUID=3f784cd9-516f-4808-a601-b19356f6bdea ro quiet splash locale=zh_CN vga=0x318 // 内核与参数
initrd /boot/initrd.img-2.6.20-16-generic // init程序

编译开源软件流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 编译源代码安装软件:automake模式
tar zxvf application.tar.gz
cd application
./configure
make
su - # 只有make install需要切换root权限
make install # 将编译后的二进制码安装到系统

# cmake模式
tar zxvf application.tar.gz
cd application
mkdir build
cd build
cmake .. # cmake后面跟application目录,..即上级目录
make VERBOSE=1 # VERBOSE参数可选,仅多打印一些东西
su -
make install

命令行提示符

  • 可以自己配置
    • 修改环境变量: export SUDO_PS1="\u@\h:\w\$", export PS1="\u@\h:\w\$"
  • $:以普通用户身份登录
  • #:以root身份登录

所有ppt出现的命令

  • passwd:更改密码
  • mkpasswd:生成随机密码
  • date,cal:找出今天的日期并显示日历
  • who,finger:找出还有谁在系统上处于活动状态
  • clear:清除屏幕
  • echo:在屏幕上写一条消息
  • write、wall、talk、mesg
    • write:给其他用户发信息
    • wall:write all, 给所有登录到系统的用户发信息
    • talk:建立聊天session
    • mesg:可以屏蔽用户发来的信息
  • pwd:当前目录
  • cd:更改目录
  • mkdir:创建目录
  • rmdir:删除目录
  • ls -l -i -R -a
  • touch:更新文件的访问, 修改时间
  • cp:复制文件
  • mv:移动并重命名文件
  • ln:链接文件
  • rm:删除文件
  • cat:打印文件内容
  • more/less:逐页显示文件,more不可以回退,less可以回退
  • ps:报告进程状态
  • pstree:显示进程树
  • jobs, fg, bg, ctrl-z:作业控制
    • bg: 后台执行
    • fg: 前台执行
    • ctrl-z: 暂停进程, 可通过fg, bg恢复
  • kill:杀死进程
  • nohup:运行命令,忽略挂起信号
  • nice,renice:修改进程优先级
  • top:查看进程的cpu占用

  • 文件操作

    • 列出目录内容: ls, (dir, vdir 与ls功能类似)
    • 创建特殊文件: mkdir, mknod, mkfifo
      • mknod: 创建设备文件
      • mkfifo: 创建管道
    • 文件操作: cp, mv, rm
    • 修改文件属性: chmod, chown, chgrp, touch
      • chown: 改文件拥有者、用户组
      • chgrp: 更改用户组
    • 查找文件: (locate), find
      • locate: 功能和 find 类似
    • 字符串匹配: grep(egrep)
      • grep: 在文本文件中搜索字符串
      • egrep: grep 扩展
    • 其它: pwd, cd, ar, file, tar, more, less, head, tail, cat
      • ar: 打包库文件
      • file: 显示文件类型
      • more, less: 一页页显示
      • head, tail: 显示文件头部、尾部
  • 进程操作:ps, kill, jobs, fg, bg, nice
  • 其它
    • who, whoami, passwd, su, uname, …
      • whoami: 当前用户名
      • uname: 显示内核信息
    • man

七种文件类型必考

  • 普通文件(regular file):
    • 文本或代码数据;没有特别的内部结构
  • 字符型设备文件(character special file)
  • 块型设备文件(block special file)
    • special files:代表硬件或逻辑设备
    • 位于 /dev 目录
  • 网络接口(socket)
  • 符号链接(symbolic link)
    • 软链接:类似windows上的快捷方式
    • 硬链接:同一个文件有两个文件名,创建出来的硬链接不占用磁盘空间和inode号。
  • 目录(directory):该目录中的文件列表
  • 管道(fifo)

目录结构

image-20230227143638153

  • /boot:内核、bootloader的配置,包括引导加载程序相关的文件。内核的initrd、vmlinux、grub文件位于/boot下。
  • /etc:系统的配置文件所在地,下载软件的配置文件在也在这里,包含所有程序所需的配置文件
  • /bin:程序文件夹,包含二进制可执行文件,例如ls,其实是在执行一个程序;也有一部分程序在/usr/bin(在我的linux上,/bin是/usr/bin的软链接)
  • /mnt挂载目录,临时挂载目录,系统管理员可以挂载文件系统。
  • /sbin:系统二进制文件,但是这个目录下的Linux命令通常由系统管理员使用,对系统进行维护,例如ifconfig/fdisk也有部分程序在/sbin,例如分区命令fdisk
  • /usr:资源文件夹(和编程相关的);编译器、默认的头文件、系统中的库文件,包含二进制文件、库文件、文档和二级程序的源代码
    • /usr/bin中包含用户程序的二进制文件。/bin
    • /usr/sbin中包含系统管理员的二进制文件。/sbin
    • /usr/lib中包含了/usr/bin/usr/sbin用到的库。
    • /usr/local中包含了从源安装的用户程序。
  • /lib:系统库。包含支持位于/bin和/sbin下的二进制文件的库文件;库文件名为 ld*或lib*.so.*
  • /proc:包括系统进行相关信息。这是一个虚拟的文件系统,包含有关正在运行的进程的信息;系统资源以文本信息形式存在。
  • /var:系统里的可变数据,变量文件,并不是存放在磁盘上的数据,一般是存放在内存中的数据。
    • 系统日志文件/var/log
    • 包和数据库文件/var/lib
    • 电子邮件/var/mail
    • 打印队列/var/spool
    • 锁文件/var/lock
    • 多次重新启动需要的临时文件/var/tmp
  • /dev:包含设备文件,这些包括终端设备、USB或连接到系统的任何设备。例如/dev/tty1
  • /tmp:包含系统和用户创建的临时文件,当系统重新启动时,这个目录下的文件都将被删除。
  • /home:用home目录来存储他们的个人档案。
  • /opt:可选的附加应用程序
  • /media:用于挂载可移动设备的临时目录。举例来说,挂载CD-ROM的/media/cdrom,挂载软盘驱动器的/media/floppy
  • /srv:srv代表服务。包含服务器特定服务相关的数据。
  • 修改环境变量PATH,临时修改可以直接PATH=$PATH:/bin,但是要永久生效得修改配置文件/etc/profile

文件权限

  • 三个访问级别:
    1. 用户(User):创建文件的用户
    2. 组(Group):拥有文件的组中的所有用户
    3. 其他(Others):其他
  • 三个权限:
    1. 读取(r):读取文件内容或目录内容
    2. 写(w):更改文件内容或在目录中创建/删除文件
    3. 执行(x):以程序执行文件或使用目录作为活动目录
  • ls -l

image-20230227153229304

  • chmod <who operator what> filename
    • who:
      • u = owner of file
      • g = group
      • o = other users on the system
      • a = all(u + g + o)
    • operator:
      • + = add permission
      • - = remove permission
      • = = clear permissions and set to mode specified
    • what: r, w, x
  • chmod <number> file
  • Default File Permissions
类型 权限 数字权限
File -rw-r—r— 644
Directory drwxr-xr-x 755

进程概念

  • 进程是一个正在执行的程序实例。由执行程序,它的当前值,状态信息以及通过操作系统管理此进程执行情况的资源组成。

man,info

  • man

    • 使用 man 命令,您可以阅读命令的手册页

    • 手册页存储在 /usr/man

  • info

    • 用于阅读文档的程序,有时可以代替手册页

    • 有关信息的信息存储在 /usr/info

linux层次图

image-20230303104322848

image-20230303105118990

ch1-2 linux basics

重定向

  • 标准输入、标准输出、标准错误
    • 对应的文件描述符:0, 1, 2
    • C语言变量:stdin, stdout, stderr
  • <, >, >>, 2>, 2>>, &>
    • 例: kill –HUP 1234 > killout.txt 2> killerr.txt
    • 例: kill –HUP 1234 > killout.txt 2>&1
      • &1: 1号fd, 不加&会重定向到名称为1的文件
      • 为什么需要将标准错误重定向到标准输出: 标准错误没有缓冲区,⽽stdout有
    • <: 重定向 0号fd
    • >: 重定向内容会覆盖掉文件, 默认重定向 1号fd
    • >>: 重定向内容追加到文件末尾, 默认重定向 1号fd
    • 2>: 只重定向 2号fd 的内容
    • &>: 重定向 1,2号fd

管道

  • 一个进程的输出作为另一个进程的输入
  • 存在一个管道文件,这个文件作为输入传递给后面的命令
  • 例:
    • ls | wc –l
    • ls –lF | grep ^d
    • ar t /usr/lib/libc.a | grep printf | pr -4 -t

环境变量

  • 环境变量
    • 操作环境的参数
    • 查看和设置环境变量:echo, env, set
      • env: 显示所有环境变量
      • set: 设置环境变量
  • 例: PATH环境变量
    • echo $PATH
    • /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/song/bin
    • PATH=$PATH:.
    • export PATH

正则表达式

可能会出道简单题

ch2 shell programming

只考bash,知道有其他shell就行

三种执行脚本方法

方法一:

1
sh script_file 

方法二:

1
2
chmod +x script_file(chown, chgrp optionally)
./script_file

方法三:

1
2
source script_file
. script_file

方法一二原理相同:新启bash进程执行脚本

方法三使用当前bash进程执行脚本

read命令

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
40
41
42
43
44
45
46
47
#! /bin/bash
echo -n "Enter your name: " # 参数-n的作用是不换行,echo默认是换行
read name #从键盘输入
echo "hello $name, welcome to my program"
exit 0 #退出shell程序。

#! /bin/bash
read -p "Enter your name:" name #-p参数,允许在read命令行中直接指定一个提示
read -p "Enter a number:" number
echo $number
exit 0

#! /bin/bash
if read -t 5 -p "please enter your name:" name # 5s内输入
then
echo "hello $name, welcome to my script"
else
echo "sorry, too slow"
fi
exit 0

#! /bin/bash
read -n1 -p "Do you want to continue [Y/N] ?" answer # 只读取一个字符
case $answer in
Y|y)
echo "fine, continue";;
N|n)
echo "ok, good bye";;
*)
echo "error choice";;
esac
exit 0

#! /bin/bash
read -s -p "Enter your password: " pass # 不显示输入
echo "your password is $pass"
exit 0

#! /bin/bash
count=1
cat viewFile.sh| while read line
do
echo "$count:$line"
count=$(($count + 1))
done
echo "Total Count:$count"
exit 0

引号的用法

image-20230412225511774

用户变量、环境变量,参数变量

环境变量

  • $HOME: 当前用户的登陆目录
  • $PATH: 以冒号分隔的用来搜索命令的目录清单
  • $PS1:命令行提示符,通常是 “$” 字符
  • $PS2:辅助提示符,用来提示后续输入,通常是 “>” 字符
  • $IFS:输入区分隔符。当 shell 读取输入数据时会把一组字符看成是单词之间的分隔符,通常是空格、制表符、换行符等。

参数变量和内部变量

  • $#: 传递到脚本程序的参数个数
  • $0: 脚本程序的名字
  • $1,$2,..: 脚本程序的参数
  • $*: 一个全体参数组成的清单,它是一个独立的变量,各个参数之间用环境变量IFS中的第一个字符分隔开
  • $@: “$*”的一种变体,它不使用IFS环境变量。

字符串比较

image-20230412230512655

算数比较

image-20230412230755009

文件

image-20230412230824083

逻辑

image-20230412231419320

if语句

1
2
3
4
5
6
7
8
9
10
if [expression]
then
statements
elif [expression]
then
statements
elif...
else
statements
fi
  • 紧凑模式:使用 ; 分割
1
2
3
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

case语句

  • 双分号
1
2
3
4
5
case str in
str1 | str2) statements;;
str3 | str4) statements;;
*) statements;
esac
1
2
3
4
5
6
7
8
9
#!/bin/sh
echo "Is this morning? Please answer yes or no."
read answer
case "$answer" in
yes | y | Yes | YES) echo "Good morning!";;
no | n | No | NO) echo "Good afternoon!";;
*) echo "Sorry, answer not recognized.";;
esac
exit 0

for

  • 适用于对一系列字符串循环处理
1
2
3
4
for var in list
do
statements
done
1
2
3
4
5
#!/bin/sh
for file in $(ls f*.sh); do
lpr $file
done
exit 0

while

1
2
3
4
while [expression]
do
statements
done
1
2
3
4
5
6
7
8
9
10
quit=n
while [ "$quit" != "y"]; do
read menu_choice
case "$menu_choice" in
a) do_something;;
b) do_anotherthing;;
q|Q) quit=y;;
*) echo "Sorry, choice not recognized.";;
esac
done
1
2
3
4
5
6
7
8
a=0
while [ "$a" -le "$LIMIT" ]; do
a=$(($a+1))
if [ "$a" gt 2 ]; then
break # Skip entire rest of loop.
fi
echo -n "$a"
done
  • $(())整数运算,否则会看作字符串

  • 不推荐使用 until

select

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
clear
select item in Continue Finish
do
case "$item" in
Continue) ;;
Finish) break ;;
*) echo "Wrong choice! Please select again!" ;;
esac
done

image-20230412233820317

命令表

命令组合

  • 分号串联:command1 ; command2 ; …

  • &&: 前面成功才会执行后面的命令

  • ||: 前面失败才会执行, 可用作备用命令

  • {statement1; statement2 ; … ;} 会看做一个命令

…….都看了吧

函数

定义时不带参数

1
2
3
4
func()
{
statements
}
  • 局部变量: local关键字
  • 函数的调用: func para1 para2 …
  • 返回值: return

例子

image-20230412235748650

image-20230412235756115

其他

杂项命令

  • break: 从for/while/until循环退出
  • continue: 跳到下一个循环继续执行
  • exit n: 以退出码”n”退出脚本运行
  • return: 函数返回
  • export: 将变量导出到shell,使之成为shell的环境变量
  • set: 为shell设置参数变量
  • unset: 从环境中删除变量或函数
  • trap: 指定在收到操作系统信号后执行的动作
  • “:”(冒号命令): 空命令
  • “.”(句点命令)或source: 在当前shell中执行命令

捕获命令输出

  • $(command) 或 `command`

  • $PWD 与 $(pwd)

参数扩展

image-20230413000143071

即时文档

  • 输入 !CATINPUT! 才会停止
1
2
3
4
#!/bin/bash
cat >> file.txt << !CATINPUT!
Hello, this is a here document.
!CATINPUT!

ch3 Programming Prerequisite

知道什么是elf

  • “ELF” 是 “Executable and Linkable Format” 的缩写,是一种二进制文件格式,用于可执行文件、目标文件和库文件
  • 它是一种标准的二进制文件格式,可用于 Linux 操作系统下的可执行程序

程序设计语言是解释型还是编译型,什么意思

  • 编译型语言是指在程序运行之前,需要先将源代码编译成机器码,然后再运行机器码。

  • 解释型语言则是在程序运行时,逐行解释源代码并执行。

静态库与动态库联系和区别

  • 静态库和动态库都是程序开发中常用的库文件。

  • 静态库在链接时会完整地拷贝至可执行文件中,被多个依赖多次使用就会有多份冗余拷贝。

  • 动态库则在链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存

  • 静态库的扩展名一般为“.a”或“.lib”,而动态库的扩展名一般为“.so”或“.dll”。

java编译过程

Java文件从源文件创建到程序运行要经过两大步骤:

  1. 源文件由编译器编译成字节码(ByteCode)
  2. 字节码由Java虚拟机解释运行

gcc参数

  • -E: 只对源程序进行预处理(调用cpp预处理器)
  • -S: 只对源程序进行预处理、编译
  • -c: 执行预处理、编译、汇编而不链接
  • -o output_file: 指定输出文件名
  • -g: 产生调试工具必需的符号信息
  • -O/On: 在程序编译、链接过程中进行优化处理
  • -Wall: 显示所有的警告信息
  • -Idir: 指定额外的头文件搜索路径
  • -Ldir: 指定额外的库文件搜索路径
  • -lname: 链接时搜索指定的库文件
  • -DMACRO[=DEFN]: 定义MACRO宏

扩展名

.h 头文件

.s 汇编

.o 目标文件

.a 静态库

.so 动态库

gdb(正常不考) 功能,原理

  • 设置断点
  • 监视变量值
  • 单步执行
  • 修改变量值

image-20230413142055943

makefile掌握ppt涉及内容

https://blog.csdn.net/weixin_38391755/article/details/80380786

  • 预定义变量可以看
  • 不考复杂的、函数等

image-20230413142324080

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
OBJECTS = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(OBJECTS)
gcc -o edit $(OBJECTS)
main.o : main.c defs.h
gcc -c main.c
kbd.o : kbd.c defs.h command.h
gcc -c kbd.c
command.o : command.c defs.h command.h
gcc -c command.c
display.o : display.c defs.h buffer.h
gcc -c display.c
insert.o : insert.c defs.h buffer.h
gcc -c insert.c
search.o : search.c defs.h buffer.h
gcc -c search.c
files.o : files.c defs.h buffer.h command.h
gcc -c files.c
utils.o : utils.c defs.h
gcc -c utils.c
clean :
rm edit $(OBJECTS)

ch3-1 Linux System Programming – File System

什么是文件、文件系统

文件:可以写入、读取或两者兼有的对象,文件具有某些属性,包括访问权限和类型

文件系统:操作系统中负责存取和管理文件的部分,是文件及其属性的集合,为引用文件的文件序列号提供了名称空间。

文件系统三层含义

  1. 一种特定的文件格式,例如EXT2, FAT16, FAT32
  2. 指按指定格式进行”格式化”的一块存储介质
  3. 指操作系统中(通常内核中)用来管理文件系统以及对文件进行操作的机制及其实现

VFS

采⽤标准的Unix系统调⽤读写位于不同物理介质上的不同⽂件系统,使得open()等系统调⽤不⽤关⼼底层的存储介质和⽂件类型

  1. 超级块(super block):某一个磁盘的某一个分区的文件系统的信息,记录了文件系统类型和参数。
  2. i-node 对象(i-node object 索引节点):
    • 记录真正的文件,文件存储在磁盘上时是按照索引号访问文件的
  3. 文件对象(file object):记录了文件描述符、索引号,不对应真正的文件,文件打开会创建出文件对象,文件关闭才会释放内核中的文件对象。记录了文件的读写状态。
  4. 目录项对象(dentry object):在路径上,无论是目录还是文件,都是一个dentry对象对应到目录包含的i-node上,目录项包括索引节点编号,目录项名称长度以及名称。

硬链接软链接 重点

  1. 硬链接
    • 不同的文件名对应同一个inode
    • 不能跨越文件系统
    • 对应系统调用link
    • 命令行使用 ln [文件名] [文件名]
    • 应用程序使用 int link(const char oldpath, const char newpath);
  2. 软链接
    • 存储被链接文件的文件名(而不是inode)实现链接
    • 可以跨越文件系统
    • 对应系统调用symlink
    • 命令行使用 ln -s [文件名] [文件名]
    • 应用程序使用 int symlink(const char oldpath, const char newpath);

系统调用和c库的关系 重点

看清题目要求,要求系统调用还是c库

系统调用部分:

  • int open(const char *pathname, int flags, mode_t mode);
    • flags
      • O_RDONLY:只读
      • O_WRONLY:只写
      • O_RDWR:读写
      • O_APPEND:追加模式打开
      • O_TRUNC:覆盖模式
      • O_CREAT:文件不存在则创建
      • O_EXCL:和O_CREAT同时使用,存在时出错
  • int close(int fd);
  • ssize_t read(int fd, void *buf, size_t count);
  • ssize_t write(int fd, const void *buf, size_t count);
  • off_t lseek(int fd, off_t offset, int whence);

    • 用于设置文件读写指针的位置
    • whence: 起始位置
      • SEEK_SET:从头偏移offset
      • SEEK_CUR:从当前偏移offset
      • SEEK_END:从尾偏移offset
  • int dup(int oldfd);

  • int dup2(int oldfd, int newfd);

    • dup2 可以指定新的fd,通常用于重定向
  • int fcntl(int fd, int cmd, struct flock *lock);

    • cmd

      • F_DUPFD:复制文件描述符
      • F_GETFD/F_SETFD:获取/设置文件描述符标志,为解决fork子进程执行其他任务(exec等)导致父进程的文件描述符被复制到子进程中,使得对应文件描述符无法被之后需要的进程获取。设置了这个标记后可以使得子进程在执行exce等命令后释放对应的文件描述符资源。
      • F_GETFL/F_SETFL:获得/设置文件状态标志(open/creat中的flags 参数)
      • F_GETOWN/F_SETOWN: 管理 I/O 可用相关的信号。
      • F_GETLK:获得文件的锁信息
      • F_SETLK:设置文件锁,如果无法获取锁,则返回错误信息
      • F_SETLKW:设置文件锁,如果无法获取锁,则等待直到可以获取为止
    • 文件锁部分要掌握

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
//file:fcntl
int main()
{
pid_t pid;
fd = open( "test.txt",O_RDWR|O_APPEND);
if (fd == -1)
##printf("open err/n");
printf("fd = %d",fd);
printf("fork!/n");
fcntl(fd,F_SETFD, 1);
char *s="ooooooooooooooooooo";
pid = fork();
if(pid == 0)
execl("ass", "./ass", &fd, NULL);
wait(NULL);
write(fd,s,strlen(s));
close(fd);
return 0;
}

//ass 源代码
int main(int argc, char *argv[])
{
int fd;
printf("argc = %d",argc);
fd = *argv[1];
printf("fd = %d",fd);
char *s = "zzzzzzzzzzzzzzzzzzz";
write(fd,(void *)s, strlen(s));
close(fd);
return 0;
}
  • fcntl(fd, F_SETFD, 1);这里将close-on-exec flag设置为true,所以调用execl的时候,fd会关闭

  • 最后的执行结果:test.txt文件中只会有”ooooo”,不会有”zzzzz”

  • int stat(const char *filename, struct stat *buf);

    • 获取文件状态信息
    • 结果写入 buf

    image-20230413192000447

  • int fstat(int fd, struct stat *buf);

    • 用于获取已打开文件的状态信息
  • int lstat(const char *path, struct stat *buf);

    • lstat函数类似于stat函数,但是对于符号链接文件,它返回符号链接本身的状态信息,而不是链接所指向的文件的状态信息

库函数部分

  • 会自动处理缓存
  • FILE *fopen(const char *filename, const char *mode); int fclose(FILE *stream);

    • r 读
    • w 清空写
    • a 追加
    • r+ 读写
    • w+ 清空读写
    • a+ 不清空读写
  • int fclose(FILE *fp);

  • 输入一个字符

    • int getc(FILE *fp):是预定义宏,无函数副作用,更快
    • int fgetc(FILE *fp):常用,在用到函数副作用/函数指针用
  • 输出一个字符到文件

    • int putc(int c, FILE *fp)
    • int fputc(int c, FILE *fp)
  • 输入字符串

    • char *fgets(char *s, int size, FILE *stream);
      • 常用,最多从流中读取并存储size-1个字符,并最后添加一个\0
  • 输出字符串

    • int fputs(const char *s, FILE *stream);
  • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

    • 参数分别为 缓冲区指针,数据块大小,数据块数量,文件指针
  • size fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

  • int scanf(const char *format, ...);
  • int printf(const char *format, ...);

  • int fseek(FILE *stream, long int offset, int whence);

    • 用于移动文件内部的位置指针
    • whence
      • SEEK_SET:从文件开头开始偏移。
      • SEEK_CUR:从当前位置开始偏移。
      • SEEK_END:从文件结尾开始偏移。
  • long ftell(FILE *stream);
    • 获取文件内部的位置指针相对于文件开头的偏移量
  • void rewind(FILE *stream);
    • 将文件内部的位置指针重新指向文件开头
  • int fflush(FILE *stream);
    • 刷新文件流。把流里的数据立刻写入文件

umask

image-20230413153957156

权限、链接系统调用是重点

来自 new bing

  • SUID(Set User ID):当一个可执行文件被设置了SUID权限后,它在执行时就会具有该文件所有者的权限。例如,如果一个文件的所有者是root,那么当一个普通用户执行该文件时,该文件就会以root的身份运行。SUID权限可以让普通用户执行一些只有root才能执行的任务。
  • SGID(Set Group ID):当一个目录被设置了SGID权限后,该目录下新建的文件和子目录都会继承该目录的组ID。例如,如果一个目录的组ID是staff,那么在该目录下新建的文件和子目录都会属于staff组。SGID权限可以让多个用户共享同一个组。
  • Sticky bit:当一个目录被设置了Sticky bit权限后,只有该目录的所有者和root用户才能删除该目录下的文件。例如,/tmp目录就被设置了Sticky bit权限,这样其他用户就不能删除/tmp目录下的文件。

系统调用

  • int chmod(const char *path, mode_t mode);

  • int fchmod(int fildes, mode_t mode);

  • int chown(const char *path, uid_t owner, gid_t group);

  • int fchown(int fd, uid_t owner, gid_t group);

  • int lchown(const char *path, uid_t owner, gid_t group);

  • mode_t umask(mode_t mask);

    • 为进程设置文件存取权限屏蔽字,并返回以前的值

目录操作

  • int mkdir(const char *pathname, mode_t mode);
  • int rmdir(const char *pathname);
  • int chdir(const char *path);
  • int fchdir(int fd);
  • char *getcwd(char *buf, size_t size);
    • 获得当前工作目录的绝对路径

文件锁 重点

  • 记录锁:按记录加锁
  • 劝告锁:
    1. 检查,加锁由应用程序自己控制
    2. 不会强制应用程序不允许访问,只是提醒
  • 强制锁
    1. 检查,加锁由内核控制
    2. 影响 open() read() write()
  • 共享锁:可以读
  • 排他锁:读写均不可

fcntl

  • int fcntl(int fd, int cmd, struct flock *lock);
    • cmd
      • F_GETLK:获得文件的封锁信息
      • F_SETLK:对文件的某个区域封锁或解除封锁
      • F_SETLKW:功能同F_SETLK, wait方式。

image-20230413200327948

ch4 内核

内核概念

  • 操作系统是一系列程序的集合,其中最重要的部分构成了内核

  • 单内核/微内核

    • 单内核是一个很大的进程,内部可以分为若干模块,运行时是一个独立的二进制文件,模块间通讯通过直接调用函数实现
    • 微内核中大部分内核作为独立的进程在特权下运行,通过消息传递进行通信
  • Linux内核的能力:内存管理,文件系统,进程管理,多线程支持,多处理支持,抢占式。
  • Linux内核区别于其他UNIX商业内核的优点
    • 单内核,模块支持
    • 免费/开源
    • 支持多种CPU,硬件支持能力非常强大
    • Linux开发者都是非常出色的程序员
    • 通过学习Linux内核的源码可以了解现代操作系统的实现原理

层次结构

驱动与模块 重点

  • 形式为 .ko,模块.ko
  • insmod <module.ko> [module parameters]

    • 加载模块
  • rmmod

  • lsmod

    • 列出内核中的模块
  • modprobe [-r] <module.name>

    • 载入指定模块及其依赖模块
    • -r 卸载模块
  • modinfo

    • 显示模块信息
  • depmod

    • 用于生成modules.dep文件,该文件记录了所有模块之间的依赖关系

模块依赖

  • 一个模块A引用另一个模块B所导出的符号,我们就说模块B被模块A引用。
  • 如果要装载模块A,必须先要装载模块B。否则,模块B所导出的那些符号的引用就不可能被链接到模块A中。这种模块间的相互关系就叫做模块依赖。

模块之间的通讯

  • 模块是为了完成某种特定任务而设计的。其功能比较的单一,为了丰富系统的功能,所以模块之间常常进行通信。其之间可以共享变量,数据结构,也可以调用对方提供的功能函数。

应用程序与内核的区别

加上注意点一共9点,写出4点即可

c语言程序 内核模块
运行在用户空间 运行在内核空间
入口为 main() 入口由 module_init() 指定
没有出口 出口由 module_exit() 指定
直接运行 通过 insmod
通过 gdb 调试 通过 kdbug,kdb,kgdb等 调试
  • 不能使用 c 库开发驱动程序
  • 没有内存保护机制
  • 小内核栈
  • 并发上的考虑

华为 15分

openEuler 是一款通用服务器操作系统;

支持 x86 和 ARM 等多种处理器架构;

2019年底开源,成为openEuler

鲲鹏处理器:基于ARMv8-64指令集开发的通用处理器,openEuler对 鲲鹏处理器 做了增强,没有神经网络加速

openEuler优化鲲鹏的4个方面

  • 多核调度技术:Numa aware

  • 软硬件协同:KAE

  • 智能优化引擎:A-Tune

  • 轻量级虚拟化:提供 iSulad 轻量级容器全场景解决方案

openEuler使用多队列调度策略

openEuler 通信支持共享内存与消息传递

openEuler 内存管理有预测机制,不是100%准确

openEuler大量使用寄存器,使用精简指令集 risc

编程题

使用C的库函数,编写一个函数void bindiff(char file1,char file2,char *fileo),将文件从file1、file2对应的路径中读取并逐字节比对,将相同的字节输出到fileo对应的文件中。(25分)

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
#include <stdio.h>

void bindiff(char *file1,char *file2,char *file3);

int main(int argc, char *argv[]) {
bindiff(argv[1], argv[2], argv[3]);
}

void bindiff(char *file1,char *file2,char *file3) {
FILE* fp1 = fopen(file1, "r");
FILE* fp2 = fopen(file2, "r");
FILE* fp3 = fopen(file3, "w");
char ch1, ch2;
while(1) {
ch1 = fgetc(fp1);
ch2 = fgetc(fp2);
if (feof(fp1) || feof(fp2)) {
break;
}
if (ch1 == ch2) {
fputc(ch1, fp3);
}
}
fclose(fp1);
fclose(fp2);
fclose(fp3);
}

shell获得用户输入的100个整数,并输出其最大值,最小值,总和。

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
max=0
min=0
sum=0
i=1
while [ $i -le 10 ]
do
read -p "num: " num
if [ $i -eq 1 ]; then
max=$num
min=$num
else
if [ $num -gt $max ]; then
max=$num
fi
if [ $num -lt $min ]; then
min=$num
fi
fi
sum=$((sum + num))
i=$((i + 1))
done

echo "最大值:$max"
echo "最小值:$min"
echo "总和:$sum"

用系统调用实现一个copy.cpp模块,输入两个参数(原文件名,目标文件名),将原文件中的内容复制到目标文件中。并且编写Makefile编译链接上述模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

#define BUFFER_SIZE 4096
int main(int argc, char* argv[]) {
int sourece_fd = open(argv[1], O_RDONLY);
int dest_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC);
int size = 0;
char buf[BUFFER_SIZE];
while((size = read(source_fd, buf, BUFFER_SIZE)) > 0) {
write(dest_fd, buf, size);
}
close(sourece_fd);
close(dest_fd);
return 0;
}
1
2
copy : copy.cpp
g++ -o copy copy.cpp

使用系统调用实现函数half(a, b)将a文件的一半拷贝到b文件,且b仅包含a文件的后一半的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#define BUFFER_SIZE 4096
int main(int argc, char* argv[]) {
int fd1 = open(argv[1], O_RDONLY);
int fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC);
int half_size = lseek(fd1, 0, SEEK_END) / 2;
lseek(fd1, half_size, SEEK_SET);
char buf[BUFFER_SIZE];
int size = 0;
while((size = read(fd1, buf, BUFFER_SIZE)) > 0) {
write(fd2, buf, size);
}
close(fd1);
close(fd2);
}