02 C语言使用队列实现缓存模块QueueBuffer
jim@DESKTOP-SVP3BEM MINGW64 /d/1_git/many-repositories/27_队列缓存_系统缓存 (develop)
$ make
gcc -o demo queue_buffer_test.c queue_buffer_cfg.c queue_buffer.c queue.c
jim@DESKTOP-SVP3BEM MINGW64 /d/1_git/many-repositories/27_队列缓存_系统缓存 (develop)
$ ./demo.exe
got memory and write data 63 'U': UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
write data 15 'V': VVVVVVVVVVVVVVV, and send
receive and got 15 data : VVVVVVVVVVVVVVV
jim@DESKTOP-SVP3BEM MINGW64 /d/1_git/many-repositories/27_队列缓存_系统缓存 (develop)
default:
gcc -o demo queue_buffer_test.c queue_buffer_cfg.c queue_buffer.c queue.c
run:
./demo
clean:
rm demo
#include "queue_buffer_cfg.h"
#include "queue_buffer.h"
#include <stdio.h> /* printf */
#include <string.h> /* memset */
int main()
{
qbuf_t *ptmp, *psend, *prcv;
qbuf_groups_cfg();
/* 用作临时内存 */
ptmp = qbuf_alloc(QBUF_GROUP_TMPBUF);
if (!ptmp) {
printf("alloc memory error\n");
return -1;
}
memset(ptmp->addr, 'U', ptmp->size);
ptmp->addr[ptmp->size - 1] = '\0';
ptmp->len = ptmp->size;
printf("got memory and write data %d 'U': %s\n", ptmp->len - 1, ptmp->addr);
qbuf_free(ptmp);
/* 用作模块消息缓存 */
psend = qbuf_alloc(QBUF_GROUP_DATBUF);
if (!psend) {
printf("alloc memory error\n");
return -1;
}
memset(psend->addr, 'V', psend->size);
psend->addr[psend->size - 1] = '\0';
psend->len = psend->size;
printf("write data %d 'V': %s, and send\n", psend->len - 1, psend->addr);
qbuf_put(psend); /* 发送 */
prcv = qbuf_get(QBUF_GROUP_DATBUF); /* 查询并接收 */
if (!prcv) {
printf("get error\n");
return -1;
}
printf("receive and got %d data : %s\n", prcv->len - 1, prcv->addr);
return 0;
}
#ifndef _QUEUE_BUFFER_CONFIG_H_
#define _QUEUE_BUFFER_CONFIG_H_
/* 每组缓存的总个数和总大小预先确定好 */
#define DATBUF_MAX_BUF_CNT 4
#define DATBUF_MAX_BUF_LEN 16
extern char g_databuf_addr[DATBUF_MAX_BUF_CNT * DATBUF_MAX_BUF_LEN];
#define DATBUF_MAX_BUF_ADR g_databuf_addr
#define MSGBUF_MAX_BUF_CNT 8
#define MSGBUF_MAX_BUF_LEN 32
extern char g_msgbuf_addr[MSGBUF_MAX_BUF_CNT * MSGBUF_MAX_BUF_LEN];
#define MSGBUF_MAX_BUF_ADR g_msgbuf_addr
#define TMPBUF_MAX_BUF_CNT 16
#define TMPBUF_MAX_BUF_LEN 64
extern char g_tempbuf_addr[TMPBUF_MAX_BUF_CNT * TMPBUF_MAX_BUF_LEN];
#define TMPBUF_MAX_BUF_ADR g_tempbuf_addr
#define QBUF_MAX_COUNT (DATBUF_MAX_BUF_CNT + MSGBUF_MAX_BUF_CNT + TMPBUF_MAX_BUF_CNT)
/* 初始化所有缓存 */
extern void qbuf_groups_cfg(void);
#endif /* _QUEUE_BUFFER_CONFIG_H_ */
#include "queue_buffer_cfg.h"
#include "queue_buffer.h"
/* 预先开辟出缓存总空间 */
char g_databuf_addr[DATBUF_MAX_BUF_CNT * DATBUF_MAX_BUF_LEN];
char g_msgbuf_addr[MSGBUF_MAX_BUF_CNT * MSGBUF_MAX_BUF_LEN];
char g_tempbuf_addr[TMPBUF_MAX_BUF_CNT * TMPBUF_MAX_BUF_LEN];
/* 初始化所有缓存 */
void qbuf_groups_cfg(void)
{
qbuf_group_cfg_t qg;
qbuf_init();
qg.type = QBUF_TYPE_ARRAY; /* 当前是静态分配的内存 */
qg.addr = DATBUF_MAX_BUF_ADR;
qg.count = DATBUF_MAX_BUF_CNT;
qg.unitsize = DATBUF_MAX_BUF_LEN;
qbuf_group_cfg(&qg, QBUF_GROUP_DATBUF);
qg.type = QBUF_TYPE_ARRAY; /* 当前是静态分配的内存 */
qg.addr = MSGBUF_MAX_BUF_ADR;
qg.count = MSGBUF_MAX_BUF_CNT;
qg.unitsize = MSGBUF_MAX_BUF_LEN;
qbuf_group_cfg(&qg, QBUF_GROUP_MSGBUF);
qg.type = QBUF_TYPE_ARRAY; /* 当前是静态分配的内存 */
qg.addr = TMPBUF_MAX_BUF_ADR;
qg.count = TMPBUF_MAX_BUF_CNT;
qg.unitsize = TMPBUF_MAX_BUF_LEN;
qbuf_group_cfg(&qg, QBUF_GROUP_TMPBUF);
}
/******************************************************************************
* \brief 缓存队列(先进先出的系统缓存)的实现
* \details 可以用作各个模块间的数据缓冲
* \author 将狼才鲸
* \date 2023-03-08
******************************************************************************/
#ifndef _QUEUE_BUFFER_H_
#define _QUEUE_BUFFER_H_
#include "queue.h"
#include <stdint.h> /* uint32_t... */
#define QBUF_MARK 0xAABBCCDD /* 魔术字 */
#define QBUF_ISVALID(x) ((x) && (x->mark == QBUF_MARK))
/* 当前缓存携带的参数 */
#define SYS_BUF_FLAG_MARK (0x000000FF)
#define SYS_BUF_FLAG_SOF (0x00000001) /* 文件开始 */
#define SYS_BUF_FLAG_EOF (0x00000002) /* 文件结束 */
/* 顺延的值为0x00000004 0x00000008 0x00000010 0x00000020... */
/* 缓存的来源 */
enum {
QBUF_TYPE_MALLOC, /* 使用malloc为每个缓存分配空间 */
QBUF_TYPE_ARRAY /* 使用静态数组为每个缓存分配空间 */
};
/* 缓存的分类 */
enum {
QBUF_GROUP_FREE, /* 已释放的缓存 */
QBUF_GROUP_DATBUF, /* 数据缓存 */
QBUF_GROUP_MSGBUF, /* 消息缓存 */
QBUF_GROUP_TMPBUF, /* 临时缓存,用完即退回 */
QBUF_GROUP_COUNT /* 缓存分类的组别数 */
};
/* 初始化整个缓存时的参数 */
typedef struct _qbuf_group_cfg {
uint32_t type; /* QBUF_TYPE_MALLOC... */
uint32_t count; /* 该组内缓存总个数 */
uint32_t unitsize; /* 每个缓存元素的最大容量 */
char *addr; /* 整块缓存的起始地址 */
} qbuf_group_cfg_t;
/* 单个缓存 */
typedef struct _qbuf {
queue_t queue; /* 队列节点 */
uint32_t mark; /* 用魔术字标识该缓存正常 */
uint32_t flags; /* 标识该缓存的类型,如文件结尾 */
uint32_t group; /* 属于哪一组缓存(可以移动到别的组别) */
char *addr; /* 当前缓存的地址 */
uint32_t size; /* 缓存总大小 */
uint32_t offset;/* 有效数据的起始位置 */
uint32_t len; /* 有效数据的长度 */
uint32_t id; /* 缓存的序号 */
uint64_t user; /* 私有数据,做拓展用 */
} qbuf_t;
/**
* \brief 将系统中所有缓存都推到已释放队列中,等待进一步初始化
*/
extern void qbuf_init(void);
/**
* \brief 初始化一个缓存组,分配好组内所有的缓存
* \param qg: 、该组的配置参数
* \param queue_entry: 该组的类型
*/
extern int qbuf_group_cfg(qbuf_group_cfg_t *qg, uint32_t group);
/**
* \brief 从该组内获取一个缓存,并初始化缓存的参数,这个缓存在用完后要记得释放回去
*/
extern qbuf_t *qbuf_alloc(uint32_t group);
/**
* \brief 缓存用完后重新释放回该组的已释放队列中
*/
extern int qbuf_free(qbuf_t *buf);
/**
* \brief 将数据发送到该组已使用队列中
*/
extern int qbuf_put(qbuf_t *buf);
/**
* \brief 从该组已使用队列中获取一个有有效数据的缓存
*/
extern qbuf_t *qbuf_get(uint32_t group);
#endif /* _QUEUE_BUFFER_H_ */
#include "queue_buffer.h"
#include "queue_buffer_cfg.h"
#include "util_errno.h"
#include <string.h> /* memset */
static queue_entry_t g_group_queue_entry[QBUF_GROUP_COUNT]; /* 队列的入口 */
static qbuf_t g_qbuf[QBUF_MAX_COUNT + 1]; /* 所有缓存节点 */
static int g_buf_id; /* 缓存的循环序号 */
/**
* \brief 将系统中所有缓存都推到已释放队列中,等待进一步初始化
*/
void qbuf_init(void)
{
int i;
qbuf_t *buf;
queue_t *pqueue;
g_buf_id = 0;
/* 初始化所有队列入口 */
for (i = 0; i < QBUF_GROUP_COUNT; i++) {
queue_entry_init(&g_group_queue_entry[i]);
}
/* 清除所有缓存参数 */
memset(g_qbuf, 0, sizeof(g_qbuf));
/* 将所有缓存变量都推入到已释放队列中,暂不分组 */
for (i = 0; i < QBUF_MAX_COUNT; i++) {
buf = &g_qbuf[i];
buf->mark = QBUF_MARK;
pqueue = &(buf->queue);
init_list_head(&(pqueue->node));
pqueue->queue_entry = &g_group_queue_entry[QBUF_GROUP_FREE];
queue_free(pqueue);
}
}
/**
* \brief 初始化一个缓存组,分配好组内所有的缓存
* \param qg: 、该组的配置参数
* \param queue_entry: 该组的类型
*/
int qbuf_group_cfg(qbuf_group_cfg_t *qg, uint32_t queue_entry)
{
int i;
queue_entry_t *pqe; /* 当前缓存组的队列入口 */
qbuf_t *buf, *oldbuf; /* 一个缓存 */
queue_t *pqueue; /* 一个缓存的节点 */
if (!qg || queue_entry <= QBUF_GROUP_FREE
|| queue_entry >= QBUF_GROUP_COUNT ) {
return -EPERM;
}
pqe = &(g_group_queue_entry[queue_entry]); /* 要推入的缓存组 */
/* 释放前一次销毁整个缓存队列后,某些仍在被分配的队列中的缓存;
将所有之前的queue_entry组的缓存放回到已释放队列中;
调用本函数前,其实已经调用过了qbuf_init,所有的缓存都放在
已释放队列中了 */
for (i = 0; i < QBUF_MAX_COUNT; i++) {
buf = &g_qbuf[i];
pqueue = &(buf->queue);
if (pqueue->queue_entry == pqe) { /* 该缓存节点所在的队列入口 */
pqueue->queue_entry = &g_group_queue_entry[QBUF_GROUP_FREE]; /* 释放 */
queue_free(pqueue);
}
}
/* 重新初始化该组缓存的入口 */
queue_entry_destroy(pqe);
queue_entry_init(pqe);
/* 从所有已释放缓存变量中获取自己组需要的变量,然后推入到自己的组中 */
oldbuf = NULL;
for (i = 0; i< qg->count; i++) { /* 遍历该组缓存总个数 */
pqueue = queue_alloc(&g_group_queue_entry[QBUF_GROUP_FREE]);
if (!pqueue) {
return -ENOMEM;
}
/* 初始化每个缓存,赋值地址 */
buf = container_of(pqueue, qbuf_t, queue);
buf->flags = 0;
buf->offset = 0;
buf->len = 0;
buf->size = qg->unitsize;
///TODO: 当qg->type == QBUF_TYPE_MALLOC时,将缓存空间替换为在使用时malloc,释放时free
if (!oldbuf) {
buf->addr = qg->addr;
} else {
buf->addr = oldbuf->addr + qg->unitsize;
}
oldbuf = buf;
pqueue->queue_entry = pqe;
queue_free(pqueue); /* 推入到自己组中 */
}
return 0;
}
/**
* \brief 从该组内获取一个缓存,并初始化缓存的参数,这个缓存在用完后要记得释放回去
*/
qbuf_t *qbuf_alloc(uint32_t group)
{
qbuf_t *buf;
queue_t *pqueue;
queue_entry_t *pqe;
if (group <= QBUF_GROUP_FREE
|| group >= QBUF_GROUP_COUNT ) {
return NULL;
}
pqe = &(g_group_queue_entry[group]);
pqueue = queue_alloc(pqe);
if (!pqueue) {
return NULL;
}
buf = container_of(pqueue, qbuf_t, queue);
buf->group = group;
buf->flags = 0;
buf->offset = 0;
buf->len = 0;
g_buf_id++;
if (g_buf_id == 0)
g_buf_id = 1;
buf->id = g_buf_id;
return buf;
}
/**
* \brief 缓存用完后重新释放回该组的已释放队列中
*/
int qbuf_free(qbuf_t *buf)
{
int res;
if (!QBUF_ISVALID(buf))
return -EPERM;
res = queue_free(&buf->queue);
if (res == 0) {
buf->group = QBUF_GROUP_FREE;
}
return res;
}
/**
* \brief 将数据发送到该组已使用队列中
*/
int qbuf_put(qbuf_t *buf)
{
int res;
if (!QBUF_ISVALID(buf))
return -EPERM;
res = queue_put(NULL, &(buf->queue), QUEUE_USED);
return res;
}
/**
* \brief 从该组已使用队列中获取一个有有效数据的缓存
*/
qbuf_t *qbuf_get(uint32_t group)
{
qbuf_t *buf;
queue_t *pqueue;
queue_entry_t *pqe;
if (group <= QBUF_GROUP_FREE
|| group >= QBUF_GROUP_COUNT ) {
return NULL;
}
pqe = &g_group_queue_entry[group];
pqueue = queue_get(pqe, QUEUE_USED);
if (!pqueue) {
return NULL;
}
buf = container_of(pqueue, qbuf_t, queue);
return buf;
}
/******************************************************************************
* \brief 队列(FIFO、Queue、缓存)的实现
* \details 不能单独使用,不包含实际的缓存数据,需要和上层结构体结合在一起使用
* \remarks Linux原生的队列kfifo一次只能操作一个队列,操作变长元素时,判断队列里
* 元素个数不容易,在复杂场景下使用不方便;消息队列msgbuf的实现又太复杂,
* 所以在这里自行实现FIFO队列(queue)模块
* \author 将狼才鲸
* \date 2023-03-08
******************************************************************************/
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include "list.h"
#include <stddef.h> /* NULL */
#if 0//defined(TARGET_LINUX32) || defined(TARGET_LINUX64) \
|| defined(_MSC_VER) || defined(WIN32) || defined(_WIN64)
/* 如果在操作系统之中,需要上锁 */
# include <pthread.h>
typedef pthread_mutex_t queue_lock_t;
# define queue_lock_init(lock) pthread_mutex_init((lock), NULL)
# define queue_lock(lock) pthread_mutex_lock((lock))
# define queue_unlock(lock) pthread_mutex_unlock((lock))
# define queue_lock_destroy(lock) pthread_mutex_destroy((lock))
#else /* OS */
/* 如果是裸机,无需上锁 */
typedef int queue_lock_t;
# define queue_lock_init(lock)
# define queue_lock(lock)
# define queue_unlock(lock)
# define queue_lock_destroy(lock)
#endif /* OS */
enum {
QUEUE_USED, /* 已使用的队列 */
QUEUE_FREE, /* 已释放的队列 */
QUEUE_MEMBER_MAX
};
/* 一个队列的入口 */
typedef struct _queue_entry {
struct list_head lists[QUEUE_MEMBER_MAX]; /* 队列中的已使用链表和已释放链表 */
queue_lock_t lock;
} queue_entry_t;
/* 一个队列节点 */
typedef struct _queue {
struct list_head node; /* 队列里的一个缓存对应的节点 */
/* 该节点所属的队列(因为可能会同时存在多个队列,节点也可以跨队列拷贝,
所以每个队列元素自身携带队列入口) */
queue_entry_t *queue_entry;
} queue_t;
/**
* \brief 初始化队列入口
*/
extern void queue_entry_init(queue_entry_t *pqe);
/**
* \brief 销毁队列
* \param pqe: queue_entry_t * 队列入口
*/
#define queue_entry_destroy(pqe) queue_lock_destroy((pqe)->lock)
/**
* \brief 初始化一个队列中的元素
*/
extern void queue_init(queue_t *pq);
/**
* \brief 将队列元素释放到其它队列或自己队列的已使用入口或已释放入口
* \param line: QUEUE_USED or QUEUE_FREE
*/
extern int queue_put(queue_entry_t *pqe, queue_t *pq, int line);
/**
* \brief 从队列入口的已使用入口或已释放入口获取一个队列元素
* \param line: QUEUE_USED or QUEUE_FREE
*/
extern queue_t *queue_get(queue_entry_t *pqe, int line);
/**
* \brief 从队列中申请一个可用的缓存
* \param pge: queue_entry_t * 队列入口指针
* \return queue_t *: 获取的队列节点
*/
#define queue_alloc(pqe) queue_get((pqe), QUEUE_FREE)
/**
* \brief 用完后将缓存进行释放
* \param pg: queue_t * 一个缓存节点指针
* \return 错误码
*/
#define queue_free(pq) queue_put(NULL, (pq), QUEUE_FREE)
/**
* \brief 预览已使用或者已释放队列中的第一个元素
* \details 该元素预览后不会被弹出
* \param line: QUEUE_USED or QUEUE_FREE
*/
extern queue_t *queue_peek(queue_entry_t *pqe, int line);
/**
* \brief 获取已使用或者已释放队列中剩余的元素个数
* \param line: QUEUE_USED or QUEUE_FREE
*/
extern int queue_count(queue_entry_t *pqe, int line);
#endif /* _QUEUE_H_ */
#include "queue.h"
#include "util_errno.h"
/**
* \brief 初始化队列入口,这时候整个队列是空的
*/
void queue_entry_init(queue_entry_t *pqe)
{
if (!pqe)
return;
queue_lock_init(&(pqe->lock));
queue_lock(&(pqe->lock));
for (int i = 0; i < QUEUE_MEMBER_MAX; i++) {
init_list_head(&(pqe->lists[i]));
}
queue_unlock(&(pqe->lock));
}
/**
* \brief 初始化一个队列中的元素
*/
void queue_init(queue_t *pq)
{
if (!pq)
return;
init_list_head(&(pq->node));
pq->queue_entry = NULL;
}
/**
* \brief 将队列元素释放到其它队列或自己队列的已使用入口或已释放入口
* \param line: QUEUE_USED or QUEUE_FREE
*/
int queue_put(queue_entry_t *pqe, queue_t *pq, int line)
{
list_head_t *node, *head;
if (!pq || line >= QUEUE_MEMBER_MAX)
return -EPERM;
if (!pqe)
pqe = pq->queue_entry;
if (!pqe)
return -EPERM;
queue_lock(&(pqe->lock));
node = &(pq->node); /* 当前队列节点 */
head = &(pqe->lists[line]); /* 队列中要要获取或者释放的入口 */
list_del_init(node); /* 删除node原来的链表,并重新初始化node这个链表 */
list_add_tail(node, head); /* 加到链表尾,先入先出 */
queue_unlock(&(pqe->lock));
return 0;
}
/**
* \brief 从队列入口的已使用入口或已释放入口获取一个队列元素
* \param line: QUEUE_USED or QUEUE_FREE
*/
queue_t *queue_get(queue_entry_t *pqe, int line)
{
queue_t *pq;
list_head_t *node, *head;
if (!pqe || line >= QUEUE_MEMBER_MAX)
return NULL;
queue_lock(&(pqe->lock));
head = &(pqe->lists[line]); /* 队列中要要获取或者释放的入口 */
pq = NULL;
if (!list_empty_careful(head)) {
pq = list_first_entry(head, queue_t, node); /* 获取队列的第一个元素 */
node = &(pq->node);
list_del_init(node); /* 销毁该元素 */
}
queue_unlock(&(pqe->lock));
return pq;
}
/**
* \brief 预览已使用或者已释放队列中的第一个元素
* \details 该元素预览后不会被弹出
* \param line: QUEUE_USED or QUEUE_FREE
*/
queue_t *queue_peek(queue_entry_t *pqe, int line)
{
queue_t *pq;
list_head_t *head;
if (!pqe || line >= QUEUE_MEMBER_MAX)
return NULL;
queue_lock(&(pqe->lock));
head = &(pqe->lists[line]); /* 队列中要要获取或者释放的入口 */
pq = NULL;
if (!list_empty_careful(head)) {
pq = list_first_entry(head, queue_t, node); /* 获取队列的第一个元素 */
}
queue_unlock(&(pqe->lock));
return pq;
}
/**
* \brief 获取已使用或者已释放队列中剩余的元素个数
* \param line: QUEUE_USED or QUEUE_FREE
*/
int queue_count(queue_entry_t *pqe, int line)
{
int count = 0;
list_head_t *node, *n, *head;
if (!pqe || line >= QUEUE_MEMBER_MAX)
return -EPERM;
queue_lock(&(pqe->lock));
head = &(pqe->lists[line]); /* 队列中要要获取或者释放的入口 */
if (!list_empty_careful(head)) {
list_for_each_safe(node, n, head) {
count++;
}
}
queue_unlock(&(pqe->lock));
return count;
}
/******************************************************************************
* \brief 双向链表(不直接包含数据)
* \details 约定链表入口地址本身是尾(最后弹出)地址,head->next是头(最先弹出)地址;
* 链表入口地址本身是无效的节点,即使弹出所有的节点,也只弹到入口之前
* 的节点为止;
* 一个双向链表实际上就是一个最简单的FIFO,就一个先进先出功能
* \note File format: UTF-8,中文编码:UTF-8;
* 本链表不包含具体的数据,数据须在包含本链表成员的上层结构体中进行操作,
* 也就是说本链表不能单独使用,必须和上层模块联用;
* 本模块当前必须在gcc中才能编译通过,在msvc中不行,要想在Windows中使用
* 需要去除typeof关键字,并在参数中增加一个变量;
* \remarks 基于linux_6.1-rc4\scripts\kconfig\list.h,
* 该源文件是从include\linux\list.h简化而来;
* Linux kernel源码中其它可供参考的链表还有:
* linux_6.1-rc4\scripts\kconfig\list.h 最好用
* linux_6.1-rc4\tools\usb\usbip\libsrc\list.h 可用
* linux_6.1-rc4\scripts\mod\list.h 可用
* linux_6.1-rc4\tools\firewire\list.h 另一种写法
* linux_6.1-rc4\tools\include\linux\list.h 太全
* linux_6.1-rc4\include\linux\list.h 最全
* \author 中文注释:将狼才鲸
* \date 2023-03-05
******************************************************************************/
#ifndef LIST_H
#define LIST_H
#include <stddef.h> /* offsetof size_t */
/**
* \brief 双向链表结构体
*/
typedef struct list_head {
struct list_head *next, *prev;
} list_head_t;
/**
* \brief 定义一个链表节点并赋初值,只是简化写法
* \details 这个接口一般用不到,实际使用时会用init_list_head()
* \param name: 要定义的链表节点名
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
/** 为链表的实际应用留下扩展接口 */
/**
* \brief 获取一个结构体中的某个成员相对于结构体首地址的偏移量
* \details 用于操作链表上层的带有有效数据+链表成员的结构体
* \remarks 在stddef.h中已有该宏定义函数
* \param TYPE: 上层结构体名称
* \param MEMBER: 结构体中要查找偏移量的成员,一般这个成员是链表结构体指针
* \return 结构体成员相对于结构体首地址的偏移量
*/
#undef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
* \brief 从结构体中某一成员地址逆推出该结构体的首地址
* \details 用于操作链表上层的带有有效数据+链表成员的结构体
* \note typeof是Linux GNU C(GCC)中扩展的关键字,从定义的变量名逆推出该变量的
* 类型,如int a; typeof(a) b;中的第二句与int b;的效果相同;
* 在Windows等其它编译器中编译会不通过;
* 原始定义在include/linux/kernel.h中
* \param ptr: 需要逆推的上层结构体中的某个成员地址,一般是链表成员的地址
* \param type: 上层结构体的类型名,一般该类型是结构体
* \param member: 上层结构体中成员地址的名称,也就是a.b或a->b里面的这个b
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/**
* \brief 获取已嵌入链表结构体的上层结构体地址
* \param ptr: 上层结构体中的&struct list_head指针
* \param type: 嵌入了list_head成员的上层结构体类型名
* \param member: 上层结构体中list_head所属的名称
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* \brief 获取链表入口所在链表第一个元素对应的上层结构体地址
* \note 调用前必须确保该链表非空
* \param ptr: 上层结构体中的&struct list_head指针,该指针是个链表入口
* \param type: 嵌入了list_head成员的上层结构体类型名
* \param member: 上层结构体中list_head所属的名称
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* \brief 循环整个链表时的for(;x;x)语句(循环时不能删除当前链表节点)
* \param pos: 当前循环到的节点,是个临时变量
* \param head: 链表入口
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* \brief 循环整个链表时的for(;x;x)语句(循环时可以删除当前链表节点)
* \param pos: 当前循环到的节点,是个临时变量
* \param n: 作为临时变量的节点
* \param head: 链表入口
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* \brief 在上层结构体的基础上循环整个链表时的for(;x;x)语句(循环时不能删除当前链表节点)
* \details 虽然是在上层带有效数据的结构体指针上进行循环,但是实际实现时
* 是以其中的链表结构体作为依据
* \param pos: 要循环的上层结构体临时变量,该结构体中带有链表成员,
* pos值用作变量,本身不必预先赋值,但是循环时不能删除它
* \param head: 上层结构体中的list_head成员的地址
* \param member: 上层结构体中的list_head成员的名称
* \note head虽然是链表入口地址,但本身是尾(最后弹出)地址,head->next是头(最先弹出)地址;
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* \brief 在上层结构体的基础上循环整个链表时的for(;x;x)语句(支持循环时删除当前链表节点)
* \details 虽然是在上层带有效数据的结构体指针上进行循环,但是实际实现时
* 是以其中的链表结构体作为依据
* \param pos: 要循环的上层结构体临时变量,该结构体中带有链表成员,
* pos值用作变量,本身不必预先赋值,循环时可以删除它
* \param n: 上层结构体临时变量
* \param head: 上层结构体中的list_head成员的地址
* \param member: 上层结构体中的list_head成员的名称
* \note head虽然是链表入口地址,但本身是尾(最后弹出)地址,head->next是头(最先弹出)地址;
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member);\
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
/* 因为下面都是内联函数,所以无需放在.c中,放在.h中即可,编译时不会
重复编译,而是会像宏定义一样内联展开*/
/** 私有函数 */
/**
* \brief 在链表序列中插入一个链表节点(已知要插入位置的之前和之后的节点)
* \param _new: 要插入的链表节点
* \param prev: 插入点前方的链表节点
* \param next: 插入点后方的链表节点
*/
static inline void __list_add(struct list_head *_new,
struct list_head *prev,
struct list_head *next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
/**
* \brief 在链表序列中删除一个链表节点(已知要删除位置的之前和之后的节点)
* \param prev: 删除点前方的链表节点
* \param next: 删除点后方的链表节点
*/
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
/**
* \brief 在链表序列中删除一个链表入口节点
* \param entry: 要删除的链表节点
*/
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/* 接口函数 */
/**
* \brief 初始化一个链表节点
* \param list: 要初始化的链表指针
*/
static inline void init_list_head(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/**
* \brief 在链表序列中删除一个链表入口节点,并重新初始化这个链表入口
* \param entry: 要删除的链表节点
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del_entry(entry);
init_list_head(entry);
}
/**
* \brief 判断链表是否为空
* \param head: 要判断的链表指针
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* \brief 判断链表是否为空(在多核CPU上较安全)
* \param head: 要判断的链表指针
* \return 为空时返回真,不为空返回假
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
/**
* \brief 将一个链表节点插入到一条链表的头部,先入后出
* \param new: 要插入的链表节点
* \param head: 要加入的那条链表的链表入口
* (链表入口所属的那个节点是链表尾,head->next是链表头)
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* \brief 将一个链表节点插入到链表尾,先入先出
* \details 也就是更新了这条链表入口节点的前一个(入口并不是链表头)
* \param _new: 要插入的链表节点
* \param head: 要加入的那条链表的链表入口
* (链表入口所属的那个节点是链表尾,head->next是链表头)
*/
static inline void list_add_tail(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head->prev, head);
}
/* 用于让销毁的链表节点指向一个未使用地址 */
#define LIST_POISON ((void *)0x0)
/**
* \brief 将一个链表节点从它自己所属的这条链表中删除
* \param entry: 要删除的链表节点
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = (struct list_head *)LIST_POISON;
entry->prev = (struct list_head *)LIST_POISON;
}
#endif /* LIST_H */
/******************************************************************************
* \brief 错误码
* \note 基于linux_6.1-rc4\include\uapi\asm-generic\errno-base.h
* linux_6.1-rc4\tools\arch\alpha\include\uapi\asm\errno.h
* linux_6.1-rc4\include\linux\errno.h
* \author 中文注释:将狼才鲸
* \date 2023-03-04
******************************************************************************/
#ifndef _UTIL_ERRNO_H_
#define _UTIL_ERRNO_H_
/*********************************** 宏定义 ***********************************/
#define EPERM 1 /* 操作不允许 */
#define ENOENT 2 /* 没有此文件或目录 */
#define ESRCH 3 /* 进程不存在 */
#define EINTR 4 /* 系统调用被中断,Interrupted system call */
#define EIO 5 /* 读写错误,I/O error */
#define ENXIO 6 /* 没有此设备或地址 */
#define E2BIG 7 /* 参数列表太长 */
#define ENOEXEC 8 /* 执行格式错误,Exec format error */
#define EBADF 9 /* 错误的文件号,Bad file number */
#define ECHILD 10 /* 没有该子进程,No child processes */
#define ENOMEM 12 /* 内存不足,Out of memory */
#define EACCES 13 /* 没有权限 */
#define EFAULT 14 /* 非法地址,Bad address */
#define ENOTBLK 15 /* 没有块设备,Block device required */
#define EBUSY 16 /* 设备或资源忙,Device or resource busy */
#define EEXIST 17 /* 文件已存在,File exists */
#define EXDEV 18 /* 链接不在同一个设备,Cross-device link */
#define ENODEV 19 /* 无此设备,No such device */
#define ENOTDIR 20 /* 不是文件夹,Not a directory */
#define EISDIR 21 /* 是文件夹,Is a directory */
#define EINVAL 22 /* 参数非法,Invalid argument */
#define ENFILE 23 /* 文件表溢出,File table overflow */
#define EMFILE 24 /* 文件打开太多,Too many open files */
#define ENOTTY 25 /* 不是字符设备,Not a typewriter */
#define ETXTBSY 26 /* 文本文件忙,Text file busy */
#define EFBIG 27 /* 文件太大,File too large */
#define ENOSPC 28 /* 设备空间不足,No space left on device */
#define ESPIPE 29 /* 非法跳转,Illegal seek */
#define EROFS 30 /* 文件系统只读,Read-only file system */
#define EMLINK 31 /* 链接太多,Too many links */
#define EPIPE 32 /* 管道损坏,Broken pipe */
#define EDOM 33 /* 参数超出函数范围,Math argument out of domain of func */
#define ERANGE 34 /* 结果不能表示,超出范围 */
#define EAGAIN 35 /* 请重试,Try again */
#define EWOULDBLOCK EAGAIN /* 操作被阻塞,Operation would block */
#define EINPROGRESS 36 /* 操作正在处理中,Operation now in progress */
#define EALREADY 37 /* 操作已就绪,Operation already in progress */
#define ENOTSOCK 38 /* Socket operation on non-socket */
#define EDESTADDRREQ 39 /* 需要目的地址,Destination address required */
#define EMSGSIZE 40 /* 消息太长,Message too long */
#define EPROTOTYPE 41 /* 协议类型错误,Protocol wrong type for socket */
#define ENOPROTOOPT 42 /* 协议不可用,Protocol not available */
#define EPROTONOSUPPORT 43 /* 协议不支持,Protocol not supported */
#define ESOCKTNOSUPPORT 44 /* Socket类型不支持,Socket type not supported */
#define EOPNOTSUPP 45 /* 在该传输端点上操作不支持,Operation not supported on transport endpoint */
#define EPFNOSUPPORT 46 /* 协议族不支持,Protocol family not supported */
#define EAFNOSUPPORT 47 /* 地址族不支持,Address family not supported by protocol */
#define EADDRINUSE 48 /* 地址已在使用,Address already in use */
#define EADDRNOTAVAIL 49 /* 所请求的地址无法分配,Cannot assign requested address */
#define ENETDOWN 50 /* 网络已断开,Network is down */
#define ENETUNREACH 51 /* 网络无法访问,Network is unreachable */
#define ENETRESET 52 /* 因为复位导致网络连接中断,Network dropped connection because of reset */
#define ECONNABORTED 53 /* 连接中止,Software caused connection abort */
#define ECONNRESET 54 /* 连接复位,Connection reset by peer */
#define ENOBUFS 55 /* 没有可用的缓存,No buffer space available */
#define EISCONN 56 /* 传输端点已连接,Transport endpoint is already connected */
#define ENOTCONN 57 /* 传输端点未连接,Transport endpoint is not connected */
#define ESHUTDOWN 58 /* 传输端点关闭后无法发送,Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 59 /* 引用太多无法拼接,Too many references: cannot splice */
#define ETIMEDOUT 60 /* 连接超时,Connection timed out */
#define ECONNREFUSED 61 /* 连接被拒绝,Connection refused */
#define ELOOP 62 /* 符号连接冲突太多 */
#define ENAMETOOLONG 63 /* 文件名太长,File name too long */
#define EHOSTDOWN 64 /* 主机已关闭,Host is down */
#define EHOSTUNREACH 65 /* 主机无路由,No route to host */
#define ENOTEMPTY 66 /* 文件夹非空,Directory not empty */
#define EUSERS 68 /* 用户太多,Too many users */
#define EDQUOT 69 /* 超出界限,Quota exceeded */
#define ESTALE 70 /* 旧的文件句柄,Stale file handle */
#define EREMOTE 71 /* 目标已移除,Object is remote */
#define ENOLCK 77 /* 没有可用的锁,No record locks available */
#define ENOSYS 78 /* 函数未实现 */
#define ENOMSG 80 /* 没有所需类型的消息,No message of desired type */
#define EIDRM 81 /* 标识符已删除,Identifier removed */
#define ENOSR 82 /* 没有流资源,Out of streams resources */
#define ETIME 83 /* 定时器超时,Timer expired */
#define EBADMSG 84 /* 不是数据消息 */
#define EPROTO 85 /* 协议错误,Protocol error */
#define ENODATA 86 /* 没有可用的数据,No data available */
#define ENOSTR 87 /* 不是流设备,Device not a stream */
#define ENOPKG 92 /* 包未安装,Package not installed */
#define EILSEQ 116 /* 字节序列非法Illegal byte sequence */
#define ECHRNG 88 /* 通道号超出范围,Channel number out of range */
#define EL2NSYNC 89 /* 级别2未同步,Level 2 not synchronized */
#define EL3HLT 90 /* 级别3中止,Level 3 halted */
#define EL3RST 91 /* 级别3复位,Level 3 reset */
#define ELNRNG 93 /* 链接号超出范围,Link number out of range */
#define EUNATCH 94 /* 协议驱动未附加,Protocol driver not attached */
#define ENOCSI 95 /* CSI结构不可用,No CSI structure available */
#define EL2HLT 96 /* 级别2中止,Level 2 halted */
#define EBADE 97 /* 无效的交换 */
#define EBADR 98 /* 无效的请求描述符 */
#define EXFULL 99 /* 交换已满,Exchange full */
#define ENOANO 100 /* 没有正极,No anode */
#define EBADRQC 101 /* 请求码无效,Invalid request code */
#define EBADSLT 102 /* Invalid slot */
#define EDEADLK 11 /* 将发生资源死锁,Resource deadlock would occur */
#define EDEADLOCK EDEADLK /* 发生死锁 */
#define EBFONT 104 /* 字库文件格式错误,Bad font file format */
#define ENONET 105 /* 设备未联网,Machine is not on the network */
#define ENOLINK 106 /* 链路已断,Link has been severed */
#define EADV 107 /* 广播错误,Advertise error */
#define ESRMNT 108 /* 挂载错误,Srmount error */
#define ECOMM 109 /* 发送时通信错误,Communication error on send */
#define EMULTIHOP 110 /* 尝试多跳,Multihop attempted */
#define EDOTDOT 111 /* RFS specific error */
#define EOVERFLOW 112 /* 参数溢出,对于已定义的数据类型来说值太大 */
#define ENOTUNIQ 113 /* 名称在网络上不唯一,Name not unique on network */
#define EBADFD 114 /* 文件描述符处于非法状态,File descriptor in bad state */
#define EREMCHG 115 /* 远端地址已改变,Remote address changed */
#define EUCLEAN 117 /* 结构需要清理,Structure needs cleaning */
#define ENOTNAM 118 /* 不是由XENIX命名的类型文件,Not a XENIX named type file */
#define ENAVAIL 119 /* 没有可用的XENIX信号量,No XENIX semaphores available */
#define EISNAM 120 /* 是一个已命名的文件,Is a named type file */
#define EREMOTEIO 121 /* 远端读写错误,Remote I/O error */
#define ELIBACC 122 /* 无法访问所需的共享库,Can not access a needed shared library */
#define ELIBBAD 123 /* 访问的共享库已损坏,Accessing a corrupted shared library */
#define ELIBSCN 124 /* a.out中的.lib部分已损坏,.lib section in a.out corrupted */
#define ELIBMAX 125 /* 试图链接的共享库太多,Attempting to link in too many shared libraries */
#define ELIBEXEC 126 /* 不能直接运行共享库,Cannot exec a shared library directly */
#define ERESTART 127 /* 应重新启动已中断的系统调用,Interrupted system call should be restarted */
#define ESTRPIPE 128 /* 流管道错误,Streams pipe error */
#define ENOMEDIUM 129 /* 找不到介质,No medium found */
#define EMEDIUMTYPE 130 /* 错误的介质类型,Wrong medium type */
#define ECANCELED 131 /* 操作已取消,Operation Cancelled */
#define ENOKEY 132 /* 所需键值不可用,Required key not available */
#define EKEYEXPIRED 133 /* 密钥已过期,Key has expired */
#define EKEYREVOKED 134 /* 密钥已吊销,Key has been revoked */
#define EKEYREJECTED 135 /* 密钥被服务拒绝,Key was rejected by service */
#define EOWNERDEAD 136 /* 拥有者已注销,Owner died */
#define ENOTRECOVERABLE 137 /* 状态不可恢复,State not recoverable */
#define ERFKILL 138 /* Operation not possible due to RF-kill */
#define EHWPOISON 139 /* 内存页硬件错误,Memory page has hardware error */
#define ERESTARTSYS 512 /* 系统重启 */
#define ERESTARTNOINTR 513 /* 无中断的系统重启 */
#define ERESTARTNOHAND 514 /* 无处理句柄的重启,restart if no handler.. */
#define ENOIOCTLCMD 515 /* 命令不存在,No ioctl command */
#define ERESTART_RESTARTBLOCK 516 /* 系统调用的重启,restart by calling sys_restart_syscall */
#define EPROBE_DEFER 517 /* 驱动需要重新探测,Driver requests probe retry */
#define EOPENSTALE 518 /* 打开时发现了一个旧入口,open found a stale dentry */
#define ENOPARAM 519 /* 参数不支持,Parameter not supported */
#define EBADHANDLE 521 /* 非法的NFS文件句柄,Illegal NFS file handle */
#define ENOTSYNC 522 /* 更新同步不匹配,Update synchronization mismatch */
#define EBADCOOKIE 523 /* Cookie太陈旧,Cookie is stale */
#define ENOTSUPP 524 /* 操作不支持,Operation is not supported */
#define ETOOSMALL 525 /* 缓存或请求太小,Buffer or request is too small */
#define ESERVERFAULT 526 /* 无法翻译,An untranslatable error occurred */
#define EBADTYPE 527 /* 服务器不支持的类型,Type not supported by server */
#define EJUKEBOX 528 /* 请求超时,Request initiated, but will not complete before timeout */
#define EIOCBQUEUED 529 /* io回调已推入队列,将得到完成标志,iocb queued, will get completion event */
#define ERECALLCONFLICT 530 /* 回调状态冲突,conflict with recalled state */
#define ENOGRACE 531 /* 拒绝取回NFS文件锁,NFS file lock reclaim refused */
#endif /* _UTIL_ERRNO_H_ */
/*********************************** 文件尾 ***********************************/