sys/queue.h分析

本文详细介绍了在系统头文件sys/queue.h中的链表/队列实现,特别是尾队列(tailqueue)的使用方法,通过示意图和测试程序展示了如何初始化、插入、删除元素,以及遍历队列的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这两天有兴趣学习使用了下系统头文件sys/queue.h中的链表/队列的实现,感觉实现的很是优美,关键是以后再也不需要自己实现这些基本的数据结构了,哈哈!

我的系统环境是

正好需要使用队列,那么本篇就以其中的尾队列(tail queue)为例,结合实际的测试程序和示意图(亿图软件)来说明。

测试程序tailq.c如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <sys/queue.h>  
  
struct _Data {  
	int                 value;  
	TAILQ_ENTRY(_Data)  tailq_entry;  
};  
  
int main(int argc, const char *argv[])  
{  
	/* 1. 初始化队列 */  
#if 0  
	TAILQ_HEAD(tailq_head, _Data)   head = TAILQ_HEAD_INITIALIZER(head);  
#else  
	TAILQ_HEAD(tailq_head, _Data)   head;  
	TAILQ_INIT(&head);  
#endif  
	int i;  
	struct _Data *pdata = NULL;  
  
	/* 2. 在队列末尾插入data1 */  
	struct _Data *data1 = (struct _Data *)calloc(1, sizeof(struct _Data));  
	data1->value = 1;  
	TAILQ_INSERT_TAIL(&head, data1, tailq_entry);  
	/* 3. 在队列末尾插入data2 */  
	struct _Data *data2 = (struct _Data *)calloc(1, sizeof(struct _Data));  
	data2->value = 2;  
	TAILQ_INSERT_TAIL(&head, data2, tailq_entry);  
	/* 4. 在data1之后插入data3 */  
	struct _Data *data3 = (struct _Data *)calloc(1, sizeof(struct _Data));  
	data3->value = 3;  
	TAILQ_INSERT_AFTER(&head, data1, data3, tailq_entry);  
	/* 5. 在data2之前插入data4 */  
	struct _Data *data4 = (struct _Data *)calloc(1, sizeof(struct _Data));  
	data4->value = 4;  
	TAILQ_INSERT_BEFORE(data2, data4, tailq_entry);  
	/* 6. 在队列首部插入data5 */  
	struct _Data *data5 = (struct _Data *)calloc(1, sizeof(struct _Data));  
	data5->value = 5;  
	TAILQ_INSERT_HEAD(&head, data5, tailq_entry);  
	/* 遍历队列 */  
	TAILQ_FOREACH(pdata, &head, tailq_entry) {  
		printf("pdata->value1 = %d\n", pdata->value);       
	}  
	puts("");  
	/* 7. 删除data5 */  
	TAILQ_REMOVE(&head, data5, tailq_entry);  
	free(data5);	/* TAILQ_REMOVE宏只是从队列中删除该节点,因此还需手动free */
  
	TAILQ_FOREACH(pdata, &head, tailq_entry) {  
		printf("pdata->value1 = %d\n", pdata->value);       
	}  
	puts("");  
  
	/* 正序遍历尾队列 */  
	/* 方法一 */  
	TAILQ_FOREACH(pdata, &head, tailq_entry) {  
		printf("pdata->value1 = %d\n", pdata->value);       
	}  
	puts("");  
	/* 方法二 */  
	for (pdata = TAILQ_FIRST(&head); pdata;   
					pdata = TAILQ_NEXT(pdata, tailq_entry)) {  
		printf("pdata->value1 = %d\n", pdata->value);       
	}  
  
	puts("");  
  
	/* 逆序遍历尾队列 */  
	/* 方法一 */  
	TAILQ_FOREACH_REVERSE(pdata, &head, tailq_head, tailq_entry) {  
		printf("pdata->value1 = %d\n", pdata->value);       
	}  
	puts("");  
	/* 方法二 */  
	for (pdata = TAILQ_LAST(&head, tailq_head); pdata;   
			pdata = TAILQ_PREV(pdata, tailq_head, tailq_entry)) {  
		printf("pdata->value1 = %d\n", pdata->value);       
		TAILQ_REMOVE(&head, pdata, tailq_entry);  
		free(pdata);  
	}  
  
	if (TAILQ_EMPTY(&head)) {  
		printf("the tail queue is empty now.\n");     
	}  
  
	exit(EXIT_SUCCESS);  
}  
代码github地址: https://github.com/astrotycoon/sys-queue.h


我们首先来看一下这个尾队列的定义:


注意,其中的tqe_prev指向的不是前一个元素,而是前一个元素中的tqe_next,这样定义的一个好处就是*tqe_prev就是自身的地址,**tqe_prev就是自身。

好,现在就顺着我的测试程序来一步步看如何使用这个尾队列吧!

第一步是初始化步骤。关于初始化我们有两种方法:使用宏TAILQ_HEAD_INITIALIZER或者使用宏TAILQ_INIT,这两者都是可以的,唯一的区别是传递给宏TAILQ_INIT的是地址,而传递给宏TAILQ_HEAD_INITIALIZER的不是,这点需要引起我们的注意。


