C语言企业项目实战(二)

简介: 教程来源 https://xcfsr.cn/category/software-dev.html C-Cache是企业级C语言缓存系统,采用模块化设计:清晰的目录结构(含server/protocol/datastore等)、CMake构建系统(支持多平台与Sanitizer)、高性能SDS动态字符串、渐进式哈希表(避免rehash卡顿)及内存分配器封装,兼顾性能、可维护性与生产就绪能力。

第二部分:项目结构与构建系统

2.1 目录结构规划
企业级项目必须有清晰、规范的目录结构,便于团队协作和长期维护。

c-cache/
├── CMakeLists.txt                    # 顶层CMake配置
├── README.md                         # 项目说明
├── LICENSE                           # 许可证
├── .gitignore                        # Git忽略文件
├── .clang-format                     # 代码格式化配置
├── .clang-tidy                       # 静态分析配置
│
├── src/                              # 源代码目录
│   ├── main.c                        # 程序入口
│   ├── server/                       # 服务器核心
│   │   ├── event_loop.h
│   │   ├── event_loop.c              # 事件循环(epoll封装)
│   │   ├── connection.h
│   │   ├── connection.c              # TCP连接管理
│   │   ├── acceptor.h
│   │   └── acceptor.c                # 监听器
│   │
│   ├── protocol/                     # 协议处理
│   │   ├── resp.h
│   │   ├── resp.c                    # RESP协议解析
│   │   ├── command.h
│   │   └── command.c                 # 命令分发与执行
│   │
│   ├── datastore/                    # 数据存储
│   │   ├── dict.h
│   │   ├── dict.c                    # 哈希表实现
│   │   ├── sds.h
│   │   ├── sds.c                     # 动态字符串
│   │   ├── object.h
│   │   ├── object.c                  # 数据对象(string/hash/list)
│   │   ├── expire.h
│   │   └── expire.c                  # 过期键管理
│   │
│   ├── persistence/                  # 持久化
│   │   ├── rdb.h
│   │   ├── rdb.c                     # RDB快照
│   │   ├── aof.h
│   │   └── aof.c                     # AOF日志
│   │
│   ├── replication/                  # 主从复制
│   │   ├── replication.h
│   │   └── replication.c             # 复制逻辑
│   │
│   ├── utils/                        # 工具函数
│   │   ├── alloc.h
│   │   ├── alloc.c                   # 内存分配器封装
│   │   ├── log.h
│   │   ├── log.c                     # 日志系统
│   │   ├── config.h
│   │   ├── config.c                  # 配置解析
│   │   ├── time.h
│   │   └── time.c                    # 时间工具
│   │
│   └── include/                      # 公共头文件
│       ├── version.h                 # 版本定义
│       └── error.h                   # 错误码定义
│
├── tests/                            # 测试目录
│   ├── unit/                         # 单元测试
│   │   ├── test_dict.c
│   │   ├── test_sds.c
│   │   └── test_resp.c
│   ├── integration/                  # 集成测试
│   │   └── test_server.c
│   └── benchmark/                    # 性能测试
│       └── bench_cache.c
│
├── deps/                             # 第三方依赖
│   ├── libevent/                     # libevent源码(可选)
│   └── jemalloc/                     # jemalloc源码(可选)
│
├── config/                           # 配置文件
│   ├── ccache.conf                   # 默认配置
│   └── ccache.example.conf           # 配置示例
│
├── scripts/                          # 脚本工具
│   ├── build.sh                      # 构建脚本
│   ├── run.sh                        # 运行脚本
│   ├── benchmark.sh                  # 性能测试脚本
│   └── cluster_setup.sh              # 集群部署脚本
│
└── docs/                             # 文档
    ├── architecture.md               # 架构设计
    ├── api.md                        # API文档
    └── deployment.md                 # 部署指南

2.2 CMake构建配置
企业级项目需要可靠的构建系统。CMake是C/C++项目的事实标准。

# CMakeLists.txt - 顶层配置
cmake_minimum_required(VERSION 3.15)
project(CCache VERSION 1.0.0 LANGUAGES C)

# ========== 全局配置 ==========
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)

