Linux C 语言编程综合教程
本教程将分为以下几个主要部分:

- 准备篇:环境搭建与基础工具 - 如何在你的 Linux 系统上准备好编程环境。
- 入门篇:第一个 C 程序 - 编写、编译并运行你的第一个 "Hello, World!" 程序。
- 核心篇:C 语言基础 - 复习 C 语言的核心语法,并结合 Linux 环境进行讲解。
- 进阶篇:Linux 系统编程 - 深入讲解如何在 C 程序中与 Linux 内核交互,包括文件 I/O、进程、信号等。
- 实战篇:构建一个简单的 Shell - 通过一个完整的项目,将所学知识融会贯通。
- 工具篇:提升开发效率 - 介绍 GCC 调试、Makefile、GDB 等必备工具。
- 资源与总结 - 推荐书籍和进一步学习的方向。
第一部分:准备篇 - 环境搭建与基础工具
在开始之前,你需要确保你的 Linux 系统已经安装了必要的开发工具。
选择你的 Linux 发行版
- Ubuntu/Debian: 对新手友好,社区庞大。
- Fedora/CentOS/RHEL: 稳定,常用于服务器和企业环境。
- Arch Linux: 提供最新的软件,但需要更多手动配置。
安装 GCC 和 Make
GCC (GNU Compiler Collection) 是编译 C 程序的核心工具。make 是一个自动化构建工具,用于管理大型项目的编译过程。
在基于 Debian/Ubuntu 的系统上:
sudo apt update sudo apt install build-essential
build-essential 包包会自动安装 gcc, g++, make 等核心编译工具。

在基于 Fedora/CentOS 的系统上:
sudo dnf groupinstall "Development Tools"
Development Tools 软件组包含了所有必要的编译和构建工具。
选择一个代码编辑器
- Vim/Neovim: 强大的终端编辑器,学习曲线陡峭但效率极高。
- Emacs: 另一个经典的“神级”编辑器,高度可定制。
- VS Code (Visual Studio Code): 现代、功能丰富的图形化编辑器,通过插件可以提供出色的 C 语言开发体验。
- Gedit: 简单易用的图形化编辑器,适合初学者。
推荐: 初学者可以从 VS Code 开始,它提供了直观的界面和丰富的提示,当你熟悉后,可以挑战 Vim。
第二部分:入门篇 - 你的第一个 C 程序
让我们创建一个经典的 "Hello, World!" 程序。

