强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

POSIX 标准详解教程 / 第十五章:最佳实践

第十五章:最佳实践

系统编程规范总结:错误处理、安全编码、性能优化、代码组织、常见陷阱。


15.1 错误处理规范

15.1.1 POSIX 错误处理原则

原则 说明
始终检查返回值 所有系统调用和库函数都可能失败
使用 errno 失败时 errno 包含具体错误码
及时处理 在调用后立即检查,不要延迟
清理资源 错误路径中释放已分配的资源
不忽略错误 即使"不可能失败"的调用也要检查

15.1.2 标准错误处理模式

/*
 * error_handling.c - POSIX 错误处理最佳实践
 * 编译: gcc -Wall -o error_handling error_handling.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

/* ========== 模式 1: 直接退出(适用于致命错误) ========== */
void fatal(const char *msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

/* ========== 模式 2: 返回错误码(适用于可恢复错误) ========== */
typedef enum {
    ERR_OK = 0,
    ERR_NOMEM,
    ERR_IO,
    ERR_NOT_FOUND,
    ERR_PERMISSION,
} error_t;

const char *error_string(error_t err)
{
    switch (err) {
    case ERR_OK:         return "成功";
    case ERR_NOMEM:      return "内存不足";
    case ERR_IO:         return "I/O 错误";
    case ERR_NOT_FOUND:  return "未找到";
    case ERR_PERMISSION: return "权限不足";
    default:             return "未知错误";
    }
}

/* ========== 模式 3: goto 清理(推荐的资源管理模式) ========== */
error_t process_file(const char *path, char **result)
{
    error_t ret = ERR_OK;
    int fd = -1;
    char *buf = NULL;

    fd = open(path, O_RDONLY);
    if (fd == -1) {
        switch (errno) {
        case ENOENT:  return ERR_NOT_FOUND;
        case EACCES:  return ERR_PERMISSION;
        default:      return ERR_IO;
        }
    }

    /* 获取文件大小 */
    off_t size = lseek(fd, 0, SEEK_END);
    if (size == -1) { ret = ERR_IO; goto cleanup; }
    lseek(fd, 0, SEEK_SET);

    /* 分配缓冲区 */
    buf = malloc((size_t)size + 1);
    if (!buf) { ret = ERR_NOMEM; goto cleanup; }

    /* 读取文件 */
    ssize_t n = read(fd, buf, (size_t)size);
    if (n != size) { ret = ERR_IO; goto cleanup; }
    buf[size] = '\0';

    *result = buf;
    buf = NULL;  /* 防止 cleanup 释放 */

cleanup:
    if (buf) free(buf);
    if (fd >= 0) close(fd);
    return ret;
}

/* ========== 模式 4: 安全的包装函数 ========== */
static void *safe_malloc(size_t size)
{
    void *p = malloc(size);
    if (!p) {
        fprintf(stderr, "malloc(%zu) 失败: %s\n", size, strerror(errno));
        exit(EXIT_FAILURE);
    }
    return p;
}

static char *safe_strdup(const char *s)
{
    char *dup = strdup(s);
    if (!dup) {
        fprintf(stderr, "strdup 失败: %s\n", strerror(errno));
        exit(EXIT_FAILURE);
    }
    return dup;
}

int main(void)
{
    /* 使用 goto 清理模式 */
    char *content = NULL;
    error_t err = process_file("/etc/hostname", &content);
    if (err != ERR_OK) {
        fprintf(stderr, "错误: %s\n", error_string(err));
        return 1;
    }
    printf("文件内容: %s", content);
    free(content);

    /* 使用安全包装函数 */
    char *dup = safe_strdup("Hello, POSIX!");
    printf("复制: %s\n", dup);
    free(dup);

    return 0;
}

15.1.3 常见 errno 值及处理

errno 宏名 常见原因 建议处理
2 ENOENT 文件不存在 检查路径或创建文件
13 EACCES 权限不足 检查权限或请求提升
12 ENOMEM 内存不足 减少使用或优雅退出
4 EINTR 被信号中断 重试操作
11 EAGAIN 资源暂时不可用 稍后重试
9 EBADF 无效文件描述符 检查 fd 有效性
22 EINVAL 无效参数 检查输入参数
32 EPIPE 管道破裂 关闭连接
110 ETIMEDOUT 操作超时 重试或报错

15.2 安全编程

15.2.1 缓冲区安全

/*
 * secure_coding.c - 安全编码实践
 * 编译: gcc -Wall -o secure_coding secure_coding.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ========== 规则 1: 使用安全的字符串函数 ========== */

/* ❌ 不安全 */
void unsafe_example(char *input)
{
    char buf[64];
    /* strcpy 不检查长度,可能导致溢出 */
    strcpy(buf, input);  /* 危险! */

    /* sprintf 不检查长度 */
    sprintf(buf, "Hello %s", input);  /* 危险! */

    /* gets 不检查长度(已从 C11 移除) */
    /* gets(buf);  /* 绝对禁止! */
}

/* ✅ 安全 */
void safe_example(const char *input)
{
    char buf[64];

    /* 使用 strncpy 并确保 null 终止 */
    strncpy(buf, input, sizeof(buf) - 1);
    buf[sizeof(buf) - 1] = '\0';

    /* 使用 snprintf */
    snprintf(buf, sizeof(buf), "Hello %s", input);

    /* 使用 fgets 替代 gets */
    if (fgets(buf, sizeof(buf), stdin)) {
        /* 去除换行符 */
        buf[strcspn(buf, "\n")] = '\0';
    }
}

/* ========== 规则 2: 整数溢出检查 ========== */

int safe_add(size_t a, size_t b, size_t *result)
{
    if (a > SIZE_MAX - b) return -1;  /* 溢出检查 */
    *result = a + b;
    return 0;
}

int safe_mul(size_t a, size_t b, size_t *result)
{
    if (a != 0 && b > SIZE_MAX / a) return -1;  /* 溢出检查 */
    *result = a * b;
    return 0;
}

/* ========== 规则 3: 格式化字符串安全 ========== */

void safe_format(const char *user_input)
{
    /* ❌ 用户输入直接作为格式字符串 */
    /* printf(user_input);  /* 格式字符串攻击! */

    /* ✅ 使用 %s 格式化 */
    printf("%s\n", user_input);
}

/* ========== 规则 4: 安全的临时文件 ========== */
#include <unistd.h>
#include <fcntl.h>

int create_safe_tempfile(char *path_buf, size_t buf_size)
{
    /* 使用 mkstemp 而非 tmpnam/mktemp */
    snprintf(path_buf, buf_size, "/tmp/myapp_XXXXXX");
    int fd = mkstemp(path_buf);
    if (fd == -1) return -1;

    /* 确保文件权限安全(仅所有者可读写) */
    fchmod(fd, 0600);
    return fd;
}

int main(void)
{
    /* 整数溢出检查 */
    size_t result;
    if (safe_mul(1024, 1024, &result) == 0)
        printf("1024 * 1024 = %zu\n", result);

    /* 安全临时文件 */
    char tmp_path[256];
    int fd = create_safe_tempfile(tmp_path, sizeof(tmp_path));
    if (fd >= 0) {
        printf("安全临时文件: %s\n", tmp_path);
        write(fd, "safe data", 9);
        close(fd);
        unlink(tmp_path);
    }

    safe_example("world");
    safe_format("user input with % special chars");

    return 0;
}

15.2.2 输入验证清单

检查项 说明
长度 字符串不超过缓冲区大小
范围 数值在有效范围内
类型 数字确实是数字,路径不包含 ..
空字符 字符串不含嵌入的 \0
编码 有效的 UTF-8 编码
权限 调用者有权限执行操作
来源 不信任外部输入(文件、网络、环境变量)

15.3 资源管理

15.3.1 RAII 风格(C 语言实现)

/*
 * resource_raii.c - C 语言中的 RAII 风格资源管理
 * 编译: gcc -Wall -o resource_raii resource_raii.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>

/* ========== 自动清理的智能指针 ========== */

/* 清理函数类型 */
typedef void (*cleanup_fn)(void *);

/* 带清理函数的包装结构 */
typedef struct {
    void *ptr;
    cleanup_fn cleanup;
} auto_ptr_t;

/* __attribute__((cleanup)) 实现自动清理(GCC/Clang) */
#define AUTO_FREE __attribute__((cleanup(auto_free_impl)))
#define AUTO_CLOSE __attribute__((cleanup(auto_close_impl)))

static void auto_free_impl(void *p)
{
    void **pp = (void **)p;
    if (*pp) {
        free(*pp);
        *pp = NULL;
    }
}

static void auto_close_impl(void *p)
{
    int *fd = (int *)p;
    if (*fd >= 0) {
        close(*fd);
        *fd = -1;
    }
}

/* 使用示例 */
int process_with_cleanup(const char *path)
{
    AUTO_FREE char *buf = NULL;
    AUTO_CLOSE int fd = -1;

    fd = open(path, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return -1;
    }

    buf = malloc(4096);
    if (!buf) {
        perror("malloc");
        return -1;  /* fd 自动关闭 */
    }

    ssize_t n = read(fd, buf, 4095);
    if (n > 0) {
        buf[n] = '\0';
        printf("读取: %s", buf);
    }

    return 0;
    /* buf 和 fd 在函数返回时自动清理 */
}

int main(void)
{
    process_with_cleanup("/etc/hostname");
    return 0;
}

15.3.2 资源泄漏预防

/*
 * resource_checklist.c - 资源管理检查清单
 * 编译: gcc -Wall -o resource_checklist resource_checklist.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>

/* ========== 文件描述符管理 ========== */
typedef struct {
    int fd;
    const char *name;
} managed_fd_t;

static managed_fd_t managed_open(const char *path, int flags, mode_t mode)
{
    managed_fd_t mfd = { .fd = -1, .name = path };
    mfd.fd = open(path, flags, mode);
    if (mfd.fd == -1) {
        fprintf(stderr, "打开 %s 失败: ", path);
        perror("");
    }
    return mfd;
}

static void managed_close(managed_fd_t *mfd)
{
    if (mfd && mfd->fd >= 0) {
        close(mfd->fd);
        mfd->fd = -1;
    }
}

/* ========== 内存管理 ========== */
typedef struct {
    void *addr;
    size_t size;
} managed_mmap_t;

static managed_mmap_t managed_mmap(size_t size, int prot, int flags)
{
    managed_mmap_t mm = { .addr = MAP_FAILED, .size = size };
    mm.addr = mmap(NULL, size, prot, flags, -1, 0);
    if (mm.addr == MAP_FAILED)
        perror("mmap");
    return mm;
}

static void managed_munmap(managed_mmap_t *mm)
{
    if (mm && mm->addr != MAP_FAILED) {
        munmap(mm->addr, mm->size);
        mm->addr = MAP_FAILED;
    }
}

int main(void)
{
    /* 使用管理的资源 */
    managed_fd_t mfd = managed_open("/tmp/resource_test",
                                     O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (mfd.fd >= 0) {
        write(mfd.fd, "test", 4);
        managed_close(&mfd);  /* 显式清理 */
    }

    managed_mmap_t mm = managed_mmap(4096, PROT_READ | PROT_WRITE,
                                      MAP_PRIVATE | MAP_ANONYMOUS);
    if (mm.addr != MAP_FAILED) {
        memset(mm.addr, 0, 4096);
        managed_munmap(&mm);  /* 显式清理 */
    }

    unlink("/tmp/resource_test");
    printf("资源管理检查完成\n");
    return 0;
}

15.4 性能优化

15.4.1 I/O 优化

/*
 * perf_io.c - I/O 性能优化技巧
 * 编译: gcc -Wall -O2 -o perf_io perf_io.c
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>

/* ========== 技巧 1: 批量读写 ========== */

/* ❌ 低效:逐字节处理 */
void process_byte_by_byte(int fd_in, int fd_out)
{
    char c;
    while (read(fd_in, &c, 1) > 0)
        write(fd_out, &c, 1);  /* 每次都是系统调用 */
}

/* ✅ 高效:使用缓冲区 */
void process_buffered(int fd_in, int fd_out, size_t buf_size)
{
    char *buf = malloc(buf_size);
    if (!buf) return;

    ssize_t n;
    while ((n = read(fd_in, buf, buf_size)) > 0) {
        /* 处理数据 */
        ssize_t written = 0;
        while (written < n) {
            ssize_t w = write(fd_out, buf + written, n - written);
            if (w == -1) break;
            written += w;
        }
    }
    free(buf);
}

/* ========== 技巧 2: 文件预分配 ========== */
#include <sys/stat.h>

void preallocate_file(int fd, off_t size)
{
    /* 使用 ftruncate 预分配(比逐块写入快) */
    ftruncate(fd, size);

    /* Linux 扩展: fallocate(更好的预分配) */
    /* fallocate(fd, 0, 0, size); */
}

/* ========== 技巧 3: 减少 stat 调用 ========== */

/* ❌ 低效 */
int is_regular_file_slow(const char *path)
{
    struct stat st;
    if (stat(path, &st) == -1) return 0;
    return S_ISREG(st.st_mode);
}

/* ✅ 高效:使用 fstat(已有 fd 时) */
int is_regular_file_fast(int fd)
{
    struct stat st;
    if (fstat(fd, &st) == -1) return 0;
    return S_ISREG(st.st_mode);
}

/* ========== 技巧 4: 使用 writev 合并小写入 ========== */
#include <sys/uio.h>

void write_header_body(int fd, const char *header, const char *body)
{
    struct iovec iov[2] = {
        { .iov_base = (void *)header, .iov_len = strlen(header) },
        { .iov_base = (void *)body,   .iov_len = strlen(body) },
    };
    /* 一次系统调用写入两个缓冲区 */
    writev(fd, iov, 2);
}

int main(void)
{
    const char *path = "/tmp/perf_test";

    /* 比较写入性能 */
    struct timespec start, end;

    /* 普通写入 */
    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (int i = 0; i < 100000; i++) {
        write(fd, "x", 1);
    }
    clock_gettime(CLOCK_MONOTONIC, &end);
    close(fd);
    printf("逐字节写入: %.3f ms\n",
           (end.tv_sec - start.tv_sec) * 1000.0 +
           (end.tv_nsec - start.tv_nsec) / 1e6);

    /* 缓冲写入 */
    fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    char buf[4096];
    memset(buf, 'x', sizeof(buf));
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (int i = 0; i < 100000; i += sizeof(buf)) {
        write(fd, buf, sizeof(buf));
    }
    clock_gettime(CLOCK_MONOTONIC, &end);
    close(fd);
    printf("缓冲写入: %.3f ms\n",
           (end.tv_sec - start.tv_sec) * 1000.0 +
           (end.tv_nsec - start.tv_nsec) / 1e6);

    unlink(path);
    return 0;
}

15.4.2 性能优化清单

优化项 说明
减少系统调用 缓冲读写,批量操作
使用 writev/readv 合并多个缓冲区的 I/O
mmap 替代 read 大文件随机访问用 mmap
O_DIRECT 绕过页缓存(特殊场景)
MADV_SEQUENTIAL 提示顺序访问
减少内存分配 预分配、内存池
避免 false sharing 线程数据对齐到缓存行
使用 poll 替代 select 不受 FD_SETSIZE 限制

15.5 代码组织

15.5.1 头文件规范

/*
 * example.h - 规范的头文件示例
 */
#ifndef EXAMPLE_H      /* 1. Include guard */
#define EXAMPLE_H

#ifdef __cplusplus      /* 2. C++ 兼容 */
extern "C" {
#endif

/* 3. 标准库包含 */
#include <stddef.h>
#include <stdint.h>

/* 4. 公开类型定义 */
typedef struct example_ctx example_ctx_t;

typedef enum {
    EXAMPLE_OK = 0,
    EXAMPLE_ERROR = -1,
} example_status_t;

/* 5. 公开 API 函数 */
example_status_t example_create(example_ctx_t **ctx);
example_status_t example_process(example_ctx_t *ctx, const void *data, size_t len);
void             example_destroy(example_ctx_t *ctx);
const char      *example_version(void);

#ifdef __cplusplus
}
#endif

#endif /* EXAMPLE_H */

15.5.2 源文件规范

/*
 * example.c - 规范的源文件示例
 */
#define _POSIX_C_SOURCE 200809L  /* 1. 特性宏 */

/* 2. 自身头文件 */
#include "example.h"

/* 3. 系统头文件 */
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>

/* 4. 私有类型和常量 */
#define MAX_ITEMS 1024

struct example_ctx {
    int count;
    char **items;
    pthread_mutex_t lock;
};

/* 5. 私有函数(static) */
static void cleanup_items(example_ctx_t *ctx)
{
    for (int i = 0; i < ctx->count; i++)
        free(ctx->items[i]);
    free(ctx->items);
    ctx->items = NULL;
    ctx->count = 0;
}

/* 6. 公开 API 实现 */
example_status_t example_create(example_ctx_t **ctx)
{
    if (!ctx) return EXAMPLE_ERROR;

    example_ctx_t *c = calloc(1, sizeof(*c));
    if (!c) return EXAMPLE_ERROR;

    pthread_mutex_init(&c->lock, NULL);
    *ctx = c;
    return EXAMPLE_OK;
}

example_status_t example_process(example_ctx_t *ctx,
                                  const void *data, size_t len)
{
    if (!ctx || !data) return EXAMPLE_ERROR;

    pthread_mutex_lock(&ctx->lock);

    if (ctx->count >= MAX_ITEMS) {
        pthread_mutex_unlock(&ctx->lock);
        return EXAMPLE_ERROR;
    }

    char *item = strndup(data, len);
    if (!item) {
        pthread_mutex_unlock(&ctx->lock);
        return EXAMPLE_ERROR;
    }

    /* 添加到列表 */
    char **new_items = realloc(ctx->items,
                                sizeof(char *) * (ctx->count + 1));
    if (!new_items) {
        free(item);
        pthread_mutex_unlock(&ctx->lock);
        return EXAMPLE_ERROR;
    }

    ctx->items = new_items;
    ctx->items[ctx->count++] = item;

    pthread_mutex_unlock(&ctx->lock);
    return EXAMPLE_OK;
}

void example_destroy(example_ctx_t *ctx)
{
    if (!ctx) return;
    pthread_mutex_lock(&ctx->lock);
    cleanup_items(ctx);
    pthread_mutex_unlock(&ctx->lock);
    pthread_mutex_destroy(&ctx->lock);
    free(ctx);
}

const char *example_version(void)
{
    return "1.0.0";
}

15.6 常见陷阱

15.6.1 陷阱清单

陷阱 说明 正确做法
if (pid = fork()) 赋值而非比较 pid = fork(); if (pid < 0)
char *p = malloc(n) 未检查 NULL p = malloc(n); if (!p) ...
read() 返回值忽略 可能部分读取 循环读取直到完成
close() 返回值忽略 NFS 等延迟写入可能在 close 时失败 检查 close() 返回值
signal() 替代 sigaction() 行为不确定 始终使用 sigaction()
sprintf 替代 snprintf 缓冲区溢出 使用 snprintf 并指定大小
strcat 替代 strncat 无长度检查 使用 snprintf 拼接
malloc 后不初始化 未定义行为 使用 callocmemset
线程中使用 strerror() 非线程安全 使用 strerror_r()
fork() 后使用 malloc 多线程中 fork 不安全 fork 后立即 exec

15.6.2 段错误调试

# 编译时添加调试信息
gcc -Wall -g -fsanitize=address -o program program.c

# AddressSanitizer (ASan) 检测内存错误
./program  # ASan 会报告内存越界、use-after-free 等

# 使用 Valgrind
valgrind --leak-check=full ./program

# 使用 GDB 调试
gcc -Wall -g -o program program.c
gdb ./program
(gdb) run
(gdb) bt  # 查看调用栈

15.7 日志与监控

15.7.1 POSIX 日志模块

/*
 * posix_logger.h - 生产级日志模块
 */
#ifndef POSIX_LOGGER_H
#define POSIX_LOGGER_H

#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>
#include <unistd.h>

typedef enum {
    LOG_DEBUG,
    LOG_INFO,
    LOG_WARN,
    LOG_ERROR,
    LOG_FATAL,
} log_level_t;

static log_level_t g_log_level = LOG_INFO;
static FILE *g_log_file = NULL;

static const char *log_level_str(log_level_t level)
{
    switch (level) {
    case LOG_DEBUG: return "DEBUG";
    case LOG_INFO:  return "INFO ";
    case LOG_WARN:  return "WARN ";
    case LOG_ERROR: return "ERROR";
    case LOG_FATAL: return "FATAL";
    default:        return "?????";
    }
}

static void log_set_level(log_level_t level) { g_log_level = level; }
static void log_set_file(FILE *fp) { g_log_file = fp; }

static void log_write(log_level_t level, const char *file,
                       int line, const char *fmt, ...)
{
    if (level < g_log_level) return;

    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    struct tm tm;
    localtime_r(&ts.tv_sec, &tm);

    char timebuf[32];
    strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", &tm);

    /* basename */
    const char *basename = strrchr(file, '/');
    basename = basename ? basename + 1 : file;

    FILE *out = g_log_file ? g_log_file : stderr;

    fprintf(out, "%s.%03ld [%s] %s:%d: ",
            timebuf, ts.tv_nsec / 1000000,
            log_level_str(level), basename, line);

    va_list args;
    va_start(args, fmt);
    vfprintf(out, fmt, args);
    va_end(args);
    fprintf(out, "\n");
    fflush(out);
}

#define LOG_DEBUG(...) log_write(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_INFO(...)  log_write(LOG_INFO,  __FILE__, __LINE__, __VA_ARGS__)
#define LOG_WARN(...)  log_write(LOG_WARN,  __FILE__, __LINE__, __VA_ARGS__)
#define LOG_ERROR(...) log_write(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_FATAL(...) log_write(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)

#endif /* POSIX_LOGGER_H */

15.8 代码审查清单

类别 检查项
错误处理 所有系统调用返回值是否检查?错误路径是否释放资源?
缓冲区安全 是否使用安全函数(snprintf, strncpy)?长度是否正确?
内存管理 malloc 是否检查 NULL?是否所有路径都释放内存?
文件描述符 所有 fd 是否在错误路径中关闭?
信号安全 信号处理函数是否只使用异步安全函数?
线程安全 共享数据是否有锁保护?是否使用线程安全函数?
整数溢出 乘法/加法是否有溢出检查?
格式化字符串 用户输入是否作为 %s 参数而非格式字符串?
权限 临时文件权限是否为 0600?是否有最小权限?
可移植性 是否依赖平台特定扩展?数据类型是否正确?

15.9 业务场景:生产就绪的 TCP 服务

/*
 * production_server.c - 生产就绪的 TCP 服务器框架
 * 编译: gcc -Wall -O2 -o production_server production_server.c -lpthread
 */
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <pthread.h>

/* ========== 日志 ========== */
#include "posix_logger.h"  /* 使用上面的日志模块 */

/* ========== 优雅关闭 ========== */
static volatile sig_atomic_t g_shutdown = 0;

static void signal_handler(int sig)
{
    (void)sig;
    g_shutdown = 1;
}

static void setup_signals(void)
{
    struct sigaction sa = {
        .sa_handler = signal_handler,
        .sa_flags = SA_RESTART,
    };
    sigemptyset(&sa.sa_mask);
    sigaction(SIGTERM, &sa, NULL);
    sigaction(SIGINT, &sa, NULL);
    signal(SIGPIPE, SIG_IGN);  /* 忽略 SIGPIPE */
}

/* ========== 套接字工具 ========== */
static int set_nonblocking(int fd)
{
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1) return -1;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

static int create_server_socket(int port)
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) { LOG_FATAL("socket: %s", strerror(errno)); return -1; }

    int opt = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(port),
        .sin_addr.s_addr = INADDR_ANY,
    };

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        LOG_FATAL("bind: %s", strerror(errno));
        close(fd);
        return -1;
    }

    if (listen(fd, 128) == -1) {
        LOG_FATAL("listen: %s", strerror(errno));
        close(fd);
        return -1;
    }

    LOG_INFO("服务器启动,端口 %d, PID=%d", port, getpid());
    return fd;
}

