Files
C-exp-collection/server-exp4/cache.c

312 lines
8.4 KiB
C
Raw Normal View History

2026-07-18 16:39:01 +08:00
/**
* 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);
}