# 设置编译选项
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    add_compile_options(-g -O0 -Wall -Wextra -Werror -Wno-unused-parameter)
    add_compile_options(-fsanitize=address -fsanitize=undefined)
    add_link_options(-fsanitize=address -fsanitize=undefined)
else()
    add_compile_options(-O3 -DNDEBUG -flto -march=native)
    add_link_options(-flto)
endif()

# 平台检测
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
    add_compile_options(-D__LINUX__)
    set(PLATFORM_LIBS pthread rt)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
    add_compile_options(-D__MACOS__)
    set(PLATFORM_LIBS pthread)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
    add_compile_options(-D__WINDOWS__)
    set(PLATFORM_LIBS ws2_32)
endif()

# ========== 第三方依赖 ==========
# 查找libevent(可选,也可自己实现epoll封装)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBEVENT REQUIRED libevent)

# 查找jemalloc(可选)
find_package(jemalloc)
if(jemalloc_FOUND)
    add_compile_options(-DUSE_JEMALLOC)
    set(EXTRA_LIBS ${EXTRA_LIBS} jemalloc::jemalloc)
endif()

# ========== 源代码组织 ==========
# 核心模块
set(CORE_SOURCES
    src/main.c
    src/server/event_loop.c
    src/server/connection.c
    src/server/acceptor.c
    src/protocol/resp.c
    src/protocol/command.c
    src/datastore/dict.c
    src/datastore/sds.c
    src/datastore/object.c
    src/datastore/expire.c
    src/persistence/rdb.c
    src/persistence/aof.c
    src/replication/replication.c
    src/utils/alloc.c
    src/utils/log.c
    src/utils/config.c
    src/utils/time.c
)

# 公共头文件目录
include_directories(
    src/include
    src
    ${LIBEVENT_INCLUDE_DIRS}
)

# 生成可执行文件
add_executable(ccache ${CORE_SOURCES})

# 链接库
target_link_libraries(ccache
    ${LIBEVENT_LIBRARIES}
    ${PLATFORM_LIBS}
    ${EXTRA_LIBS}
)

# ========== 安装配置 ==========
install(TARGETS ccache DESTINATION bin)
install(FILES config/ccache.conf DESTINATION etc)
install(DIRECTORY scripts/ DESTINATION bin/scripts)

# ========== 测试 ==========
enable_testing()
add_subdirectory(tests)

# ========== 打包配置 ==========
set(CPACK_GENERATOR "TGZ;ZIP")
set(CPACK_PACKAGE_NAME "ccache")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
include(CPack)

第三部分:核心数据结构实现

3.1 SDS(Simple Dynamic String)实现
C语言字符串的痛点:获取长度需要O(n)遍历、缓冲区溢出风险、二进制不安全。SDS是Redis采用的高性能字符串实现,我们将其作为C-Cache的基础组件。

// src/datastore/sds.h
#ifndef CCACHE_SDS_H
#define CCACHE_SDS_H

#include <stddef.h>
#include <stdint.h>

/* SD S头部结构(5种类型,根据字符串长度选择) */
#define SDS_TYPE_5  0  // 长度 < 32
#define SDS_TYPE_8  1  // 长度 < 256
#define SDS_TYPE_16 2  // 长度 < 64K
#define SDS_TYPE_32 3  // 长度 < 4GB
#define SDS_TYPE_64 4  // 长度 < 2^63

/* 内部使用的SDS头(内存布局:len + alloc + flags + buf[]) */
struct __attribute__((__packed__)) sdshdr8 {
    uint8_t len;      // 已使用长度
    uint8_t alloc;    // 分配的总长度(不含header和'\0')
    unsigned char flags; // 类型标识(低3位)
    char buf[];
};

struct __attribute__((__packed__)) sdshdr16 {
    uint16_t len;
    uint16_t alloc;
    unsigned char flags;
    char buf[];
};

struct __attribute__((__packed__)) sdshdr32 {
    uint32_t len;
    uint32_t alloc;
    unsigned char flags;
    char buf[];
};

struct __attribute__((__packed__)) sdshdr64 {
    uint64_t len;
    uint64_t alloc;
    unsigned char flags;
    char buf[];
};