/* ========== 客户端处理 ========== */
static void *handle_client(void *arg)
{
    int client_fd = *(int *)arg;
    free(arg);

    char buf[4096];
    ssize_t n;

    while (!g_shutdown) {
        n = read(client_fd, buf, sizeof(buf));
        if (n <= 0) break;

        /* 回声 */
        ssize_t written = 0;
        while (written < n) {
            ssize_t w = write(client_fd, buf + written, n - written);
            if (w == -1) { if (errno == EINTR) continue; break; }
            written += w;
        }
    }

    close(client_fd);
    return NULL;
}

/* ========== 主程序 ========== */
int main(int argc, char *argv[])
{
    int port = argc > 1 ? atoi(argv[1]) : 8080;

    log_set_level(LOG_INFO);
    setup_signals();

    int server_fd = create_server_socket(port);
    if (server_fd == -1) return 1;

    while (!g_shutdown) {
        struct sockaddr_in client_addr;
        socklen_t len = sizeof(client_addr);
        int client_fd = accept(server_fd,
                               (struct sockaddr *)&client_addr, &len);
        if (client_fd == -1) {
            if (errno == EINTR) continue;
            LOG_ERROR("accept: %s", strerror(errno));
            continue;
        }

        /* TCP_NODELAY 减少延迟 */
        int opt = 1;
        setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));

        LOG_INFO("新连接 fd=%d", client_fd);

        /* 创建线程处理客户端 */
        int *fd_arg = malloc(sizeof(int));
        if (!fd_arg) { close(client_fd); continue; }
        *fd_arg = client_fd;

        pthread_t tid;
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

        if (pthread_create(&tid, &attr, handle_client, fd_arg) != 0) {
            LOG_ERROR("pthread_create: %s", strerror(errno));
            free(fd_arg);
            close(client_fd);
        }
        pthread_attr_destroy(&attr);
    }

    LOG_INFO("服务器正在关闭...");
    close(server_fd);
    LOG_INFO("服务器已关闭");

    return 0;
}