创建源代码文件
打开你的编辑器,创建一个名为 hello.c 的文件,并输入以下代码:
// hello.c
#include <stdio.h> // 引入标准输入输出库
int main() {
printf("Hello, Linux World!\n");
return 0; // 程序正常退出
}
#include <stdio.h>: 这行代码告诉编译器,我们需要使用stdio.h(Standard Input/Output) 库中的函数,printf。int main(): 这是 C 程序的入口函数,程序从这里开始执行。printf(...): 用于在屏幕上打印文本。\n: 是一个换行符。return 0;: 表示程序成功执行完毕并返回 0 给操作系统。
编译源代码
打开终端,进入 hello.c 文件所在的目录,使用 gcc 命令进行编译:
gcc hello.c -o hello
gcc: 调用 GCC 编译器。hello.c: 你的源文件。-o hello: 指定输出的可执行文件名为hello,如果不加-o,默认会生成一个名为a.out的文件。
运行程序
编译成功后,你会得到一个名为 hello 的可执行文件,在终端中输入以下命令来运行它:
./hello
- 表示在当前目录下寻找并执行该程序,在 Linux 中,直接输入
hello可能会找不到,因为它不在系统的PATH环境变量中。
你应该会在终端看到输出:
Hello, Linux World!
第三部分:核心篇 - C 语言基础
这部分假设你已经有 C 语言基础,我们将重点介绍在 Linux 环境下的一些特有实践。
头文件与库
在 Linux 中,C 语言的标准库头文件位于 /usr/include 目录下,系统级的头文件(如 unistd.h, sys/stat.h)也在这里,当你使用 #include <...> 时,编译器会自动在这里查找。
基本数据类型
与标准 C 一致,但要注意:
int: 在现代的 64 位 Linux 系统上,通常是 4 字节(32位)。long long: 用于表示更大的整数。char: 通常是 1 字节。
控制流
if, for, while, switch 等与标准 C 完全相同。
函数
Linux 系统编程充满了各种由内核提供的函数,
printf来自libc(C标准库)。open,read,write来自unistd.h(系统调用)。
第四部分:进阶篇 - Linux 系统编程
这是 Linux C 编程的精髓,即通过 C 语言调用 Linux 内核提供的 API(系统调用)来与操作系统交互。
文件 I/O (文件操作)
C 标准库 (stdio.h) 提供了 fopen, fread, fwrite, fclose 等高级接口,而 Linux 系统调用提供了更底层的接口。
主要系统调用:
open(): 打开或创建一个文件。read(): 从文件描述符中读取数据。write(): 向文件描述符写入数据。close(): 关闭一个文件描述符。
示例:使用 read 和 write 复制文件
#include <stdio.h>
#include <unistd.h> // 用于 read, write, close
#include <fcntl.h> // 用于 open
#include <stdlib.h> // 用于 exit
#define BUFFER_SIZE 1024
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
exit(EXIT_FAILURE);
}
int src_fd = open(argv[1], O_RDONLY); // 只读方式打开源文件
if (src_fd == -1) {
perror("Error opening source file");
exit(EXIT_FAILURE);
}
int dest_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); // 写入方式创建/截断目标文件,权限644
if (dest_fd == -1) {
perror("Error opening destination file");
close(src_fd);
exit(EXIT_FAILURE);
}
char buffer[BUFFER_SIZE];
ssize_t bytes_read;
while ((bytes_read = read(src_fd, buffer, BUFFER_SIZE)) > 0) {
if (write(dest_fd, buffer, bytes_read) != bytes_read) {
perror("Error writing to destination file");
close(src_fd);
close(dest_fd);
exit(EXIT_FAILURE);
}
}
if (bytes_read == -1) {
perror("Error reading from source file");
}
close(src_fd);
close(dest_fd);
printf("File copied successfully.\n");
return 0;
}
编译并运行:
gcc copy_file.c -o copy_file ./copy_file my_file.txt my_file_copy.txt
perror() 函数: 这是一个非常有用的函数,它会打印你传入的字符串,然后附加一个冒号和 errno 对应的错误信息,帮助你快速定位问题。
进程控制
fork(): 创建一个与当前进程几乎完全相同的子进程。fork()调用一次,返回两次,在父进程中返回子进程的 PID,在子进程中返回 0。exec(): 用一个新的程序替换当前进程的映像,通常与fork()结合使用,实现“创建子进程并执行新程序”。wait(): 父进程调用此函数来等待子进程结束。exit(): 终止当前进程。
示例:创建子进程并执行 ls 命令
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid > 0) {
// 父进程
printf("Parent: Child with PID %d created.\n", pid);
wait(NULL); // 等待子进程结束
printf("Parent: Child has finished.\n");
} else {
// 子进程
printf("Child: Executing 'ls -l'...\n");
char *args[] = {"ls", "-l", NULL};
execvp("ls", args); // 执行 ls -l 命令
// execvp 成功,下面的代码不会被执行
perror("execvp failed");
exit(EXIT_FAILURE);
}
return 0;
}
信号
信号是 Linux 进程间通信的一种异步机制,当你按下 Ctrl+C 时,操作系统会向前台进程发送一个 SIGINT 信号。
signal(): 用于捕获和处理信号。
示例:捕获 SIGINT (Ctrl+C)
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handle_sigint(int sig) {
printf("\nCaught SIGINT! No longer exiting on Ctrl+C.\n");
// 在这里可以执行清理工作
}
int main() {
signal(SIGINT, handle_sigint); // 注册信号处理函数
while (1) {
printf("Program is running... Press Ctrl+C to test.\n");
sleep(1);
}
return 0;
}
第五部分:实战篇 - 构建一个简单的 Shell
这是检验你学习成果的绝佳方式,我们将创建一个名为 myshell 的程序,它可以:
- 打印一个提示符(如
myshell>)。 - 读取用户输入的命令。
- 解析命令(
ls -l)。 - 创建子进程并执行该命令。
- 等待命令执行完毕,然后再次显示提示符。
// myshell.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_LINE 80 // 最大命令长度
void parse_input(char *input, char **args) {
char *token;
int i = 0;
token = strtok(input, " \n"); // 按空格和换行符分割
while (token != NULL && i < MAX_LINE - 1) {
args[i++] = token;
token = strtok(NULL, " \n");
}
args[i] = NULL; // 参数列表必须以 NULL
}
int main() {
char input[MAX_LINE];
char *args[MAX_LINE / 2 + 1]; // 命令参数列表
while (1) {
printf("myshell> ");
fflush(stdout); // 确保提示符立即显示
// 读取用户输入
if (fgets(input, sizeof(input), stdin) == NULL) {
break; // 处理 EOF (Ctrl+D)
}
// 解析输入
parse_input(input, args);
if (args[0] == NULL) {
continue; // 空输入
}
// 内置命令 exit
if (strcmp(args[0], "exit") == 0) {
break;
}
pid_t pid = fork();
if (pid == 0) {
// 子进程
execvp(args[0], args);
perror("execvp failed"); // execvp 返回,说明执行失败
exit(EXIT_FAILURE);
} else if (pid > 0) {
// 父进程
wait(NULL); // 等待子进程结束
} else {
perror("fork failed");
}
}
return 0;
}
编译并运行:
gcc myshell.c -o myshell ./myshell
现在你就有了一个可以运行 ls, pwd, echo hello 等简单命令的迷你 Shell 了!
第六部分:工具篇 - 提升开发效率
GDB (GNU Debugger) - 调试你的程序
当程序出现 bug 时,gdb 是你的好朋友。
使用步骤:
- 编译时加上
-g选项:生成包含调试信息的可执行文件。gcc -g mybuggy.c -o mybuggy
- 启动 GDB:
gdb ./mybuggy
- 常用 GDB 命令:
(gdb) break main或(gdb) b main: 在main函数处设置断点。(gdb) run或(gdb) r: 运行程序。(gdb) next或(gdb) n: 下一行(不进入函数)。(gdb) step或(gdb) s: 下一行(进入函数)。(gdb) print my_var或(gdb) p my_var: 打印变量my_var的值。(gdb) continue或(gdb) c: 继续运行,直到下一个断点。(gdb) quit: 退出 GDB。
Makefile - 自动化构建
当项目文件变多时,每次手动输入 gcc 命令变得非常繁琐。Makefile 定义了一系列规则,告诉 make 如何编译和链接你的程序。
一个简单的 Makefile 示例 (Makefile 文件内容):
# 定义变量
CC = gcc
CFLAGS = -Wall -g
TARGET = my_program
SRCS = main.c utils.c helper.c
OBJS = $(SRCS:.c=.o)
# 默认目标,当 make 不带参数时执行
all: $(TARGET)
# 链接规则:如何生成最终可执行文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
# 编译规则:如何从 .c 文件生成 .o 文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理生成的文件
clean:
rm -f $(OBJS) $(TARGET)
# .PHONY 表示这是一个伪目标,不生成文件
.PHONY: all clean
使用方法:
make: 执行all目标,编译所有.c文件并链接成my_program。make clean: 删除所有.o文件和最终的可执行文件my_program。
第七部分:资源与总结
推荐书籍
- 《C 程序设计语言》 (The C Programming Language, K&R): C 语言圣经,必读。
- 《C Primer Plus》: 内容详尽,适合初学者系统学习。
- 《Unix 环境高级编程》 (Advanced Programming in the Unix Environment, APUE): Linux 系统编程的“圣经”,深入浅出地讲解了几乎所有重要的 Unix API。
- 《Linux/UNIX 系统编程手册》 (Linux/UNIX System Programming): 另一本系统编程领域的巨著,内容非常全面。
在线资源
- man 手册页: Linux 自带的最佳文档,在终端中输入
man 2 open查看open系统调用的手册(2代表系统调用)。 - The Linux Programming Interface (TLPI) Online Resources: https://man7.org/tlpi/ 提供了书籍的示例代码和勘误。
- Stack Overflow: 解决编程问题的绝佳社区。
学习 Linux C 编程是一个循序渐进的过程:
- 打好基础: 熟练掌握 C 语言语法。
- 熟悉工具: 学会使用 GCC, GDB, Make。
- 深入系统: 理解文件、进程、内存等核心概念,并学习如何通过系统调用与它们交互。
- 动手实践: 通过编写小程序(如 Shell, Web 服务器, 文件工具)来巩固知识。
祝你学习愉快!