初始化后的数据结构怎样的呢? 我们看下示意图:


接下来的两个步骤(步奏2和步奏3)都是在这个队列的尾部追加元素(data1和data2),使用的是宏TAILQ_INSERT_TAIL:


那么队列的变化过程是这样的,请看示意图:



接下来的操作是在data1之前插入data3,使用的是宏TAILQ_INSERT_AFTER:


形象的示意图如下:


整理后的示意图如下:


紧接着的操作是在data2之前插入data4,使用的是宏TAILQ_INSERT_BEFORE:


形象的示意图如下:


整理后的示意图如下:


现在在队列首部插入data5,使用的是宏TAILQ_INSERT_HEAD:


形象的示意图如下:


整理后的示意图如下:


删除数据data5使用是宏TAILQ_REMOVE:


现在的队列布局如下:


好了,基本的操作就这么多,接下来我们看看提供的几个数据元素访问方法:


前三个很简单,一看就懂,我们重点分析下TAILQ_LAST和TAILQ_PREV。

TAILQ_LAST的目的是获取队列中的最后一个元素的地址,注意是地址哦!(head)->tqh_last代表的是最后一个元素中tqe_next的地址,通过强转之后,((struct headname *)((head)->tqh_last))->tqh_last实际上就是最后一个元素中的tqe_prev,而文章一开始介绍定义的时候就说过,*tqe_prev代表的是自身元素的地址,所以TAILQ_LAST最后获取的就是最后一个元素的地址,宏TAILQ_PREV的道理是一样的。

OK,测试程序接下来就是遍历整个队列,并打印出数据,可以使用提供的宏TAILQ_FOREACH,也可以使用上述的几个访问方法来遍历。


好了,其实本文没啥内容,对我个人而言就主要是想熟悉下亿图这个软件,哈哈




参考链接:

queue.h之尾队列

关于libevent与FreeBSD内核中TAILQ队列的理解

深入理解FreeBSD中的TAILQ

libevent源码分析-- queue.h中TAILQ_QUEUE的理解

queue.h(Circular queue 循环队列) 

libevent源码之TAILQ详解

《 QEMU代码中的QLIST

Libevent源码分析-----TAILQ_QUEUE队列

