C++ 操作系统与系统编程

✍️ Fer·📅 2026年8月19日·👁 2 次阅读
c++开发
📚 系列:现代 C++ 学习之路

一、进程管理

⭐ 进程的创建与终止

cpp
#include <unistd.h>
#include <sys/wait.h>

void ForkExample() {
    pid_t pid = fork();

    if (pid < 0) {
        perror("fork failed");
    } else if (pid == 0) {
        // 子进程
        std::cout << "Child PID: " << getpid() << "\n";
        execlp("ls", "ls", "-la", nullptr);  // 替换为 ls 程序
        _exit(1);  // exec 失败才会到这里
    } else {
        // 父进程
        std::cout << "Parent PID: " << getpid() << ", Child: " << pid << "\n";
        int status;
        waitpid(pid, &status, 0);  // 等待子进程结束
        if (WIFEXITED(status)) {
            std::cout << "Child exited with code: " << WEXITSTATUS(status) << "\n";
        }
    }
}

⭐ 进程 vs 线程选择

特性多进程多线程
隔离性✅ 强(崩溃不互相影响)❌ 弱(一个崩全部崩)
通信IPC(较复杂)共享内存(简单)
创建开销
调试较难较容易
适用场景高可靠性(如 Nginx)高性能(如线程池)

二、进程间通信(IPC)

⭐ IPC 方式概览

⭐ 管道(Pipe)

cpp
#include <unistd.h>

void PipeExample() {
    int fd[2];  // fd[0] 读端, fd[1] 写端
    pipe(fd);

    pid_t pid = fork();
    if (pid == 0) {
        // 子进程:写入
        close(fd[0]);  // 关闭读端
        const char* msg = "Hello from child";
        write(fd[1], msg, strlen(msg));
        close(fd[1]);
        _exit(0);
    } else {
        // 父进程:读取
        close(fd[1]);  // 关闭写端
        char buf[256]{};
        read(fd[0], buf, sizeof(buf));
        std::cout << "Received: " << buf << "\n";
        close(fd[0]);
        wait(nullptr);
    }
}

⭐ 共享内存(最快的 IPC)

cpp
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

// POSIX 共享内存
void SharedMemoryExample() {
    const char* name = "/my_shm";
    const int SIZE = 4096;

    // 创建/打开共享内存
    int fd = shm_open(name, O_CREAT | O_RDWR, 0666);
    ftruncate(fd, SIZE);

    // 映射到进程地址空间
    void* ptr = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    // 写入数据
    sprintf((char*)ptr, "Hello, shared memory!");

    // 读取数据(另一个进程也可以读取同一块内存)
    std::cout << (char*)ptr << "\n";

    // 清理
    munmap(ptr, SIZE);
    shm_unlink(name);
}

⭐ mmap 内存映射文件

cpp
#include <sys/mman.h>
#include <fcntl.h>

// 将文件映射到内存(零拷贝读取)
std::string ReadFileWithMmap(const std::string& path) {
    int fd = open(path.c_str(), O_RDONLY);
    if (fd < 0) return "";

    struct stat sb;
    fstat(fd, &sb);
    size_t size = sb.st_size;

    void* addr = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
    close(fd);

    if (addr == MAP_FAILED) return "";

    std::string content((char*)addr, size);
    munmap(addr, size);
    return content;
}

三、信号(Signal)

⭐ 常见信号

信号编号含义默认动作
SIGINT2Ctrl+C 中断终止
SIGTERM15终止请求终止
SIGKILL9强制终止终止(不可捕获)
SIGSEGV11段错误终止+core
SIGCHLD17子进程终止忽略
SIGPIPE13写已关闭的管道终止
SIGUSR1/210/12用户自定义终止

信号处理

cpp
#include <signal.h>
#include <atomic>

std::atomic<bool> g_running{true};

