Linux核心知识全解析
一、目录结构
1. 根目录与系统目录
根目录为 /,核心子目录包括:bin(命令二进制文件)、boot(启动文件)、etc(系统配置文件)、home(普通用户家目录)、root(管理员家目录)、lib(库文件)、usr(用户应用)等。
2. 用户家目录规则
用户名示例:stu、xiaowang。
对应家目录:/home/stu、/home/xiaowang,家目录下可创建 C 语言开发文件/目录(如 main.c、mycode 文件夹)。
特殊路径符号:.(当前目录)、..(上一级目录)、~(家目录)。
3. 路径类型
绝对路径:如 /home/stu/main.c(从根目录开始的完整路径)。
相对路径:基于当前位置的路径(如 cd mycode、cd ..)。
二、文件类型
Windows 后缀区分(.txt、.jpg、.exe 等)
Linux 符号区分:-(普通文件)、d(目录文件)、p(管道)、l(链接文件)、c/b(设备文件)、s(套接字文件)
三、修改权限
1.权限基础逻辑
权限维度:r(读,数字值4)、w(写,数字值2)、x(执行,数字值1)、-(无权限,数字值0)。
归属维度:u(文件所有者)、g(所属组)、o(其他用户)。
2.权限表示方法
符号法:如 rw-r--r--(所有者读/写,组/其他仅读)。
数字法:将归属维度的权限值相加,如 764(u:7=4+2+1、g:6=4+2、o:4=4)。
3.权限修改命令(chmod)
符号法示例:chmod u-x a(移除文件 a 所有者的执行权限)、chmod u+w a.c(给文件 a.c 所有者添加写权限)。
数字法示例:chmod 764 a(设置文件 a 权限为 rwxrw-r--)。
四、基本命令
cd:切换目录 cd /(到根目录)、cd ~(到家目录)、cd mycode(到当前目录下的mycode)
pwd:显示当前工作目录的绝对路径
ls:列出当前目录下的文件/目录
touch:创建空普通文件
mkdir:创建目录(文件夹)
cp:复制文件或目录
rm:删除文件或目录
mv:移动或重命名文件
cat:查看文件内容(全部)
more:分页查看文件(向下翻页)
less:分页查看文件(可上下翻页)
tail:查看文件末尾内容
head:查看文件开头内容
find:查找文件
grep:搜索文件内容
Vim 基础使用
1. 命令模式(默认进入)
2. 插入模式(可输入文本)
3. 末行模式(执行命令)
末行模式常用命令
:w:保存文件
:q:退出(前提是文件未修改)
:wq:保存并退出
:q!:不保存强制退出
模式切换
i:在光标前插入
a:在光标后插入
o:在当前行下方插入新行
I:在行首插入
A:在行尾插入
O:在当前行上方插入新行
进程管理
基本概念
进程:一个正在运行的程序
PID:进程唯一标识符
PCB(进程控制块) / 进程描述符:内核中用于描述进程的数据结构
bash(shell):命令解释器 用来与用户交互
查看进程
ps:查看当前终端 运行的进程 -f -ef显示详细信息
top:动态显示进程信息
进程控制
kill + PID:结束指定进程
kill + -9 +PID: 强制结束进程
Ctrl + C:结束前台运行的进程
Ctrl + Z:暂停前台运行的进程
&:在命令后加 & 使其后台运行
jobs:查看后台运行的任务 显示任务号
fg+%任务号:后台切换至前台
bg+%任务号:前台切换至后台(需要先ctrl+z)
|管道:将一个进程输出的信息传递给另一个进程
man帮助手册
man:查看命令、系统调用、库函数的帮助文档
1:命令
2:系统调用
3:库函数
行号设置
:set nu:显示行号
:set nonu:取消行号
跳转
:n:跳转到第 n 行
gg:跳转到第一行
G:跳转到文件末尾
复制/粘贴/删除
n + yy:复制 n 行
n + dd:剪切/删除 n 行
p:粘贴
u:撤销
Ctrl + r:恢复(反撤销)
x:删除一个字符
r:替换一个字符
R:进入替换模式,按 ESC 结束
cc:清空当前行并进入插入模式
C:清空光标到行末并进入插入模式
关机与重启
关机:shutdown -h now、halt、init 0
重启:shutdown -r now、reboot、init 6
解压命令
tar zxf xx.tar.gz:解压 .tar.gz 文件
运行级别
3:文本模式(无图形界面)
5:图形界面模式
五、编译全流程
编译四阶段
① 预编译:处理#include、#define等预处理指令,展开宏、插入头文件,删除注释
命令:gcc -E main.c -o main.i
输入:main.c(源文件),输出:main.i(预处理后的C文件)
② 编译:将C代码翻译成汇编助记符(如ADD),同时完成语法检查
命令:gcc -S main.i -o main.s
输入:main.i,输出:main.s(汇编文件)
③ 汇编:将汇编代码翻译成机器指令(二进制0101)
命令:gcc -c main.s -o main.o
输入:main.s,输出:main.o(Linux目标文件)/main.obj(Windows目标文件)
④ 链接:合并目标文件、C标准库等,生成可执行程序
命令:gcc main.o -o main
输入:main.o,输出:main(Linux可执行文件)/main.exe(Windows可执行文件)
编译方式分类
一步编译(多文件):直接编译多源文件,自动完成四阶段
命令:gcc -o main main.c add.c
分步编译:先分别编译main.c、add.c生成.o目标文件,再链接生成main
工程自动化:Makefile
作用:管理C语言工程,实现自动化编译,避免手动重复执行gcc命令
核心:编写Makefile文件,定义编译规则,执行make命令自动编译
六、GDB调试核心命令
调试准备
编译时加-g参数生成调试信息:gcc -o test test.c -g
常用调试命令
l(list) 显示源代码
b 行号/函数名 设置断点
info break 查看所有断点
r(run) 运行程序
n(next) 单步执行(不进入函数)
s(step) 单步执行(进入函数)
p 变量名 打印变量值
c(continue) 继续运行到下一个断点
finish 跳出当前函数
bt(backtrace) 查看函数调用栈
q(quit) 退出gdb
七、静态库 vs 共享库
核心定义
静态库 libxx.a 预先编译好的方法的集合 编译链接阶段,直接将库代码打包进可执行文件
共享库 libxx.so 预先编译好的方法的集合 程序运行阶段,仅在可执行文件中做标记,运行时加载
静态库
1. 生成静态库
先将源文件编译为目标文件:gcc -c add.c、gcc -c max.c
用ar命令打包生成静态库:ar crv libfoo.a add.o max.o
ar:静态库打包工具
c:创建新库,r:替换库中文件,v:显示详细过程
生成的静态库命名规则:libxxx.a(lib前缀+库名+.a后缀)
2. 使用静态库
编译链接命令:gcc -o main main.c -L. -lfoo
-L.:指定库文件搜索路径为当前目录(.代表当前目录)
-lfoo:链接名为foo的库(自动补全为libfoo.a,省略lib前缀和.a后缀)
运行可执行程序:./main,直接运行成功,输出add=5
静态库在编译链接阶段就将库代码打包进可执行文件,运行不依赖外部库
共享库
先将源文件编译为目标文件(需加-fPIC生成位置无关代码):gcc -c add.c -o add.o、gcc -c max.c -o max.o
用gcc生成共享库:gcc -shared -fPIC -o libfoo.so add.o max.o
-shared:生成共享库
-fPIC:生成位置无关代码,确保库可在内存任意位置加载
生成的动态库命名规则:libxxx.so(lib前缀+库名+.so后缀)
2. 查看动态库依赖
用ldd命令查看可执行程序依赖的共享库:ldd main
输出显示libfoo.so => /lib/libfoo.so,说明程序成功找到并加载了/lib路径下的libfoo.so
静态库&共享库区别
静态库(libxxx.a):用到的方法包含到可执行程序
链接时机:编译链接阶段,直接将库代码打包进可执行文件
特点:可执行文件体积大,运行不依赖外部库,移植性强,库更新需重新编译程序
共享库(动态库,libxxx.so):不包含,所有程序共享
链接时机:程序运行阶段,仅在可执行文件中做标记,运行时加载库
特点:可执行文件体积小,库更新无需重新编译程序,运行依赖系统中的库文件,需确保库在系统搜索路径中,最好将库文件直接放在 存放库文件的标准目录:/use/lib,/lib...
八、fork
main函数的3个参数
int main(int argc, char *argv[], char *envp[])
1.argc (argument count):命令行参数个数,≥1,程序名字本身也算1个参数。
2.argv (argument vector):字符串指针数组,存放每个命令行参数字符串。
argv[0] = 程序名;argv[1]是第一个传入参数,末尾argv[argc]=NULL。
3.envp (environment pointer):环境变量字符串数组,格式"KEY=VALUE",数组最后以NULL结尾,保存系统全部环境变量(PATH、HOME等)。
printf行缓冲机制(用户态缓冲区)
printf并不会直接把内容打印屏幕,数据先存放到用户缓冲区。
缓冲区刷新(真正输出到屏幕)3种条件:
1. 缓冲区存满,自动输出;
2. 遇到\n换行符 或者调用fflush(stdout)强制刷新;
3. 程序运行结束,自动刷新缓冲区。
fork
1. PCB(进程控制块):内核结构体struct task_struct,管理每一个进程,每个进程拥有唯一pid进程号。
2. fork()系统调用:创建子进程。返回值>0:父进程,返回值是新建子进程的pid,返回值=0:子进程。
3. fork之后:子进程几乎完全复制父进程,代码、数据、用户缓冲区都会复制一份,父子进程是两个独立进程,各自往下执行。
fork写实拷贝
写时复制,创建子进程时并不立刻复制物理内存页;只有当进程对内存做修改的时候,才复制内存页面。页面大小常见:4K。
vfork:子进程直接共享父进程内存空间,不拷贝;子进程必须exit()或者exec(),不能直接return。
僵死进程
子进程先于父进程结束,父进程没有获取子进程的退出码,那么子进程变成僵死进程。解决办法:父进程调用 wait() / waitpid(),回收子进程资源。
九、io文件
系统调用
open,read,write,close 实现在内核中,即内核中的函数,调用时切换成内核态,产生中断陷入内核。库函数在用户空间,直接跳转函数入口就绪执行。
父进程打开的文件,fork后父子进程都可以共享访问,文件偏移量是共享的。
文件表(每个进程私有):每个进程自己拥有一张表,fd‑0:标准输入 stdin, fd‑1:标准输出 stdout,fd‑2:标准错误 stderr,open打开文件,返回最小可用fd编号。fork时,子进程完整复制这张表。
printf()->系统调用write() strlen()->库函数 fopen()->open()
进程替换-exec
在当前进程内部加载并运行新程序,PID保持不变,原有代码直接被替换覆盖。
命令解释器
bash属于shell(命令解释器),接收用户输入,和操作系统内核交互。用户在终端输入外部命令时,bash执行fork+exec:先fork创建子进程,子进程调用exec,把自身替换为要执行的命令程序;内核负责管理所有进程。
十、信号
本质是软中断,用来通知进程发生了某个事件。
发送信号:kill() 函数传入参数:进程pid + 信号编号sig,向指定进程发送信号。
处理信号:signal(),进程收到信号有3种响应方式① 默认处理:SIG_DFL,系统默认行为(终止、暂停等)② 忽略信号:SIG_IGN,直接丢弃该信号,不做处理 ③ 自定义捕获:注册回调函数 void fun(int sig);,收到信号执行自己写的处理函数。
十一、进程间通信IPC
IPC通信方式:管道、信号量、共享内存、消息队列、套接字。
管道
无名管道:依靠fork()创建,文件描述符:fd[0]读端,fd[1]写端,只用于有血缘关系的进程。
有名管道FIFO:有管道文件,任意进程之间通信,用open分别打开读、写。
管道特点:数据存放在内存;读端、写端默认都是阻塞模式,半双工通信。
阻塞与返回规则
① 必须两个进程同时打开管道,一个读、一个写,才能正常工作。
② 管道为空:read()读操作阻塞;管道写满:write()写操作阻塞。
③ 写端全部关闭:read()返回0,表示读到末尾。
④ 读端全部关闭:继续执行write(),会触发异常,产生信号。
信号量
p操作代表获取资源-1,可能阻塞;v操作代表释放资源+1
临界资源:同一时刻只能一个进程使用的资源;临界区:操作该资源的代码片段。
死锁:两个或多个进程/线程在竞争资源时,各自持有部分资源并等待对方释放资源,导致互相等待、谁都无法继续执行的僵持状态。
如何避免死锁:① 破坏互斥条件:尽量使用可共享的资源,或用读写锁让多个读者同时访问;② 破坏请求与保持条件:采用一次性申请所有资源(资源预分配)的方式,进程运行前一次性申请所需全部资源,申请不到则一个都不拿;③ 破坏不可剥夺条件:进程申请不到新资源时,主动释放已持有的资源,稍后重新申请;④ 破坏循环等待条件:对资源进行编号,所有进程必须按编号递增的顺序申请资源,避免形成等待环路。
共享内存
速度最快,多个进程把同一块内核内存映射到自己的用户空间。
十二、线程
进程:一个正在运行的程序,动态,进行资源分配的基本单位。
线程:进程内部的一条执行路径,执行序列,用户创建、管理由内核完成,可以使用多个处理器。
pthread_create(&tid, NULL, 入口函数, 参数):创建线程
pthread_join(tid,NULL):阻塞等待线程结束,回收资源。
pthread_exit(NULL):只退出当前线程。
多线程的程序执行fork()后,子进程只有一条执行路径,fork()所在的那条执行路径。
线程同步
保证线程安全,包括信号量,互斥锁,条件变量,读写锁
信号量:sem_init(&sem, 0,初值); //第二个参数0:用于线程
sem_wait(&sem);//P操作:‑1,资源不足阻塞
sem_post(&sem);//V操作:+1,唤醒阻塞线程
sem_destroy(&sem);
互斥锁:pthread_mutex_init(&m,NULL);
pthread_mutex_lock(&m); //加锁,临界区
pthread_mutex_unlock(&m); //解锁
pthread_mutex_destroy(&m);
读写锁:pthread_rwlock_init(&lock,NULL) ;
pthread_rwlock_lock(&lock); //加锁
pthread_rwlock_unlock(&lock) ;//解锁
pthread_rwlock_destroy(&lock);
条件变量:与互斥锁一起使用,wait会释放锁阻塞,signal唤醒一个线程,broadcast唤醒全部;判断条件必须使用while处理虚假唤醒。
生产者‑消费者模型
2生产者(各产30),3消费者(各消费20),缓冲区大小7,总生产=60,总消费=60
信号量:s1空位,初值7:生产者P,消费者V,s2产品,初值0:消费者P,生产者V
互斥锁:保护缓冲区,P(sem_wait)写在加锁外面,否则死锁
生产者 :sem_wait(s1)占空位;加锁 →放数据 →解锁;sem_post(s2)产品+1
消费者:sem_wait(s2)取产品;加锁 →取数据 →解锁;sem_post(s1)空位+1
读者‑写者模型
读者优先版本:允许多个读者同时读;写者必须独占资源,读写、写写不能同时进行。
计数器:count统计读者数量
互斥锁:rmutex保护count;wmutex为读写共用锁。
读者:第一个读者加wmutex,最后一个读者释放wmutex,中间读者只修改count。
写者:直接对wmutex加锁解锁。
缺点:持续不断的新读者会造成写者饥饿。
十三、网络编程
osi模型 七层 :应用层->表示层->会话层->传输层->网络层->数据链路层->物理层
tcp/ip协议 四层 :应用层->传输层->网络层->网络接口层(数据链路层、物理层)
网络编程:可以在不同主机进行进程间通信
ip地址:唯一标识一台主机 ipv4 ipv6
MAC地址:48唯一标识一台主机
tcp协议
面向连接的,可靠的流式服务
服务器:socket()->bind()->listen()->accept()->recv()->send()->close()
客户端:socket()->connect()->send()->recv()->close()
socket():创建套接字,返回文件描述符,作为后续通信的句柄。
bind():将套接字绑定到指定的 IP 地址和端口号。
listen():将套接字设为监听状态,等待客户端连接,内核维护半连接队列和全连接队列。
accept():从全连接队列中取一个已完成三次握手的连接,返回新的套接字用于与该客户端通信。
connect():主动向服务器发起连接请求,触发三次握手。
send() / recv():发送和接收数据,TCP 是面向连接的可靠字节流,可多次收发。
发送缓冲区 / 接收缓冲区:TCP 为每个连接在内核中维护发送缓冲区和接收缓冲区,send() 只是把数据拷贝到发送缓冲区,由内核负责发送;recv() 从接收缓冲区读取数据。
close():关闭套接字,释放资源;主动关闭方触发四次挥手。
三次握手:TCP建立连接的过程,客户端connect发SYN,服务端回SYN+ACK,客户端再回ACK,完成后连接进入全连接队列。
四次挥手:TCP断开连接的过程,客户端close发FIN,服务端回ACK;等服务端数据发完,close再发FIN,最后客户端回ACK,正式断开。
TCP可靠性:应答确认,超时重传,去重,乱序重排,滑动窗口,进行流量控制。
粘包:TCP 面向字节流,发送方多次 send() 的数据可能被合并成一次 recv() 接收,或一次 send() 的数据被拆成多次 recv() 接收。加起始和结束标记,报文采用固定长度,报头包含数据长度。
TIME_WAIT:主动关闭方在发送最后一个 ACK 后进入 TIME_WAIT 状态,等待 2MSL(最大报文段生存时间,通常为 2 分钟)后才真正关闭连接。
TIME_WAIT 存在的意义:① 保证最后一个 ACK 能可靠到达对方:如果该 ACK 丢失,对方会重发 FIN,主动关闭方需要保留状态以便重发 ACK,否则对方无法正常关闭;② 防止旧连接的延迟报文干扰新连接:等待 2MSL 可确保本次连接的所有报文在网络中消失,避免相同四元组的新连接收到旧连接的残留数据。
udp协议
无连接,不可靠的数据报服务
服务端:socket()->bind()->recvfrom()->sendto()->close()
客户端:socket()->sendto()->recvfrom()->close()
tcp和udp可以同时使用同一个端口
http协议
HTTP 与 HTTPS 区别:① HTTP 明文传输,数据不加密,容易被窃听和篡改;HTTPS 在 HTTP 基础上加入 SSL/TLS 加密层,对传输内容进行加密,保证数据机密性和完整性。② HTTP 默认端口 80,HTTPS 默认端口 443。③ HTTPS 需要向 CA 机构申请数字证书,用于身份认证;HTTP 无需证书。④ HTTPS 建立连接时多一次 TLS 握手,性能略低于 HTTP,但安全性更高。
HTTP GET 与 POST 区别:① GET 用于获取资源,参数拼接在 URL 后面,如 ?name=value,可见且长度受限;POST 用于提交数据,参数放在请求体中,相对隐蔽且长度不受限。② GET 请求可被浏览器缓存、收藏、保留在历史记录中;POST 一般不会被缓存。③ GET 是幂等的,多次请求结果相同;POST 不幂等,多次提交可能产生多次副作用。④ GET 只支持 ASCII 字符;POST 支持二进制数据,可上传文件。
HTTP 应答状态码:服务器处理请求后返回的三位数字状态码,用于告知客户端请求结果。
1xx(信息类):请求已接收,继续处理。如 100 Continue(继续发送请求体)。
2xx(成功类):请求成功处理。① 200 OK:请求成功,返回正常数据;② 201 Created:资源创建成功(常用于 POST);③ 204 No Content:请求成功但无返回内容。
3xx(重定向类):需要进一步操作完成请求。① 301 Moved Permanently:资源永久移动到新地址;② 302 Found:资源临时重定向;③ 304 Not Modified:资源未修改,可使用本地缓存。
4xx(客户端错误类):请求有误,客户端需修改。① 400 Bad Request:请求语法错误;② 401 Unauthorized:未认证,需要登录;③ 403 Forbidden:已认证但无权限访问;④ 404 Not Found:资源不存在;⑤ 405 Method Not Allowed:请求方法不被允许。
5xx(服务器错误类):服务器处理出错。① 500 Internal Server Error:服务器内部错误;② 502 Bad Gateway:网关或代理收到无效响应;③ 503 Service Unavailable:服务器暂时不可用(如过载、维护);④ 504 Gateway Timeout:网关超时。
http连接网页
1.先DNS解析域名得到服务器IP+80端口,调用connect()建立TCP连接。
2. 短连接:建立TCP连接 → 浏览器发HTTP请求 → 服务器返回HTTP响应 → 立刻close()断开TCP连接,一次请求就断开。
3. 长连接:TCP连接建立后,多次收发HTTP请求响应,连接不马上关闭,用完之后才断开。
十四、IO复用
select/poll/epoll:检测描述符集合中哪些描述符事件已就绪
select 轮询O(n),epoll 注册回调O(1)
select
FD_ZERO(fd_set*) 清空集合;FD_SET(fd, fd_set*) 将描述符加入集合;FD_CLR(fd, fd_set*) 将描述符从集合移除;FD_ISSET(fd, fd_set*) 判断描述符是否就绪。
工作流程:① 定义并初始化 fd_set 集合,把要监听的描述符加入集合;② 调用 select() 阻塞等待,内核检测集合中是否有描述符就绪;③ select() 返回后,用 FD_ISSET 遍历集合,找出就绪的描述符;④ 对就绪的描述符执行读写操作;⑤ 每次循环前需要重新设置 fd_set,因为 select 会修改集合内容。
poll
工作流程:① 定义 pollfd 数组,为每个要监听的描述符设置 fd 和 events;② 调用 poll() 阻塞等待,内核检测数组中哪些描述符事件就绪;③ poll() 返回后,遍历数组检查每个元素的 revents 字段,判断哪些描述符就绪;④ 对就绪的描述符执行读写操作;⑤ 与 select 不同,poll 不会修改 events,因此无需每次循环重新设置。
epoll
工作流程:① 调用 epoll_create() 创建 epoll 实例;② 调用 epoll_ctl() 把要监听的描述符注册到内核红黑树中;③ 调用 epoll_wait() 阻塞等待,内核把就绪的描述符放入就绪链表并返回;④ 遍历返回的就绪事件数组,对就绪的描述符执行读写操作;⑤ 新增或删除监听对象时,再次调用 epoll_ctl() 维护红黑树。
ET LT
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)