ny@wxsc07:~/new/dxp_test_bak - new$ make clean && make CFLAGS="-g" rm -f main.o src/generate_box_pairs.so src/info_partition.so src/output_bin.so src/get_org_info.so src/generate_box_pairs.o src/info_partition.o src/output_bin.o src/get_org_info.o dxp_test mpic++ -I ./include -I /tools/Xilinx/Vitis_HLS/2022.2/include/ -O3 -fPIC -c main.cpp -o main.o In file included from ./include/md_fix.h:5, from main.cpp:7: ./include/ppip_float.h:7: warning: "__always_inline" redefined 7 | #define __always_inline //__attribute__((always_inline)) inline | In file included from /usr/include/features.h:461, from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33, from /usr/include/stdio.h:27, from main.cpp:1: /usr/include/x86_64-linux-gnu/sys/cdefs.h:319: note: this is the location of the previous definition 319 | # define __always_inline __inline __attribute__ ((__always_inline__)) | mpic++ -I ./include -I /tools/Xilinx/Vitis_HLS/2022.2/include/ -O3 -fPIC -c src/generate_box_pairs.cpp -o src/generate_box_pairs.o In file included from ./include/md_fix.h:5, from src/generate_box_pairs.cpp:1: ./include/ppip_float.h:7: warning: "__always_inline" redefined 7 | #define __always_inline //__attribute__((always_inline)) inline | In file included from /usr/include/features.h:461, from /usr/include/x86_64-linux-gnu/c++/9/bits/os_defines.h:39, from /usr/include/x86_64-linux-gnu/c++/9/bits/c++config.h:528, from /usr/include/c++/9/bits/stl_algobase.h:59, from /usr/include/c++/9/deque:60, from /usr/include/c++/9/queue:60, from /tools/Xilinx/Vitis_HLS/2022.2/include/hls_stream.h:23, from ./include/md_fix.h:4, from src/generate_box_pairs.cpp:1: /usr/include/x86_64-linux-gnu/sys/cdefs.h:319: note: this is the location of the previous definition 31
03-26
“/**************************************************************************** * sched/pthread/pthread_create.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include <nuttx/config.h> #include <sys/types.h> #include <stdbool.h> #include <string.h> #include <pthread.h> #include <sched.h> #include <debug.h> #include <assert.h> #include <errno.h> #include <nuttx/queue.h> #include <nuttx/sched.h> #include <nuttx/arch.h> #include <nuttx/semaphore.h> #include <nuttx/kmalloc.h> #include <nuttx/pthread.h> #include "sched/sched.h" #include "group/group.h" #include "clock/clock.h" #include "pthread/pthread.h" #include "tls/tls.h" /**************************************************************************** * Public Data ****************************************************************************/ /* Default pthread attributes (see include/nuttx/pthread.h). When configured * to build separate kernel- and user-address spaces, this global is * duplicated in each address spaced
07-11
162.c:29:5: error: redeclaration of enumerator ‘TCP_LISTEN’ TCP_LISTEN, ^ In file included from 162.c:8:0: /usr/include/netinet/tcp.h:158:3: note: previous definition of ‘TCP_LISTEN’ was here TCP_LISTEN, ^ 162.c:30:5: error: redeclaration of enumerator ‘TCP_SYN_SENT’ TCP_SYN_SENT, ^ In file included from 162.c:8:0: /usr/include/netinet/tcp.h:150:3: note: previous definition of ‘TCP_SYN_SENT’ was here TCP_SYN_SENT, ^ 162.c:32:5: error: redeclaration of enumerator ‘TCP_ESTABLISHED’ TCP_ESTABLISHED, ^ In file included from 162.c:8:0: /usr/include/netinet/tcp.h:149:3: note: previous definition of ‘TCP_ESTABLISHED’ was here TCP_ESTABLISHED = 1, ^ 162.c:35:5: error: redeclaration of enumerator ‘TCP_CLOSE_WAIT’ TCP_CLOSE_WAIT, ^ In file included from 162.c:8:0: /usr/include/netinet/tcp.h:156:3: note: previous definition of ‘TCP_CLOSE_WAIT’ was here TCP_CLOSE_WAIT, ^ 162.c:36:5: error: redeclaration of enumerator ‘TCP_LAST_ACK’ TCP_LAST_ACK, ^ In file included from 162.c:8:0: /usr/include/netinet/tcp.h:157:3: note: previous definition of ‘TCP_LAST_ACK’ was here TCP_LAST_ACK, ^ 162.c:37:5: error: redeclaration of enumerator ‘TCP_CLOSING’ TCP_CLOSING, ^ In file included from 162.c:8:0: /usr/include/netinet/tcp.h:159:3: note: previous definition of ‘TCP_CLOSING’ was here TCP_CLOSING /* now a valid state */ ^ 162.c:38:5: error: redeclaration of enumerator ‘TCP_TIME_WAIT’ TCP_TIME_WAIT, ^ In file included from 162.c:8:0: /usr/include/netinet/tcp.h:154:3: note: previous definition of ‘TCP_TIME_WAIT’ was here TCP_TIME_WAIT, ^ 162.c: In function ‘cleanup_thread’: 162.c:137:9: warning: implicit declaration of function ‘TAILQ_FOREACH_SAFE’ [-Wimplicit-function-declaration] TAILQ_FOREACH_SAFE(conn, &conn_queue, entries, tmp) { ^ 162.c:137:47: error: ‘entries’ undeclared (first use in this function) TAILQ_FOREACH_SAFE(conn, &conn_queue, entries, tmp) { ^ 162.c:137:47: note: each undeclared identifier is reported only once for each function it appears in 162.c:137:61: error: expected ‘;’ before ‘{’ token TAILQ_FOREACH_SAFE(conn, &conn_queue, entries, tmp) { ^ k1@k1:~/work/CDemo$
最新发布
08-05
分析代码:#include <sys/types.h> #include <sys/fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define SERVER_PORT 12345 /* arbitrary, but client and server must agree */ #define BUF_SIZE 4096 /* block transfer size */ #define QUEUE_SIZE 10 int main(int argc, char *argv[]) { int s, b, l, fd, sa, bytes, on = 1; char buf[BUF_SIZE]; /* buffer for outgoing file */ struct sockaddr_in channel; /* hold's IP address */ /* Build address structure to bind to socket. */ memset(&channel, 0, sizeof(channel)); /* zero channel */ channel.sin_family = AF_INET; channel.sin_addr.s_addr = htonl(INADDR_ANY); channel.sin_port = htons(SERVER_PORT); /* Passive open. Wait for connection. */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* create socket */ if (s < 0) fatal("socket failed"); setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)); b = bind(s, (struct sockaddr *) &channel, sizeof(channel)); if (b < 0) fatal("bind failed"); l = listen(s, QUEUE_SIZE); /* specify queue size */ if (l < 0) fatal("listen failed"); /* Socket is now set up and bound. Wait for connection and process it. */ while (1) { sa = accept(s, 0, 0); /* block for connection request */ if (sa < 0) fatal("accept failed"); read(sa, buf, BUF_SIZE); /* read file name from socket */ /* Get and return the file. */ fd = open(buf, O_RDONLY); /* open the file to be sent back */ if (fd < 0) fatal("open failed"); while (1) { bytes = read(fd, buf, BUF_SIZE); /* read from file */ if (bytes <= 0) break; /* check for end of file */ write(sa, buf, bytes); /* write bytes to socket */ } close(fd); /* close file */ close(sa); /* close connection */ } } fatal(char *string) { printf("%s", string); exit(1); }
06-11
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值