【Linux系统编程】——【Linux内核提供的系统调用】Cyber骇客视角带你掌握 Linux 进行C++系统编程时所需的底层头文件(附带实操代码)

🥰个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
🌠 有善始者实繁,能克终者盖寡

索引与导读
前言
根据我们前面所学习的知识:
Linux 内核是用 C 语言编写的,大部分底层头文件都是 C 风格的
本章带你学习并掌握Linux环境下进行C++系统编程时需要掌握的内核系统调用
一、核心系统调用
这是最基础的部分,几乎每个系统级程序都会用到
| 头文件 | 主要功能 | 常用函数/对象 |
|---|---|---|
<unistd.h> |
Linux 标准服务 |
read, write, close, fork, pipe, getpid |
<fcntl.h> |
文件控制 | open, fcntl, O_CREAT, O_RDONLY |
<sys/types.h> |
基本系统数据类型 | pid_t, size_t, mode_t |
<sys/stat.h> |
文件状态 | stat, chmod, mkdir |
1.1 <fcntl.h> 与 <unistd.h>:文件的打开与读写
#include <fcntl.h> // open, O_WRONLY, O_CREAT
#include <unistd.h> // write, close
#include <iostream>
int main(){
// 1. 使用 open 打开或创建一个文件
// O_WRONLY: 只写; O_CREAT: 不存在则创建; 0644: 设置权限
int fd = open("example.txt", O_WRONLY | O_CREAT, 0644);
if(fd = -1){
perror("Open fail!!")
return 1;
}
// 2. 使用 write 写入数据
const char* text = "Hello Linux System Programming!";
write(fd, text, 31);
// 3. 使用 close 关闭文件描述符
close(fd);
cout << "文件写入并关闭成功!" << endl;
return 0;
}
1.2 <sys/stat.h>:获取文件的“身份证”信息
如果你想知道一个文件有多大、什么时候修改的、是文件夹还是普通文件,就用这个头文件
#include <sys/stat.h> // 包含系统状态结构 struct stat 和相关宏(如 S_ISDIR)
#include <iostream> // 包含标准输入输出流(cout, endl)
#include <ctime> // 包含 ctime 函数,用于处理时间转换
using namespace std; // 引入标准命名空间,之后可以直接使用 cout, endl 等
int main() {
// 定义一个结构体变量,用于接收内核返回的文件属性信息
struct stat file_info;
// --- 1. 获取文件状态 ---
// stat() 会将 "example.txt" 的元数据填充到 file_info 结构体中
if (stat("example.txt", &file_info) == 0) {
// st_size 记录文件字节数
cout << "文件大小: " << file_info.st_size << " 字节" << endl;
// --- 2. 判定文件类型 ---
// S_ISDIR 宏通过逻辑位运算检查 st_mode 字段
if (S_ISDIR(file_info.st_mode)) {
cout << "文件类型: 这是一个目录" << endl;
}
else if (S_ISREG(file_info.st_mode)) {
cout << "文件类型: 这是一个普通文件" << endl;
}
// --- 3. 时间戳转换 (进阶演示) ---
// ctime() 将 st_mtime (秒数) 转换为人类可读的字符串(如 Mon Jun 1 21:15:00 2026)
cout << "最后修改时间: " << ctime(&file_info.st_mtime); // ctime 自带换行
} else {
// 如果当前目录下没有 example.txt,代码会执行到这里
cerr << "提示:未找到 example.txt,请先运行之前的写入示例。" << endl;
}
// --- 4. 创建新目录 ---
// 0755 代表: 所有者(rwx=7), 组用户(r-x=5), 其他用户(r-x=5)
if (mkdir("test_dir", 0755) == 0) {
cout << "目录 'test_dir' 创建成功!" << endl;
} else {
// 报错的常见原因:目录已存在或权限不足
cout << "目录创建失败,可能已存在。" << endl;
}
return 0;
}
1.3 <sys/types.h>:跨平台的类型保障
这个头文件更像是一个“翻译官”。它定义了如 pid_t(进程 ID 类型)、mode_t(权限模式类型)等。使用它能保证你的代码在不同的 Linux 架构下(如 32 位或 64 位)都能跑通
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
int main() {
// 使用 pid_t 而不是普通的 int,这更符合系统编程规范
pid_t my_pid = getpid();
pid_t parent_pid = getppid();
std::cout << "当前进程 ID: " << my_pid << std::endl;
std::cout << "父进程 ID: " << parent_pid << std::endl;
return 0;
}
1.4 <unistd.h> 特色功能:创建子进程
除了读写文件,<unistd.h> 还有个绝活——fork(),它是 Linux 进程管理的灵魂
#include <unistd.h>
#include <iostream>
int main() {
pid_t pid = fork();
if (pid == 0) {
// fork 返回 0,代表这里是子进程
std::cout << "我是子进程,PID: " << getpid() << std::endl;
} else if (pid > 0) {
// fork 返回正数(子进程的 PID),代表这里是父进程
std::cout << "我是父进程,我创建了子进程: " << pid << std::endl;
}
return 0;
}
总结一下区别:
<sys/types.h>:提供数据类型(如pid_t、size_t)<fcntl.h>:提供文件操作标志(如O_RDONLY、O_NONBLOCK)<sys/stat.h>:提供文件属性查询(如文件大小、权限)<unistd.h>:提供核心系统接口(如read、write、fork、pipe)
二、多进程与信号处理
Linux 是多任务系统,处理进程间的交互至关重要
<sys/wait.h>:用于父进程等待子进程结束(如wait,waitpid)<signal.h>:处理系统信号 在C++中,通常需要处理SIGINT(Ctrl+C)或SIGPIPE<sys/ipc.h>&<sys/shm.h>:共享内存交互,属于较老但强大的进程间通信方式
2.1 多进程:<sys/wait.h> + fork()
这段代码演示父进程如何创建子进程,并使用 wait() 优雅地为子进程回收资源,防止产生僵尸进程
#include <iostream>
#include <unistd.h> // fork, sleep
#include <sys/wait.h> // wait
using namespace std;
int main() {
// 创建一个分身(子进程)
pid_t pid = fork();
if (pid == -1) {
perror("fork 失败");
return 1;
}
if (pid == 0) {
// --- 这里是子进程的代码区 ---
cout << "[子进程] 正在运行,我的 PID 是: " << getpid() << endl;
sleep(2); // 模拟子进程干活 2 秒
cout << "[子进程] 工作完成,准备退出。" << endl;
return 88; // 退出码,可以传给父进程
} else {
// --- 这里是父进程的代码区 ---
cout << "[父进程] 我在等待子进程 (PID: " << pid << ") 结束..." << endl;
int status;
// wait 函数会阻塞父进程,直到子进程结束
// status 用于接收子进程的退出状态
wait(&status);
if (WIFEXITED(status)) {
cout << "[父进程] 检测到子进程正常退出,退出状态码: " << WEXITSTATUS(status) << endl;
}
}
return 0;
}
2.2 信号处理:<signal.h>
信号是Linux发送给进程的异步通知
最常见的就是你按 Ctrl+C 时,系统会发送 SIGINT 信号
#include <iostream>
#include <signal.h> // 信号处理头文件
#include <unistd.h> // sleep
using namespace std;
// 这是一个信号处理函数(回调函数)
// 当指定的信号发生时,内核会自动跳过来执行这段代码
void my_handler(int sig_num) {
cout << "\n[收到信号] 捕获到信号编号: " << sig_num << endl;
cout << "[提示] 你是不是按了 Ctrl+C?但我还没想退出呢!" << endl;
// 在实际开发中,这里通常用于平滑关闭数据库连接、保存日志等
}
int main() {
// --- 注册信号处理器 ---
// 告诉内核:如果有人发 SIGINT (Ctrl+C),请执行 my_handler
signal(SIGINT, my_handler);
cout << "程序已启动,正在循环运行。请尝试按 Ctrl+C 试试..." << endl;
while (true) {
cout << "工作中..." << endl;
sleep(2);
}
return 0;
}
2.3 共享内存 (IPC):<sys/ipc.h> & <sys/shm.h>
共享内存允许两个完全独立的进程访问同一块物理内存,这是最快的进程间通信方式
由于代码逻辑较长(涉及创建、映射、读写、分离、删除),后面我们会展开讲解
三、网络编程 (Sockets)
Linux 的网络栈非常强大,主要涉及以下头文件:
<sys/socket.h>:核心套接字函数,如socket,bind,listen,accept<netinet/in.h>:定义互联网协议族(如struct sockaddr_in)<arpa/inet.h>:IP地址转换工具(如inet_pton,ntohs)<netdb.h>:域名解析(如getaddrinfo)
后面我们到
Linux系统编程的时候会讲解的
四、多线程编程
虽然 C++11 引入了原生的 <thread>,但在 Linux 系统编程中,底层往往是基于 POSIX Threads (Pthreads)
在 Linux 系统编程中,Pthreads 是多线程的标准实现 虽然现代 C++ 程序员习惯使用 <thread>,但在底层高性能服务器开发中,pthread 仍然是不可替代的,因为它提供了诸如线程分离、设置栈大小、设置调度策略、线程亲和性(绑定 CPU 核)等底层 API
4.1 核心头文件 <pthread.h>
要使用 Pthreads,必须包含这个头文件,并在编译时手动链接库:
g++ main.cpp -O main -lpthread
4.2 实操案例
#include <iostream>
#include <pthread.h> // 核心头文件
#include <unistd.h> // sleep
using namespace std;
// 线程执行函数必须符合这个格式:void* 函数名(void*)
// 因为 void* 可以指向任何类型,这让我们可以传递任意参数
void* thread_work(void* arg) {
// 将 void* 强制转回我们需要的类型
char* msg = (char*)arg;
for (int i = 0; i < 3; ++i) {
cout << "[子线程] 收到消息: " << msg << ",正在处理 " << i << "..." << endl;
sleep(1);
}
// 线程结束,可以返回一个结果
return (void*)"子线程任务完成!";
}
int main() {
pthread_t my_thread; // 线程的“身份证”变量
const char* message = "Hello from Main!";
// --- 1. 创建线程 ---
/* 参数详解:
* &my_thread : 存储新线程 ID
* NULL : 线程属性(通常用 NULL 表示默认属性)
* thread_work: 线程要运行的函数名
* (void*)message : 传给该函数的参数
*/
if (pthread_create(&my_thread, NULL, thread_work, (void*)message) != 0) {
cerr << "线程创建失败!" << endl;
return 1;
}
cout << "[主线程] 子线程已启动,我先去喝杯咖啡..." << endl;
// --- 2. 等待线程结束 (Join) ---
/* 就像 wait() 等待子进程一样,pthread_join 会阻塞主线程,
* 直到指定的线程运行结束。第二个参数用于接收返回值。
*/
void* result;
pthread_join(my_thread, &result);
cout << "[主线程] 子线程返回信息: " << (char*)result << endl;
cout << "[主线程] 程序退出。" << endl;
return 0;
}
4.3 线程同步:互斥锁<pthread.h>
多线程是最怕乱的,两个线程同时改一个变量就会导致崩溃
pthread提供了互斥锁(Mutex):
pthread_mutex_t lock; // 定义一把锁
pthread_mutex_init(&lock, NULL); // 初始化锁
pthread_mutex_lock(&lock); // 【加锁】— 进房间关门
// --- 临界区:操作共享资源 ---
pthread_mutex_unlock(&lock); // 【解锁】— 出房间开门
pthread_mutex_destroy(&lock); // 销毁锁
五、高性能 I/O 多路复用(自行了解)
这是开发高性能服务器(如 Web Server)的关键
-
<sys/select.h>:经典的select模型 -
<sys/poll.h>:poll模型 -
<sys/epoll.h>:Linux特有 在大规模并发连接下性能最强,是现代Linux网络引擎的核心
六、C 与 C++ 的转换
为了更符合 C++ 风格,建议优先使用带 c 前缀的头文件(如果标准库提供):
- 用
<cstdio>代替<stdio.h> - 用
<cstring>代替<string.h> - 用
<cerrno>代替<errno.h>(用于调试系统错误)
七、错误处理
Linux 系统调用失败通常返回 -1
你需要结合 <cerrno> 和 <cstring> 来获取可读的错误信息:
#include <iostream>
#include <cstring>
#include <cerrno>
if (write(fd, buf, size) == -1) {
cerr << "Error: " << strerror(errno) << endl;
}
💻结尾— 核心连接协议
警告: 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠
【📡】 建立深度链接: 关注本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。
【⚡】 能量过载分发: 执行点赞操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。
【💾】 离线缓存核心: 将本页加入收藏。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。
【💬】 协议加密解密: 在评论区留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。
【🛰️】 信号频率投票: 通过投票发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。


更多推荐


所有评论(0)