02 C语言使用队列实现缓存模块QueueBuffer

陆沈浪
2023-12-01

02 C语言使用队列实现缓存模块QueueBuffer

作者将狼才鲸
创建日期2023-03-08

  • 运行结果:
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)

  • 源码:
  • Makefile:
default:
	gcc -o demo queue_buffer_test.c queue_buffer_cfg.c queue_buffer.c queue.c
run:
	./demo
clean:
	rm demo

  • queue_buffer_test.c:
#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;
}
  • queue_buffer_cfg.h:
#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_ */

  • queue_buffer_cfg.c:
#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);
}

  • queue_buffer.h:
/******************************************************************************
 * \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_ */

  • queue_buffer.c:
#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;
}

  • queue.h:
/******************************************************************************
 * \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_ */

  • queue.c:
#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;
}

  • list.h:
/******************************************************************************
 * \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 */

  • util_errno.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_ */

/*********************************** 文件尾 ***********************************/

 类似资料: