Linux C学习路线全概及知识点笔记2(仅供个人参考)
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
目录
文章目录
1,进程创建、回收(以linux为例)
1.1 进程创建流程
1.2 进程回收机制
1.3 源文件变成可执行程序的过程
1.3.1 详解
1.3.2 gcc命令细化
1.3.3 可执行文件加载为进程的过程(Linux 视角)
1.4 总结框架表
2,crontab、at命令(待补充,用于定时备份等)
3,进程间通信
3.1 信号量
3.1.1 定义
3.1.2 信号量的类型
3.2 信号量在进程间通信中的作用
3.3 Linux下的信号量实现方式
3.4 System V 信号量详解(进程间通信重点)
3.4.1 使用步骤
3.4.2 核心结构和函数原型
3.4.3 典型代码示例(进程间同步)
3.5 信号量使用中的常见注意事项
4,POSIX 信号量
4.1 POSIX信号量头文件与链接方式
4.2 POSIX信号量类型
4.3 常用函数接口(详细对照表)
4.4 使用命名信号量(适用于进程间)
4.4.1 创建和初始化
4.4.2 使用信号量(同步)
4.4.3 关闭与释放资源
4.4.4 示例:两个进程同步输出
4.5 使用匿名信号量(适用于线程或内存共享)
4.5.1 初始化匿名信号量
4.5.2 使用方式
4.5.3 销毁信号量
4.5.4 示例:线程互斥访问共享资源
5,消息队列
5.1 消息队列定义
5.2 消息队列的主要特点
5.3 System V 消息队列关键结构与系统调用
5.3.1 创建或获取消息队列:msgget()
5.3.2 消息结构
5.3.3 发送消息:msgsnd()
5.3.4 接收消息:msgrcv()
5.3.5 控制消息队列:msgctl()
5.3.6 示例程序
5.3.7 调试建议
6,共享内存
6.1 定义
6.2 特点
6.3 System V共享内存相关系统调用
6.4 使用流程概览
6.5 共享内存函数讲解
6.5.1 创建共享内存段:shmget()
6.5.2 映射共享内存段:shmat()
6.5.3 分离共享内存段:shmdt()
6.5.4 控制共享内存段:shmctl()
6.6 共享内存示例代码(两个进程)
6.6.1 写入进程(write.c)
6.6.2 读取进程(reader.c)
6.7 调试与实用技巧
7,信号的基本概念
7.1 信号的发送与接收
7.2 捕捉与忽略信号
7.2.1 忽略信号
7.3 阻塞信号
7.4 sigaction():更强大的信号处理
7.5 信号与进程控制
7.6 信号的实际应用场景
8,线程创建
8.1 线程与进程的区别
8.2 POSIX线程库(pthread)基础
8.2.1 创建线程的函数
8.2.2 示例:创建一个简单线程
8.3 等待线程结束(pthread_join)
8.4 线程生命周期简图
8.5 线程相关函数整理
8.6 常见问题与注意事项
9,线程同步与互斥的基本概念
9.1 互斥锁(pthread_mutex_t)概述
9.1.2 互斥锁的定义与初始化
9.1.3 使用互斥锁的基本流程
9.1.4 示例代码:两个线程并发写共享变量
9.2 互斥锁函数一览
9.3 互斥锁类型说明(高级)
9.4 常见问题与调试建议
9.5 互斥锁使用场景分析
10,什么是守护进程(Daemon Process)
10.1 守护进程的常见用途
10.2 如何编写一个守护进程(用户态实现)
10.2.1 示例:为守护进程添加信号处理(优雅的退出)
10.2.2 与 systemd 配合的守护进程管理(现代Linux系统)
11,库的基本概念
11.1 什么是库?
11.2 静态库 VS 动态库 对比分析
11.3 静态库和动态库的制作方法(含完整示例)
11.3.1 制作静态库
11.3.1.1 使用静态库
11.4 制作动态库 .so
11.4.1 编译步骤
11.4.2 使用动态库
11.5 关键技术知识点补充
11.6 建议与实践经验
1,进程创建、回收(以linux为例)
在linux系统中,“进程”是程序的运行实例。我们以fork()和exec()为切入点:
1.1 进程创建流程
阶段 | 内容说明 |
---|---|
fork() | 创建一个新的子进程,子进程复制父进程的地址空间,但逻辑上是两个独立进程。 |
exec() | 子进程可以通过 exec 系列函数加载一个新的程序映像,替换自己的代码段、数据段等。 |
clone() | 更底层的创建方式(底层系统调用),fork() 等也基于 clone() 实现。 |
示例:
#include <unistd.h>
#include <sys/types.h>
#included <stdio.h>int main()
{pid_t pid = fork();if (pid == 0) {// 子进程printf("子进程,PID = %d\n", getpid());execlp("ls", "ls", "-l", NULL);} else if (pid > 0) {// 父进程printf("父进程,PID = %d\n", getpid());wait(NULL); // 等待子进程结束} else {printf("Fork failed!\n");}return 0;
}
1.2 进程回收机制
子进程终止时,内核会将其状态保留(称为“僵尸进程”),直到父进程调用wait()或waitpid()将其资源释放。
情况 | 处理方式 |
---|---|
父进程调用 wait() | 内核回收子进程资源 |
父进程未调用 wait() | 子进程成为“僵尸进程”,资源占用不释放 |
父进程提前结束 | 子进程被 init 进程收养,由它负责回收 |
1.3 源文件变成可执行程序的过程
整个流程可以分为四个阶段:预处理、编译、汇编、连接。
1.3.1 详解
阶段 | 工具 | 输入 | 输出 | 说明 |
---|---|---|---|---|
预处理 | cpp | .c 文件 | .i 文件 | 展开宏、处理头文件 #include 、删除注释 |
编译 | cc1 | .i 文件 | .s 文件 | 将 C 源代码翻译为汇编语言 |
汇编 | as | .s 文件 | .o 文件 | 汇编器把汇编代码翻译为机器码的目标文件 |
链接 | ld | .o 文件和库文件 | 可执行文件 | 将多个 .o 文件和库链接成一个完整的可执行程序 |
1.3.2 gcc命令细化
命令 | 说明 |
---|---|
gcc -E hello.c -o hello.i | 仅预处理 |
gcc -S hello.i -o hello.s | 编译为汇编 |
gcc -c hello.s -o hello.o | 汇编为目标文件 |
gcc hello.o -o hello | 链接成可执行文件 |
1.3.3 可执行文件加载为进程的过程(Linux 视角)
当你运行一个可执行程序,Linux内核将执行以下操作:
1)shell调用execve()系统调用
2)内核解析ELF文件结构
验证头部信息
读取程序头表(Program Header Table)
3)内核为进程分配内存
映射代码段、数据段
设置堆栈
4)加载器准备环境
复制命令行参数和环境变量到新进程内存
设置入口地址
5)CPU切换到入口地址
开始执行用户代码(即main函数)
1.4 总结框架表
内容 | 描述 |
---|---|
进程创建方式 | fork、clone、exec 系列函数 |
进程回收 | wait/waitpid 回收子进程资源 |
编译四阶段 | 预处理 → 编译 → 汇编 → 链接 |
可执行程序加载 | ELF 分析 → 映射内存 → 准备堆栈 → 切换执行 |
系统调用接口 | 用户态通过 syscall 进入内核态 |
2,crontab、at命令(待补充,用于定时备份等)
3,进程间通信
3.1 信号量
3.1.1 定义
信号量(Semaphore)是一种特殊的整型变量,用于在进程或线程之间实现同步和互斥控制。
3.1.2 信号量的类型
类型 | 说明 |
---|---|
计数信号量(Counting Semaphore) | 允许多个资源同时被访问,如连接池、线程池 |
二值信号量(Binary Semaphore) | 取值仅为0或1,等价于互斥锁,用于互斥控制 |
3.2 信号量在进程间通信中的作用
场景 | 用途 |
---|---|
控制共享资源的访问 | 避免多个进程同时访问同一资源 |
同步操作 | 保证某些操作顺序执行 |
实现临界区(Critical Section) | 控制某代码块只能被一个进程进入执行 |
3.3 Linux下的信号量实现方式
机制类型 | 用于 | 接口 |
---|---|---|
System V 信号量 | 进程间通信 | semget 、semop 、semctl |
POSIX 信号量 | 进程/线程间 | sem_init 、sem_wait 等 |
3.4 System V 信号量详解(进程间通信重点)
3.4.1 使用步骤
步骤 | 函数 | 描述 |
---|---|---|
1 | semget | 创建或获取一个信号量集合 |
2 | semctl | 控制信号量的属性,如初始化、删除等 |
3 | semop | P/V 操作(即 wait/signal 操作) |
3.4.2 核心结构和函数原型
senget(); 创建或获取信号量集合
int semget(key_t key, int nsems, int semflg);key 标识唯一信号量集,可通过fork生成
nsems 创建信号量个数
semflg 权限设置,如IPC_CREAT
semctl(); 控制信号量(初始化、删除等)
int semctl(int semid, int semnum, int cmd, ...);常用命令:SETVAL:设置信号量的值IPC_RMID:删除信号量GETVAL:获取当前值
semop(); 信号量操作(P/V操作)
int semop(int semid, struct sembuf *sops, size_t nsops);struct sembuf {unsigned short sem_num; // 信号量索引short sem_op; //操作数(-1 p操作,+1 v操作)short sem_flg; // 0或IPC_NOWAIT
};
3.4.3 典型代码示例(进程间同步)
例子:父子进程使用信号量同步输出
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <sys/wait.h>union semun {int val;
};int main(){key_t key = ftok(".", 123);int semid = semget(key, 1, IPC_CREAT | 0666);// 初始化信号量0union semun sem_union;sem_union.val = 0;semctl(semid, 0, SETVAL, sem_union);pid_t pid = fork();if (pid == 0) {// 子进程printf("子进程执行完毕\n");struct sembuf sb = {0, 1, 0}; // v操作semop(semid, &sb, 1);exit(0);} else {// 父进程等待子进程完成struct sembuf sb = {0, -1, 0}; // p操作semop(semid, &sb, 1);printf("父进程检测到子进程已完成,继续执行\n");semctl(semid, 0, IPC_RMID); // 删除信号量wait(NULL);}return 0;
}
3.5 信号量使用中的常见注意事项
问题 | 原因或建议解决方式 |
---|---|
死锁 | 同步顺序不一致,需避免循环等待 |
忘记释放资源 | 异常退出未执行 V 操作,建议设置信号处理 |
IPC 对象未清理(内核残留) | 使用 ipcs 查看,用 ipcrm 删除 |
并发性能瓶颈 | 使用共享内存 + 原子操作优化性能 |
生产环境中更推荐 POSIX 信号量
4,POSIX 信号量
POSIX信号量是基于POSIX标准定义的一种同步机制,支持:
命令信号量(适用于进程间)
匿名信号量(适用于线程间或共享内存中的进程)
与System V不同,POSIX信号量接口更贴近现代C编程,支持资源释放自动化(通过sem_close, sem_unlink)。
4.1 POSIX信号量头文件与链接方式
#include <semaphore.h> // 所有POSIX信号量接口
#include <fcntl.h> // O_CREAT 等
#include <sys/stat.h> // S_IRUSR等编译时需要链接 -pthread或-lrt
gcc demo.c -o demo -pthread
4.2 POSIX信号量类型
类型 | 适用范围 | 创建方式 |
---|---|---|
命名信号量 | 多进程共享 | sem_open |
匿名信号量 | 多线程或共享内存 | sem_init |
4.3 常用函数接口(详细对照表)
函数名 | 用法描述 |
---|---|
sem_init | 初始化匿名信号量 |
sem_destroy | 销毁匿名信号量(与 sem_init 配合) |
sem_open | 打开或创建命名信号量 |
sem_close | 关闭命名信号量(与 sem_open 配合) |
sem_unlink | 删除命名信号量名称 |
sem_wait | P 操作,等待(阻塞直到信号量值 > 0) |
sem_trywait | 非阻塞的 P 操作 |
sem_post | V 操作,释放 |
sem_getvalue | 获取当前信号量的值 |
4.4 使用命名信号量(适用于进程间)
4.4.1 创建和初始化
sem_t *sem = sem_open("/mysem", o_CREAT, 0666, 1);/mysem:信号量名称(必须以 / 开头)
0666: 权限
1: 初始值
4.4.2 使用信号量(同步)
sem_wait(sem); // p操作(阻塞)
sem_post(sem); // v操作(释放)
4.4.3 关闭与释放资源
sem_close(sem); // 关闭描述符
sem_unlink("/mysem"); // 从系统中删除该命名信号量
4.4.4 示例:两个进程同步输出
父进程代码(writer.c)
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>int main() {sem_t *sem = sem_open("/syncsem", O_CREAT, 0666, 0);printf("父进程准备工作完成,等待子进程信号。。。\n");sem_wait(sem); // 等待子进程完成任务printf("父进程收到子进程信号,继续执行。。。\n");sem_close(sem);sem_unlink("/syncsem");return 0;
}
子进程代码(reader.c)
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <unistd.h>
#included <stdlib.h>int main()
{sem_t *sem = sem_open("/syncsem", 0);sleep(2); // 模拟处理printf("子进程完成任务,通知父进程。。。\n");sem_post(sem); // 释放信号量sem_close(sem);return 0;
}
4.5 使用匿名信号量(适用于线程或内存共享)
4.5.1 初始化匿名信号量
sem_t sem;
sem_init(&sem, 0, 1); // 第二参数为0表示线程间使用;为1表示可用于进程间(共享内存中)
4.5.2 使用方式
sem_wait(&sem);
sem_post(&sem);
4.5.3 销毁信号量
sem_destory(&sem);
4.5.4 示例:线程互斥访问共享资源
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <unsitd.h>sem_t sem;void *thread_func(void *arg) {sem_wait(&sem);printf("线程 %ld 正在访问共享资源\n", pthread_self());sleep(1);printf("线程 %ld 释放共享资源\n", pthread_self());sem_post(&sem);return;
}int main()
{pthread_t t1, t2;sem_init(&sem, 0, 1); // 匿名信号量,线程间使用pthread_create(&t1, NULL, thread_func, NULL);pthread_create(&