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

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

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)未检查 NULLp = 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-3POSIX 标准体系、文件系统模型、进程生命周期
核心机制篇4-7线程同步、信号处理、I/O 模型、IPC 机制
进阶篇8-12网络编程、内存管理、时间函数、环境配置、Shell 脚本
工程篇13-15可移植性、合规测试、最佳实践

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