作者 | 将狼才鲸 |
---|---|
创建日期 | 2023-03-08 |
Gitee源码仓库地址:C语言使用链表实现队列(Queue、FIFO)模块
Linux原生的队列KFIFO一次只能操作一个队列,操作变长元素时,判断队列里元素个数不容易,在复杂场景下使用不方便;消息队列msgbuf的实现又太复杂,所以自行实现FIFO队列(queue)。
jim@DESKTOP-SVP3BEM MINGW64 /d/1_git/many-repositories/26_队列_缓存 (develop)
$ make
gcc -o demo queue_test.c queue.c
jim@DESKTOP-SVP3BEM MINGW64 /d/1_git/many-repositories/26_队列_缓存 (develop)
$ make run
./demo
put:
0
1
2
3
4
5
6
7
get:
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
jim@DESKTOP-SVP3BEM MINGW64 /d/1_git/many-repositories/26_队列_缓存 (develop)
源码展示:
Makefile:
default:
gcc -o demo queue_test.c queue.c
run:
./demo
clean:
rm demo
#include "queue.h"
#include /* memcpy */
#include /* printf */
#define BUFFER_COUNT 8 /* 缓存总个数 */
#define BUFFER_UNIT_SIZE 32 /* 单个缓存的最大长度 */
/* 单个缓存元素 */
typedef struct _queue_buf {
queue_t queue;
char *buf;
} queue_buf_t;
static char g_buf[BUFFER_COUNT][BUFFER_UNIT_SIZE]; /* 开辟的缓存空间 */
static queue_buf_t queue_buf[BUFFER_COUNT]; /* 所有缓存节点 */
int main()
{
int i;
queue_entry_t queue_entry; /* 整个缓存队列的入口 */
queue_entry_init(&queue_entry); /* 初始化队列入口 */
/* 初始化,将所有缓存放到已释放队列中 */
for (i = 0; i < BUFFER_COUNT; i++) {
queue_init(&queue_buf[i].queue); /* 初始化每个队列元素 */
queue_buf[i].buf = g_buf[i];
queue_buf[i].queue.queue_entry = &queue_entry;
queue_free(&queue_buf[i].queue); /* 将所有缓存加入到已释放队列 */
}
/* 读取所有空闲缓存,赋值后存到已使用队列中 */
i = 0;
printf("\nput:\n");
while (1) {
queue_t *pq = queue_alloc(&queue_entry);
if (!pq)
break;
queue_buf_t *pqbuf = container_of(pq, queue_buf_t, queue);
memcpy(pqbuf->buf, &i, sizeof(int)); /* 赋值,递增序列 */
printf("%d\n", i);
i++;
queue_put(NULL, pq, QUEUE_USED);
}
/* 读取所有已使用缓存,将里面内容打印出来,然后用完后重新放回已释放队列 */
i = 0;
printf("\nget:\n");
while (1) {
queue_t *pq = queue_get(&queue_entry, QUEUE_USED);
if (!pq)
break;
queue_buf_t *pqbuf = container_of(pq, queue_buf_t, queue);
printf("%d: %d\n", i, *(int *)pqbuf->buf); /* 打印值,看是不是递增序列 */
i++;
queue_free(pq);
}
return 0;
}
/******************************************************************************
* \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 /* NULL */
#if 0//defined(TARGET_LINUX32) || defined(TARGET_LINUX64) \
|| defined(_MSC_VER) || defined(WIN32) || defined(_WIN64)
/* 如果在操作系统之中,需要上锁 */
# include
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 /* 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_ */
/*********************************** 文件尾 ***********************************/