/* SDS类型定义 */
typedef char* sds;

/* 基础操作API */
sds sdsnew(const char *init);
sds sdsnewlen(const void *init, size_t initlen);
sds sdsdup(const sds s);
void sdsfree(sds s);
size_t sdslen(const sds s);
size_t sdsavail(const sds s);
sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t);
sds sdscpylen(sds s, const char *t, size_t len);
sds sdscpy(sds s, const char *t);
sds sdsgrowzero(sds s, size_t len);
sds sdscatprintf(sds s, const char *fmt, ...);
void sdstrim(sds s, const char *cset);
void sdsrange(sds s, int start, int end);
int sdscmp(const sds s1, const sds s2);
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
void sdsfreesplitres(sds *tokens, int count);
void sdstolower(sds s);
void sdstoupper(sds s);
sds sdsfromlonglong(long long value);

#endif // CCACHE_SDS_H
j// src/datastore/sds.c
#include "sds.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>

/* 获取SDS头指针 */
static inline struct sdshdr8 *sdsheader8(const sds s) {
    return (struct sdshdr8 *)(s - sizeof(struct sdshdr8));
}

static inline struct sdshdr16 *sdsheader16(const sds s) {
    return (struct sdshdr16 *)(s - sizeof(struct sdshdr16));
}

static inline struct sdshdr32 *sdsheader32(const sds s) {
    return (struct sdshdr32 *)(s - sizeof(struct sdshdr32));
}

static inline struct sdshdr64 *sdsheader64(const sds s) {
    return (struct sdshdr64 *)(s - sizeof(struct sdshdr64));
}

/* 获取SDS类型 */
static inline char sdsflags(const sds s) {
    return s[-1];  // flags在buf前面一个字节
}

/* 获取SDS长度 - O(1)时间复杂度!*/
size_t sdslen(const sds s) {
    if (!s) return 0;
    unsigned char flags = sdsflags(s);
    switch(flags & SDS_TYPE_MASK) {
        case SDS_TYPE_5: return (size_t)((struct sdshdr5 *)s->buf)[-1] >> 3;
        case SDS_TYPE_8: return sdsheader8(s)->len;
        case SDS_TYPE_16: return sdsheader16(s)->len;
        case SDS_TYPE_32: return sdsheader32(s)->len;
        case SDS_TYPE_64: return sdsheader64(s)->len;
        default: return 0;
    }
}

/* 获取可用空间 */
size_t sdsavail(const sds s) {
    if (!s) return 0;
    unsigned char flags = sdsflags(s);
    switch(flags & SDS_TYPE_MASK) {
        case SDS_TYPE_5: return 0;
        case SDS_TYPE_8: return sdsheader8(s)->alloc - sdsheader8(s)->len;
        case SDS_TYPE_16: return sdsheader16(s)->alloc - sdsheader16(s)->len;
        case SDS_TYPE_32: return sdsheader32(s)->alloc - sdsheader32(s)->len;
        case SDS_TYPE_64: return sdsheader64(s)->alloc - sdsheader64(s)->len;
        default: return 0;
    }
}

/* 创建新SDS(从C字符串) */
sds sdsnew(const char *init) {
    return sdsnewlen(init, init ? strlen(init) : 0);
}

/* 创建新SDS(从二进制数据) */
sds sdsnewlen(const void *init, size_t initlen) {
    sds s;
    struct sdshdr8 *sh;
    size_t hdrlen;
    unsigned char type = sdsReqType(initlen);

    hdrlen = sdsHdrSize(type);
    s = malloc(hdrlen + initlen + 1);
    if (!s) return NULL;

    sh = (struct sdshdr8 *)s;
    s = (char *)s + hdrlen;

    switch(type) {
        case SDS_TYPE_5:
            *((uint8_t *)sh) = (uint8_t)((initlen << 3) | SDS_TYPE_5);
            break;
        case SDS_TYPE_8:
            sh->len = initlen;
            sh->alloc = initlen;
            sh->flags = type;
            break;
        // ... 其他类型类似
    }

    if (initlen && init) {
        memcpy(s, init, initlen);
    }
    s[initlen] = '\0';

    return s;
}

