
📖 资源简介
最近有朋友问Linux程序设计从入门到实战(价值198元)的学习资料,正好手头有一套比较完整的,就整理出来分享给大家。
✨ 资源亮点
- 内容扎实:100节视频课程,跟着老师一步步操作,比自学摸索快得多。
- 零基础友好:从环境搭建开始讲起,不需要有任何前置知识,纯新手也能入门。
📚 课程内容
资源清单
| 类型 | 数量 |
|---|---|
| 视频教程 | 100 节 |
| 压缩文件 | 0 个 |
| 合计 | 100 |
为什么要学”Linux 程序设计”
很多人把”会用 Linux”和”会 Linux 程序设计”混为一谈。前者是在命令行敲 ls、cd、grep,后者是用 C 语言直接调用内核提供的系统调用,写出在 Linux 上跑后台服务、网络代理、数据库引擎这类底层软件的能力。
当前端、客户端、业务后端都在向上层框架收敛时,真正有壁垒的工程师反而在往下走:云原生、存储、网络、数据库、性能优化、中间件,这些岗位几乎都要求你能读懂 epoll、mmap、io_uring 的源码级用法。这套课的价值,就是帮你把”应用层业务开发者”升级成”懂操作系统的底层工程师”。
适合谁学:
- 想做 C/C++ 后台、嵌入式、驱动、云原生组件的同学;
- 写 Java / Go / Python 但被”线上性能问题””内核参数调优”卡住的应用层工程师;
- 准备面试后台开发岗、希望把”进程 / 线程 / IO / 网络”答到原理层的人。
一、Shell 与工具:程序员的”日常肌肉”
正式写 C 之前,先把命令行工具用熟。Linux 一切皆文件,绝大多数问题都能靠管道串起来解决。
权限与文件
Linux 用一个 9 位的权限位表示三组(user / group / other)的读、写、执行:
# -rwxr-xr-- => user=rwx, group=r-x, other=r--

chmod 755 script.sh # rwxr-xr-x
chmod u+x script.sh # 只给所有者加执行位
chown alice:dev app.log # 改属主和属组
数字权限速查:r=4, w=2, x=1,三位相加即一组。
文本处理三剑客
# grep:过滤

grep -rn "TODO" ./src/ --include="*.c"
# awk:按列处理

ps aux | awk '$3 > 5.0 {print $2, $3"%", $11}'
# sed:流编辑

sed -i 's/old/new/g' config.ini
# find:按条件找文件

find . -name "*.log" -mtime +7 -delete
Shell 脚本基础
#!/usr/bin/env bash
set -euo pipefail # 出错即退、未定义变量报错、管道失败即退
# 特殊变量:$0=脚本名 $#=参数个数 $@=所有参数 $?=上一条命令退出码

if [ $# -lt 1 ]; then
echo "usage: $0 <name>" >&2
exit 1
fi
for f in "$@"; do
case "$f" in
*.log) echo "log file: $f" ;;
*.conf) echo "config file: $f" ;;
*) echo "other: $f" ;;
esac
done
# 函数:用 local 防止污染全局

greet() {
local name="$1"
echo "hello, $name"
return 0
}
greet "world"
写脚本最常见的坑:不写 set -euo pipefail,导致错误被静默吞掉;或者不加引号,文件名带空格就崩。
二、C 语言系统编程:文件 I/O
系统调用 vs 标准库
Linux 提供两套 I/O 接口,必须分清:
| 接口 | 来源 | 缓冲 | 句柄类型 |
|---|---|---|---|
open/read/write/lseek |
内核系统调用 | 无用户态缓冲 | 文件描述符 int fd |
fopen/fread/fwrite/fprintf |
libc 标准库 | 有用户态缓冲 | 文件指针 FILE * |
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
int fd = open("hello.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) { perror("open"); return 1; }
const char *msg = "hello, linux\n";
ssize_t n = write(fd, msg, 14);
if (n < 0) { perror("write"); }
close(fd);
return 0;
}
文件描述符是一个从 0 开始的小整数。每个进程默认打开 0(stdin)、1(stdout)、2(stderr),之后每次 open 都分配当前最小未用的 fd。理解 fd 表是理解重定向、管道、socket 的前提。
stat 与目录遍历
#include <sys/stat.h>
struct stat st;
if (stat("hello.txt", &st) == 0) {
printf("size=%ld bytes, mode=%o\n", st.st_size, st.st_mode & 0777);
}
#include <dirent.h>
DIR *d = opendir(".");
struct dirent *ent;
while ((ent = readdir(d)) != NULL) {
printf("%s\n", ent->d_name);
}
closedir(d);
三、进程:fork / exec / wait
Unix 进程模型一句话:进程靠 fork 克隆自己,靠 exec 换上新程序,靠 wait 回收子进程。
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
pid_t pid = fork();
if (pid < 0) { perror("fork"); return 1; }
if (pid == 0) {
/* 子进程 */
execlp("ls", "ls", "-l", NULL);
perror("exec never returns on success");
return 127;
} else {
/* 父进程 */
int status;
waitpid(pid, &status, 0);
printf("child exit=%d\n", WEXITSTATUS(status));
}
return 0;
}
两个必须理解的概念:
- 僵尸进程(Zombie):子进程退出了,但父进程没
wait,内核还保留它的退出状态。ps里看到Z状态。解决办法:父进程及时wait/waitpid,或忽略SIGCHLD。 - 孤儿进程:父进程先死了,子进程被 init(PID 1)收养。一般无害。
守护进程(daemon) 的标准套路:fork 一次让父进程退出 → setsid() 脱离会话 → 再 fork 一次防止重新拿终端 → 改工作目录到 / → 重定向 0/1/2 到 /dev/null。
四、信号:进程之间的”软中断”
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
volatile sig_atomic_t stop = 0;
void on_sigint(int sig) {
(void)sig;
stop = 1; /* 异步信号里只改 volatile sig_atomic_t 是安全的 */
}
int main(void) {
struct sigaction sa = {0};
sa.sa_handler = on_sigint;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
while (!stop) {
pause();
}
printf("bye\n");
return 0;
}
要点:
- 用
sigaction而不是老的signal(),后者在不同 Unix 上行为不一致。 - 在信号处理函数里不要调用
printf/malloc这类非异步信号安全函数。 SIGPIPE默认会让进程在写一个已关闭的管道时直接死掉,网络服务里通常要signal(SIGPIPE, SIG_IGN)。- 子进程退出时给父进程发
SIGCHLD,父进程在它里面waitpid回收。
五、线程与同步
Linux 线程是用 POSIX Threads(pthread)实现的,本质是”轻量级进程”,和进程共享地址空间。
#include <pthread.h>
#include <stdio.h>
static long counter = 0;
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *worker(void *arg) {
(void)arg;
for (int i = 0; i < 100000; i++) {
pthread_mutex_lock(&lock);
counter++;
pthread_mutex_unlock(&lock);
}
return NULL;
}
int main(void) {
pthread_t t[4];
for (int i = 0; i < 4; i++) pthread_create(&t[i], NULL, worker, NULL);
for (int i = 0; i < 4; i++) pthread_join(t[i], NULL);
printf("counter=%ld\n", counter); /* 不加锁会远小于 400000 */
return 0;
}
同步原语对照表:
| 原语 | 用途 | 典型场景 |
|---|---|---|
pthread_mutex_t |
互斥,保护临界区 | 共享计数器、链表 |
pthread_cond_t |
等待某条件成立 | 生产者-消费者 |
pthread_rwlock_t |
读写锁 | 读多写少的配置 |
pthread_barrier_t |
线程齐步走 | 并行计算同步点 |
死锁四条件:互斥、持有并等待、不可抢占、循环等待。破坏任意一个即可——最实用的做法是所有线程以相同顺序加锁。
六、网络编程:从 socket 到 epoll
TCP 服务端骨架
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(8080);
bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr));
listen(listen_fd, 128);
for (;;) {
int cfd = accept(listen_fd, NULL, NULL);
char buf[1024];
ssize_t n = read(cfd, buf, sizeof(buf) - 1);
if (n > 0) {
buf[n] = '\0';
write(cfd, "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nok\n", 33);
}
close(cfd);
}
return 0;
}
经典三次握手 / 四次挥手必须背熟:
- 三次握手:
SYN→SYN+ACK→ACK,目的是双方都确认彼此的收发能力。 - 四次挥手:
FIN→ACK→FIN→ACK,因为 TCP 全双工,关闭是两个方向分别进行。
为什么需要 IO 多路复用
上面的服务端每来一个连接就 accept + read + close,同时只能处理一个客户端。要支持并发,常见思路:
| 模型 | 思路 | 问题 |
|---|---|---|
| 阻塞 IO + 每连接一线程 | accept 后 pthread_create |
连接数一上去,线程爆炸 |
| select / poll | 一个线程轮询所有 fd | fd 多了每次遍历 O(n) |
| epoll | 内核事件通知,只返回就绪的 fd | 高并发首选 |
| 异步 IO(io_uring) | 内核完成后回调 | 新一代,生态还在完善 |
epoll 的三个系统调用:
int epfd = epoll_create1(0);
struct epoll_event ev = {0};
ev.events = EPOLLIN;
ev.data.fd = listen_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev);
struct epoll_event events[128];
int n = epoll_wait(epfd, events, 128, -1);
for (int i = 0; i < n; i++) {
/* events[i].data.fd 就是就绪的 fd */
}
Reactor 模式就是基于 epoll:一个主线程跑 epoll_wait,就绪事件分发到对应的 handler 处理。Redis、Nginx、Netty 全是这个套路。
七、编译、链接与调试
GCC 四步走
# 预处理(宏展开、头文件插入)

gcc -E hello.c -o hello.i
# 编译(C -> 汇编)

gcc -S hello.i -o hello.s
# 汇编(汇编 -> 机器码 .o)

gcc -c hello.s -o hello.o
# 链接(多个 .o 拼成可执行文件)

gcc hello.o -o hello
理解这四步,才能看懂”为什么改了头文件要全量重编””为什么加个 -fPIC 才能编动态库”。
静态库 vs 动态库
# 静态库:把 .ar 包进可执行文件

ar rcs libhello.a hello.o
gcc main.c -L. -lhello -o app_static
# 动态库:运行时才加载

gcc -fPIC -shared hello.c -o libhello.so
# 让程序找到 .so

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
gcc main.c -L. -lhello -o app_dyn
动态库找不到的经典报错 error while loading shared libraries,解决方法就是把 .so 路径加进 LD_LIBRARY_PATH,或写到 /etc/ld.so.conf.d/ 后 ldconfig。
GDB 调试
gcc -g -O0 demo.c -o demo # 必须带 -g
gdb ./demo
(gdb) break main
(gdb) run
(gdb) next / step / finish
(gdb) print var
(gdb) backtrace
(gdb) watch counter
线上 core dump 分析:gdb ./app core.xxx,然后 bt full。
学习路线导读
这套课 100 节,按从易到难大致可拆成五段:
- Linux 环境与基本命令:装系统、ssh、文件权限、进程管理、管道重定向。
- Shell 脚本:变量、控制流、函数、文本处理。
- C 语言系统编程:文件 I/O、进程、信号。
- 线程与同步:pthread、互斥锁、条件变量。
- 网络编程与工程化:socket、epoll、Makefile、静态/动态库、GDB。
建议每章都边看边敲,不要只看视频。系统编程课和 Web 课最大的区别是——你抄一遍代码它就能跑,跑不通就是你没理解。
高频面试题
fork之后父子进程共享哪些资源、不共享哪些?(共享文件描述符表、内存映射;不共享地址空间、文件偏移……注意 fork 后文件偏移是共享的)- 僵尸进程怎么产生?怎么避免?
select / poll / epoll区别?epoll 为什么高效?(O(n) 遍历 vs O(1) 就绪通知;红黑树 + 就绪链表)- 什么是惊群?怎么避免?
- 进程间通信(IPC)有哪几种?各自适用场景?(管道 / FIFO / 消息队列 / 共享内存 / 信号量 / Unix Domain Socket)
- TCP 为什么三次握手、四次挥手?TIME_WAIT 为什么要等 2MSL?
- 动态库和静态库的区别?什么是动态链接的符号可见性?
常见踩坑 FAQ
Q:写的 socket 程序 bind 时报 Address already in use?
A:加 SO_REUSEADDR。这是 TIME_WAIT 残留导致的,开发期几乎必踩。
Q:fork 后 printf 输出多了一行?
A:stdio 默认行缓冲,重定向到文件时变成全缓冲,fork 会把缓冲区一起复制一份,父进程退出时再 flush 一次。fflush(stdout) 或加 \n 可解。
Q:多线程程序加了锁还是结果不对?
A:检查是不是在没加锁的代码路径也改了同一个变量,或者锁的粒度太粗把临界区写大了。先用 valgrind --tool=helgrind 跑一遍。
Q:make 时找不到头文件?
A:用 -I/path/to/include 告诉 gcc;找库用 -L/path/to/lib + -lname。这两个参数 90% 的编译错误都在它们上。
总结
Linux 程序设计是一门”慢功夫”,但天花板极高。它不像 Web 框架那样半个月就能出活,但一旦你把进程、文件描述符、epoll、mmap 这些底层概念吃透,上层用什么语言、什么框架都是降维打击。
学习建议:
- 先在自己机器上装一台虚拟机(Ubuntu 或 Debian),所有实验都在里面做,不要在公司开发机上瞎搞。
- 每学一个系统调用,都先写一个最小可运行 demo,再去看别人的项目怎么用。
- 配合《Unix 环境高级编程》(APUE)当字典,视频 + 书 + 手动 debug 三件套。
- 不要一上来啃内核源码,先把用户态 API 用熟,再回头看内核实现会顺很多。
资源获取
💡 建议:尽快保存到本地网盘或电脑,避免分享链接后期失效。
整理不易,如果对你有帮助,欢迎分享给更多需要的朋友。










-800x600.png)




请登录后查看评论内容