0000
This commit is contained in:
311
server-exp4/cache.c
Normal file
311
server-exp4/cache.c
Normal file
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* cache.c — LRU 缓存实现
|
||||
*
|
||||
* 实现细节:
|
||||
* 1. 使用 DJB2 哈希算法将 URL 映射到桶索引
|
||||
* 2. 双向链表 (head↔...↔tail) 维护访问顺序:
|
||||
* - head 方向是最近使用 (MRU)
|
||||
* - tail 方向是最久未使用 (LRU)
|
||||
* 3. 查找命中时,将节点移动到链表头部
|
||||
* 4. 插入满时,淘汰链表尾部的 LRU 节点
|
||||
* 5. 线程安全: 所有操作使用 pthread_mutex 保护
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "cache.h"
|
||||
|
||||
/* ======================== 内部常量 ======================== */
|
||||
|
||||
#define DEFAULT_NUM_BUCKETS 101 /* 哈希桶数量 (质数,减少冲突) */
|
||||
#define MAX_CACHE_SIZE 1024 /* 默认最大条目数 */
|
||||
|
||||
/* ======================== 内部辅助函数 ======================== */
|
||||
|
||||
/**
|
||||
* hash_url — DJB2 哈希算法
|
||||
* 该算法简单高效,分布均匀,适合字符串键
|
||||
*/
|
||||
static unsigned long hash_url(const char *url)
|
||||
{
|
||||
unsigned long hash = 5381;
|
||||
int c;
|
||||
while ((c = *url++))
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_remove — 从双向链表中移除节点 (不释放内存)
|
||||
*/
|
||||
static void list_remove(cache_t *cache, cache_entry_t *entry)
|
||||
{
|
||||
if (entry->prev)
|
||||
entry->prev->next = entry->next;
|
||||
else
|
||||
cache->head = entry->next; /* 移除的是头节点 */
|
||||
|
||||
if (entry->next)
|
||||
entry->next->prev = entry->prev;
|
||||
else
|
||||
cache->tail = entry->prev; /* 移除的是尾节点 */
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add_head — 将节点添加到链表头部 (MRU 位置)
|
||||
*/
|
||||
static void list_add_head(cache_t *cache, cache_entry_t *entry)
|
||||
{
|
||||
entry->prev = NULL;
|
||||
entry->next = cache->head;
|
||||
if (cache->head)
|
||||
cache->head->prev = entry;
|
||||
else
|
||||
cache->tail = entry; /* 链表为空,也是尾节点 */
|
||||
cache->head = entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* evict_lru — 淘汰 LRU 节点 (链表尾部)
|
||||
* 从哈希表和链表中移除,释放内存
|
||||
*/
|
||||
static void evict_lru(cache_t *cache)
|
||||
{
|
||||
if (cache->tail == NULL)
|
||||
return;
|
||||
|
||||
cache_entry_t *victim = cache->tail;
|
||||
|
||||
/* 从链表中移除 */
|
||||
list_remove(cache, victim);
|
||||
|
||||
/* 从哈希桶中移除 */
|
||||
unsigned long h = hash_url(victim->url) % cache->num_buckets;
|
||||
cache_entry_t **pp = &cache->buckets[h];
|
||||
while (*pp) {
|
||||
if (*pp == victim) {
|
||||
*pp = victim->hnext;
|
||||
break;
|
||||
}
|
||||
pp = &(*pp)->hnext;
|
||||
}
|
||||
|
||||
/* 释放内存 */
|
||||
free(victim->url);
|
||||
free(victim->data);
|
||||
free(victim);
|
||||
|
||||
cache->cur_size--;
|
||||
cache->evictions++;
|
||||
}
|
||||
|
||||
/* ======================== 公开 API 实现 ======================== */
|
||||
|
||||
/**
|
||||
* cache_init — 初始化 LRU 缓存
|
||||
*/
|
||||
cache_t *cache_init(int max_size)
|
||||
{
|
||||
cache_t *cache = (cache_t *)calloc(1, sizeof(cache_t));
|
||||
if (cache == NULL) {
|
||||
perror("cache_init: calloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cache->num_buckets = DEFAULT_NUM_BUCKETS;
|
||||
cache->max_size = (max_size > 0 && max_size <= MAX_CACHE_SIZE)
|
||||
? max_size : MAX_CACHE_SIZE;
|
||||
cache->cur_size = 0;
|
||||
cache->head = NULL;
|
||||
cache->tail = NULL;
|
||||
cache->hits = 0;
|
||||
cache->misses = 0;
|
||||
cache->evictions = 0;
|
||||
cache->bytes_served = 0;
|
||||
|
||||
cache->buckets = (cache_entry_t **)calloc(cache->num_buckets,
|
||||
sizeof(cache_entry_t *));
|
||||
if (cache->buckets == NULL) {
|
||||
perror("cache_init: calloc buckets failed");
|
||||
free(cache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&cache->lock, NULL) != 0) {
|
||||
perror("cache_init: mutex_init failed");
|
||||
free(cache->buckets);
|
||||
free(cache);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("[Cache] 初始化完成: 最大条目=%d, 桶数量=%d\n",
|
||||
cache->max_size, cache->num_buckets);
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* cache_lookup — 在缓存中查找 URL (O(1) 平均)
|
||||
*
|
||||
* 流程:
|
||||
* 1. 计算哈希值,定位桶
|
||||
* 2. 遍历桶内冲突链查找匹配 URL
|
||||
* 3. 命中: 将条目移到链表头部 (标记为最近使用),拷贝数据
|
||||
* 4. 未命中: 返回 0
|
||||
*/
|
||||
int cache_lookup(cache_t *cache, const char *url,
|
||||
char **data, long *size, char *filetype)
|
||||
{
|
||||
if (cache == NULL || url == NULL)
|
||||
return 0;
|
||||
|
||||
pthread_mutex_lock(&cache->lock);
|
||||
|
||||
unsigned long h = hash_url(url) % cache->num_buckets;
|
||||
cache_entry_t *entry = cache->buckets[h];
|
||||
|
||||
while (entry) {
|
||||
if (strcmp(entry->url, url) == 0) {
|
||||
/* 缓存命中: 移动到链表头部 */
|
||||
list_remove(cache, entry);
|
||||
list_add_head(cache, entry);
|
||||
|
||||
/* 更新访问时间 */
|
||||
clock_gettime(CLOCK_MONOTONIC, &entry->timestamp);
|
||||
|
||||
/* 拷贝数据给调用者 */
|
||||
*size = entry->size;
|
||||
strcpy(filetype, entry->filetype);
|
||||
*data = (char *)malloc(entry->size);
|
||||
if (*data)
|
||||
memcpy(*data, entry->data, entry->size);
|
||||
|
||||
cache->hits++;
|
||||
cache->bytes_served += entry->size;
|
||||
|
||||
pthread_mutex_unlock(&cache->lock);
|
||||
return 1;
|
||||
}
|
||||
entry = entry->hnext;
|
||||
}
|
||||
|
||||
/* 缓存未命中 */
|
||||
cache->misses++;
|
||||
pthread_mutex_unlock(&cache->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cache_insert — 向缓存插入新条目
|
||||
*
|
||||
* 流程:
|
||||
* 1. 若缓存已满,先淘汰 LRU 条目
|
||||
* 2. 创建新条目,拷贝数据
|
||||
* 3. 添加到哈希表和链表头部
|
||||
*/
|
||||
int cache_insert(cache_t *cache, const char *url,
|
||||
const char *data, long size, const char *filetype)
|
||||
{
|
||||
if (cache == NULL || url == NULL || data == NULL || size <= 0)
|
||||
return -1;
|
||||
|
||||
pthread_mutex_lock(&cache->lock);
|
||||
|
||||
/* 检查是否已存在 (更新) */
|
||||
unsigned long h = hash_url(url) % cache->num_buckets;
|
||||
cache_entry_t *entry = cache->buckets[h];
|
||||
while (entry) {
|
||||
if (strcmp(entry->url, url) == 0) {
|
||||
/* 已存在: 更新数据和位置 */
|
||||
free(entry->data);
|
||||
entry->data = (char *)malloc(size);
|
||||
if (entry->data == NULL) {
|
||||
pthread_mutex_unlock(&cache->lock);
|
||||
return -1;
|
||||
}
|
||||
memcpy(entry->data, data, size);
|
||||
entry->size = size;
|
||||
strcpy(entry->filetype, filetype);
|
||||
clock_gettime(CLOCK_MONOTONIC, &entry->timestamp);
|
||||
|
||||
/* 移动到头部 */
|
||||
list_remove(cache, entry);
|
||||
list_add_head(cache, entry);
|
||||
pthread_mutex_unlock(&cache->lock);
|
||||
return 0;
|
||||
}
|
||||
entry = entry->hnext;
|
||||
}
|
||||
|
||||
/* 缓存已满,淘汰 LRU */
|
||||
while (cache->cur_size >= cache->max_size)
|
||||
evict_lru(cache);
|
||||
|
||||
/* 创建新条目 */
|
||||
cache_entry_t *new_entry = (cache_entry_t *)calloc(1, sizeof(cache_entry_t));
|
||||
if (new_entry == NULL) {
|
||||
pthread_mutex_unlock(&cache->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
new_entry->url = strdup(url);
|
||||
new_entry->data = (char *)malloc(size);
|
||||
if (new_entry->url == NULL || new_entry->data == NULL) {
|
||||
free(new_entry->url);
|
||||
free(new_entry->data);
|
||||
free(new_entry);
|
||||
pthread_mutex_unlock(&cache->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(new_entry->data, data, size);
|
||||
new_entry->size = size;
|
||||
strcpy(new_entry->filetype, filetype);
|
||||
clock_gettime(CLOCK_MONOTONIC, &new_entry->timestamp);
|
||||
|
||||
/* 添加到哈希桶 */
|
||||
new_entry->hnext = cache->buckets[h];
|
||||
cache->buckets[h] = new_entry;
|
||||
|
||||
/* 添加到链表头部 */
|
||||
list_add_head(cache, new_entry);
|
||||
cache->cur_size++;
|
||||
|
||||
pthread_mutex_unlock(&cache->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cache_free — 释放所有缓存资源
|
||||
*/
|
||||
void cache_free(cache_t *cache)
|
||||
{
|
||||
if (cache == NULL) return;
|
||||
|
||||
pthread_mutex_lock(&cache->lock);
|
||||
|
||||
/* 遍历链表释放所有条目 */
|
||||
cache_entry_t *entry = cache->head;
|
||||
while (entry) {
|
||||
cache_entry_t *next = entry->next;
|
||||
free(entry->url);
|
||||
free(entry->data);
|
||||
free(entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
free(cache->buckets);
|
||||
pthread_mutex_unlock(&cache->lock);
|
||||
pthread_mutex_destroy(&cache->lock);
|
||||
|
||||
printf("[Cache] 释放完成: 命中=%lld 未命中=%lld 淘汰=%lld "
|
||||
"命中率=%.1f%% 服务字节=%lld\n",
|
||||
cache->hits, cache->misses, cache->evictions,
|
||||
cache->hits + cache->misses > 0
|
||||
? 100.0 * cache->hits / (cache->hits + cache->misses)
|
||||
: 0.0,
|
||||
cache->bytes_served);
|
||||
|
||||
free(cache);
|
||||
}
|
||||
Reference in New Issue
Block a user