/* 追加字符串(核心优化:预分配空间,减少内存分配次数) */
sds sdscatlen(sds s, const void *t, size_t len) {
    size_t curlen = sdslen(s);
    s = sdsMakeRoomFor(s, len);
    if (!s) return NULL;

    memcpy(s + curlen, t, len);
    s[curlen + len] = '\0';
    sdslen(s) = curlen + len;
    return s;
}

/* 空间预分配策略(Redis经典策略) */
sds sdsMakeRoomFor(sds s, size_t addlen) {
    size_t avail = sdsavail(s);
    if (avail >= addlen) return s;

    size_t newlen = sdslen(s) + addlen;
    size_t hdrlen = sdsHdrSize(sdsflags(s));
    char *newsh;

    /* 核心策略:小于1MB时翻倍扩容,大于1MB时每次增加1MB */
    if (newlen < SDS_MAX_PREALLOC) {
        newlen *= 2;
    } else {
        newlen += SDS_MAX_PREALLOC;
    }

    unsigned char newtype = sdsReqType(newlen);
    if (newtype == SDS_TYPE_5) newtype = SDS_TYPE_8;

    size_t newhdrlen = sdsHdrSize(newtype);

    if (newtype == sdsflags(s)) {
        newsh = realloc(s - hdrlen, newhdrlen + newlen + 1);
        if (!newsh) return NULL;
        s = newsh + hdrlen;
    } else {
        // 类型变化,需要重新分配并拷贝
        newsh = malloc(newhdrlen + newlen + 1);
        if (!newsh) return NULL;
        memcpy(newsh + newhdrlen, s, curlen + 1);
        free(s - hdrlen);
        s = newsh + newhdrlen;
    }

    // 更新头部信息
    // ...
    return s;
}

3.2 高性能哈希表实现
哈希表是缓存系统的核心数据结构。C-Cache实现了渐进式哈希表,解决单次扩容导致的服务卡顿问题。

// src/datastore/dict.h
#ifndef CCACHE_DICT_H
#define CCACHE_DICT_H

#include <stdint.h>
#include "sds.h"

/* 哈希函数类型 */
typedef uint64_t (*dictHashFunction)(const void *key, size_t len);

/* 键值对 */
typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;
    struct dictEntry *next;  // 链地址法解决冲突
} dictEntry;

/* 字典类型(多态操作) */
typedef struct dictType {
    uint64_t (*hashFunction)(const void *key, size_t len);
    void *(*keyDup)(void *privdata, const void *key);
    void *(*valDup)(void *privdata, const void *obj);
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);
    void (*keyDestructor)(void *privdata, void *key);
    void (*valDestructor)(void *privdata, void *obj);
} dictType;

/* 哈希表(一个字典有两个哈希表,用于渐进式rehash) */
typedef struct dictht {
    dictEntry **table;      // 哈希桶数组
    unsigned long size;     // 哈希表大小
    unsigned long sizemask; // 用于计算索引的掩码(size-1)
    unsigned long used;     // 已使用节点数
} dictht;

/* 字典主结构 */
typedef struct dict {
    dictType *type;          // 类型特定函数
    void *privdata;          // 私有数据
    dictht ht[2];            // 两个哈希表(ht[1]用于rehash)
    int rehashidx;           // rehash进度(-1表示未进行)
    int iterators;           // 当前运行的迭代器数量
} dict;

/* 迭代器 */
typedef struct dictIterator {
    dict *d;
    int table;               // 当前遍历的哈希表索引(0或1)
    int index;               // 当前桶索引
    int safe;                // 是否安全迭代器(允许修改)
    dictEntry *entry;        // 当前节点
    dictEntry *nextEntry;    // 下一个节点
} dictIterator;

/* API声明 */
dict *dictCreate(dictType *type, void *privdata);
int dictExpand(dict *d, unsigned long size);
int dictRehash(dict *d, int n);
int dictAdd(dict *d, void *key, void *val);
dictEntry *dictFind(dict *d, const void *key);
void *dictFetchValue(dict *d, const void *key);
int dictDelete(dict *d, const void *key);
void dictRelease(dict *d);
dictIterator *dictGetIterator(dict *d);
dictEntry *dictNext(dictIterator *iter);
void dictReleaseIterator(dictIterator *iter);