15.10 总结:POSIX 系统编程核心原则

原则 说明
一切皆文件 文件、设备、管道、套接字统一使用 fd 操作
始终检查错误 系统调用返回值和 errno
最小权限 只请求所需的权限和资源
资源显式释放 文件描述符、内存、锁用后释放
使用标准接口 POSIX API > 平台扩展
安全编码 缓冲区检查、整数溢出、格式化字符串
线程安全 使用 _r 函数、适当的同步机制
信号安全 处理函数中只使用异步安全函数
可移植性 <stdint.h> 类型、特性宏、条件编译
测试覆盖 边界条件、错误路径、跨平台测试

15.11 扩展阅读

  1. 《The Art of Unix Programming》 — Eric Raymond 著,Unix 哲学
  2. 《Secure Programming Cookbook》 — Viega & Messier 著,安全编程
  3. CERT C Coding Standardhttps://wiki.sei.cmu.edu/confluence/display/c
  4. Linux man-pages 项目https://man7.org/linux/man-pages/
  5. 《UNIX and Linux System Administration Handbook》 — 系统管理权威
  6. Google C++ Style Guide(C 部分适用):https://google.github.io/styleguide/cppguide.html
  7. MISRA C 标准:嵌入式安全编程规范

15.12 全教程总结

恭喜你完成了 POSIX 标准详解教程的全部 15 章学习!回顾一下:

部分 章节 核心收获
基础篇 1-3 POSIX 标准体系、文件系统模型、进程生命周期
核心机制篇 4-7 线程同步、信号处理、I/O 模型、IPC 机制
进阶篇 8-12 网络编程、内存管理、时间函数、环境配置、Shell 脚本
工程篇 13-15 可移植性、合规测试、最佳实践

POSIX 不仅是一份技术标准,更是 Unix 哲学的体现——简单、可组合、可移植。掌握 POSIX,就是掌握了系统编程的通用语言。