// 信号处理函数(注意:只能调用异步信号安全函数)
void SignalHandler(int sig) {
    if (sig == SIGINT || sig == SIGTERM) {
        g_running.store(false);  // 优雅退出
    }
}

void SetupSignals() {
    struct sigaction sa{};
    sa.sa_handler = SignalHandler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    sigaction(SIGINT, &sa, nullptr);
    sigaction(SIGTERM, &sa, nullptr);

    // 忽略 SIGPIPE(网络编程必需)
    signal(SIGPIPE, SIG_IGN);
}

int main() {
    SetupSignals();
    while (g_running) {
        DoWork();
    }
    Cleanup();
    return 0;
}

四、文件描述符与 I/O

⭐ 文件描述符

┌──────────────────────────────────────────┐
│  进程的文件描述符表                        │
│                                          │
│  fd 0 ──→ stdin  (标准输入)              │
│  fd 1 ──→ stdout (标准输出)              │
│  fd 2 ──→ stderr (标准错误)              │
│  fd 3 ──→ 打开的文件 / socket / pipe     │
│  fd 4 ──→ ...                            │
│  ...                                      │
└──────────────────────────────────────────┘

⭐ 非阻塞 I/O

cpp
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

// 设置非阻塞
void SetNonBlocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

// 非阻塞读取
ssize_t NonBlockingRead(int fd, char* buf, size_t size) {
    ssize_t n = read(fd, buf, size);
    if (n < 0) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            return 0;  // 暂时没有数据,不是错误
        }
        return -1;     // 真正的错误
    }
    return n;
}

五种 I/O 模型


五、定时器

⭐ 常用定时器方案

cpp
// 1. sleep / usleep(简单但不精确)
sleep(1);            // 秒
usleep(1000);        // 微秒

// 2. timerfd(推荐,配合 epoll)
#include <sys/timerfd.h>

int CreateTimer(int interval_ms) {
    int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);

    itimerspec ts{};
    ts.it_value.tv_sec = interval_ms / 1000;
    ts.it_value.tv_nsec = (interval_ms % 1000) * 1000000;
    ts.it_interval = ts.it_value;  // 重复

    timerfd_settime(tfd, 0, &ts, nullptr);
    return tfd;  // 可以放入 epoll 监听
}

// 3. C++ 标准库计时
#include <chrono>
#include <thread>

void PreciseSleep(int ms) {
    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}

六、守护进程(Daemon)

⭐ 创建守护进程的标准步骤

cpp
void Daemonize() {
    pid_t pid = fork();
    if (pid > 0) _exit(0);     // 父进程退出
    if (pid < 0) _exit(1);

    setsid();                   // 新会话

    pid = fork();               // 第二次 fork
    if (pid > 0) _exit(0);
    if (pid < 0) _exit(1);

    chdir("/");
    umask(0);

    // 关闭标准 fd
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);

    // 重定向到 /dev/null
    open("/dev/null", O_RDWR);  // fd 0
    dup(0);                     // fd 1
    dup(0);                     // fd 2
}

七、系统调用与性能

⭐ 减少系统调用的技巧

优化说明效果
缓冲 I/Ofread/fwrite 内部缓冲减少 read/write 次数
mmap内存映射替代 read/write零拷贝
sendfile内核态直接传文件到 socket零拷贝网络传输
writev/readv散布/聚集 I/O合并多次 write
epoll事件驱动替代轮询高并发
io_uring (Linux 5.1+)异步 I/O 新接口极致性能

sendfile 零拷贝

cpp
#include <sys/sendfile.h>

// 将文件直接发送到 socket(不经过用户态)
void SendFileToSocket(int file_fd, int socket_fd, size_t size) {
    off_t offset = 0;
    sendfile(socket_fd, file_fd, &offset, size);
    // 数据路径:磁盘 → 内核缓冲区 → socket(不经过用户空间)
}

八、总结知识图谱

💬 评论

加载评论中...
📑 文章目录