#endif
j// src/datastore/dict.c - 核心实现
#include "dict.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/* 初始哈希表大小 */
#define DICT_HT_INITIAL_SIZE 4

/* 哈希表扩容因子(used >= size时触发扩容)*/
#define DICT_HT_EXPAND_FACTOR 1

/* 缩容因子(used < size * 0.1时触发缩容)*/
#define DICT_HT_SHRINK_FACTOR 10

/* 创建字典 */
dict *dictCreate(dictType *type, void *privdata) {
    dict *d = malloc(sizeof(dict));
    if (!d) return NULL;

    d->type = type;
    d->privdata = privdata;
    d->rehashidx = -1;
    d->iterators = 0;
    d->ht[0] = (dictht){ NULL, 0, 0, 0 };
    d->ht[1] = (dictht){ NULL, 0, 0, 0 };

    if (dictExpand(d, DICT_HT_INITIAL_SIZE) != 0) {
        free(d);
        return NULL;
    }

    return d;
}

/* 扩容/缩容核心函数 */
int dictExpand(dict *d, unsigned long size) {
    // 如果正在rehash,不能再次扩容
    if (d->rehashidx != -1) return -1;

    // 确保size是2的幂
    unsigned long realsize = DICT_HT_INITIAL_SIZE;
    while (realsize < size) realsize <<= 1;

    // 创建新哈希表
    dictht n = { NULL, realsize, realsize - 1, 0 };
    n.table = calloc(realsize, sizeof(dictEntry*));
    if (!n.table) return -1;

    // 如果当前哈希表为空,直接使用新表
    if (d->ht[0].table == NULL) {
        d->ht[0] = n;
        return 0;
    }

    // 否则设置ht[1]为新表,启动渐进式rehash
    d->ht[1] = n;
    d->rehashidx = 0;
    return 0;
}

/* 渐进式rehash(核心:分步迁移,避免单次卡顿)*/
int dictRehash(dict *d, int n) {
    // 空转保护
    if (d->rehashidx == -1) return 0;

    int empty_visits = n * 10;  // 最多跳过10*n个空桶
    while (n-- && d->ht[0].used != 0) {
        // 跳过空桶(最多跳过empty_visits次)
        while (d->ht[0].table[d->rehashidx] == NULL) {
            d->rehashidx++;
            if (--empty_visits == 0) return 1;
        }

        // 迁移当前桶的所有节点
        dictEntry *de = d->ht[0].table[d->rehashidx];
        while (de) {
            dictEntry *next = de->next;

            // 计算在新表中的索引
            unsigned long h = dictHashKey(d, de->key) & d->ht[1].sizemask;
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;

            d->ht[0].used--;
            d->ht[1].used++;
            de = next;
        }
        d->ht[0].table[d->rehashidx] = NULL;
        d->rehashidx++;
    }

    // 检查rehash是否完成
    if (d->ht[0].used == 0) {
        free(d->ht[0].table);
        d->ht[0] = d->ht[1];
        d->ht[1] = (dictht){ NULL, 0, 0, 0 };
        d->rehashidx = -1;
        return 0;
    }

    return 1;
}

/* 在rehash过程中查找键 */
dictEntry *dictFind(dict *d, const void *key) {
    // 如果正在rehash,执行一步rehash(每次查找都推进)
    if (d->rehashidx != -1) {
        dictRehash(d, 1);
    }

    unsigned long h = dictHashKey(d, key);
    // 先在ht[0]查找
    dictEntry *de = d->ht[0].table[h & d->ht[0].sizemask];
    while (de) {
        if (dictCompareKeys(d, key, de->key)) return de;
        de = de->next;
    }

    // 如果正在rehash,再到ht[1]查找
    if (d->rehashidx != -1) {
        de = d->ht[1].table[h & d->ht[1].sizemask];
        while (de) {
            if (dictCompareKeys(d, key, de->key)) return de;
            de = de->next;
        }
    }

    return NULL;
}

/* 添加键值对 */
int dictAdd(dict *d, void *key, void *val) {
    // 检查是否需要扩容
    if (d->ht[0].used >= d->ht[0].size && d->rehashidx == -1) {
        dictExpand(d, d->ht[0].used * 2);
    }

    // 如果正在rehash,推进
    if (d->rehashidx != -1) dictRehash(d, 1);

    // 计算哈希值
    unsigned long h = dictHashKey(d, key);
    int table = d->rehashidx != -1 ? 1 : 0;
    unsigned long idx = h & d->ht[table].sizemask;

    // 检查键是否已存在
    dictEntry *de = d->ht[table].table[idx];
    while (de) {
        if (dictCompareKeys(d, key, de->key)) return -1;
        de = de->next;
    }

    // 创建新节点
    de = malloc(sizeof(dictEntry));
    if (!de) return -1;

    de->key = dictDupKey(d, key);
    de->v.val = dictDupVal(d, val);
    de->next = d->ht[table].table[idx];
    d->ht[table].table[idx] = de;
    d->ht[table].used++;

    return 0;
}

3.3 内存分配器封装

// src/utils/alloc.h
#ifndef CCACHE_ALLOC_H
#define CCACHE_ALLOC_H

#include <stddef.h>

/* 内存分配器接口(支持jemalloc、tcmalloc或系统malloc)*/
void *zmalloc(size_t size);
void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);
void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
void zmalloc_enable_thread_safeness(void);

#endif
// src/utils/alloc.c
#include "alloc.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#ifdef USE_JEMALLOC
#include <jemalloc/jemalloc.h>
#define malloc je_malloc
#define calloc je_calloc
#define realloc je_realloc
#define free je_free
#endif

/* 内存使用统计(原子操作) */
static size_t used_memory = 0;
static pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;

/* OOM处理函数 */
static void (*oom_handler)(size_t) = NULL;

void *zmalloc(size_t size) {
    void *ptr = malloc(size);
    if (!ptr) {
        if (oom_handler) oom_handler(size);
        return NULL;
    }

    pthread_mutex_lock(&used_memory_mutex);
    used_memory += size;
    pthread_mutex_unlock(&used_memory_mutex);

    return ptr;
}

void *zcalloc(size_t size) {
    void *ptr = calloc(1, size);
    if (!ptr) {
        if (oom_handler) oom_handler(size);
        return NULL;
    }

    pthread_mutex_lock(&used_memory_mutex);
    used_memory += size;
    pthread_mutex_unlock(&used_memory_mutex);

    return ptr;
}

void *zrealloc(void *ptr, size_t size) {
    size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
    void *newptr = realloc(ptr, size);
    if (!newptr && size > 0) {
        if (oom_handler) oom_handler(size);
        return NULL;
    }

    pthread_mutex_lock(&used_memory_mutex);
    used_memory -= old_size;
    used_memory += size;
    pthread_mutex_unlock(&used_memory_mutex);

    return newptr;
}

void zfree(void *ptr) {
    if (!ptr) return;

    size_t old_size = malloc_usable_size(ptr);
    pthread_mutex_lock(&used_memory_mutex);
    used_memory -= old_size;
    pthread_mutex_unlock(&used_memory_mutex);

    free(ptr);
}

size_t zmalloc_used_memory(void) {
    size_t mem;
    pthread_mutex_lock(&used_memory_mutex);
    mem = used_memory;
    pthread_mutex_unlock(&used_memory_mutex);
    return mem;
}

来源:
https://xcfsr.cn/category/software-dev.html

相关文章
|
15小时前
|
缓存 NoSQL Redis
C语言企业项目实战(一)
教程来源 https://xcfsr.cn/ 本文以高性能内存缓存系统C-Cache为实战案例,完整呈现C语言在企业级开发中的工程化实践:涵盖需求分析、架构设计(事件驱动+多线程)、内存管理、Redis协议兼容实现、持久化与主从复制等,助你掌握百万行代码级C项目的核心能力。
|
13天前
|
JavaScript
js中数组排序的五种方式
下面主要介绍了数组排序的五种方式——sort()方法、选择排序、冒泡排序、插入排序和快速排序,
|
11天前
|
人工智能 Linux API
三句话完成公众号发文|OpenClaw 阿里云/本地部署与百炼大模型配置实战指南
OpenClaw(曾用名Clawdbot、Moltbot)是一款面向个人与轻量团队的AI自动化代理工具,可通过自然语言指令完成内容创作、图文生成、格式转换、平台发布等流程化工作。对于自媒体运营、技术博主、职场用户而言,它能将原本数小时的内容产出与发布流程压缩到十几分钟,大幅降低重复操作成本。
211 4
|
11天前
|
人工智能 Linux API
零基础阿里云部署OpenClaw全教程:轻量服务器+百炼大模型一键搭建与避坑指南
OpenClaw(社区昵称“小龙虾”,曾用名Clawdbot、Moltbot)是2026年主流的开源AI智能体执行框架,最大特点是**能听懂自然语言、完成真实任务**,可自动处理文件、执行代码、联网检索、收发消息、运行自动化流程,不再局限于单纯对话。对于零基础用户,最稳定、最简单的方式就是使用阿里云轻量应用服务器+官方预置镜像,搭配百炼平台提供的免费大模型额度,全程零代码、10分钟内即可拥有一台7×24小时在线的AI数字员工。
320 2
|
6天前
|
人工智能 JSON 监控
Claude Code 源码泄露:一份价值亿元的 AI 工程公开课
我以为顶级 AI 产品的护城河是模型。读完这 51.2 万行泄露的源码,我发现自己错了。
4398 17
|
15小时前
|
运维 安全 中间件
PHP实战:从零到一构建企业级应用(五)
教程来源:https://htnus.cn/category/tech-trends.html 本文详解PHP企业级用户管理系统实战:含操作日志中间件(自动记录请求、脱敏敏感数据)、Docker容器化部署(Nginx+PHP-FPM+Redis多服务集成)及生产级性能优化(OPcache、Redis会话、安全配置),覆盖开发到运维全链路。
|
12天前
|
Java 数据库连接 Maven
MyBatis学习知识点大全(三)
教程来源 https://app-abggx9rbr6dd.appmiaoda.com 本文系统讲解MyBatis与Spring集成(XML/Boot配置、事务管理、SqlSessionTemplate)、PageHelper分页插件(基础用法与自定义实现)、代码生成器(MBG配置与手写生成器),以及配置优化、SQL性能调优和N+1等问题解决方案,助力高效开发。
|
4天前
|
人工智能 数据挖掘 程序员
藏在Claude Code里的小惊喜!187种Loading状态词,告别单调编程等待
本文揭秘Claude Code中187种趣味又专业的Loading状态词(如Analyzing、Baking、Crunching等),覆盖编译、推理、数据处理等全场景,附中文翻译与自定义配置教程,让等待过程更愉悦、更高效!
|
10天前
|
人工智能 文字识别 开发者
阿里云Tokens有什么用?写代码、写文案、做图片都会用到Tokens,AI大模型的计量单位
阿里云Token是大模型处理文本的基本计量单位(1 Token≈0.75汉字),输入输出均按此计费。新用户注册百炼平台可享超7000万免费Token,覆盖百余款千问模型,有效期90天。实测可支持2.3万篇文章、4.7万次对话或933份百页文档处理,价值数百元,助力开发者低成本试用AI。
271 14
|
4天前
|
人工智能 数据可视化 机器人
阿里云无影云电脑部署OpenClaw全教程:飞书集成+千问Qwen3.6-Plus配置保姆级指南
OpenClaw(又称Clawdbot)是一款开源的AI智能体框架,可快速将主流大模型与各类即时通讯工具打通,实现智能对话、自动化任务、信息检索等能力,被开发者称为“AI龙虾”。阿里云无影云电脑作为轻量化、可视化的云端计算服务,无需本地硬件配置,自带Ubuntu桌面环境,完美适配OpenClaw的运行需求,尤其适合零基础新手快速部署。
278 4