Linux C语言:用零拷贝技术实现TCP代理(源代码+测试服务端客户端代码)

  

目录

test-server-client

Makefile

eproxy-original.c

eproxy.c

list.h


 

https://github.com/Rtoax/test/tree/master/zero-copy/github/eproxy


test-server-client

点击查看服务端客户端代码

 

Makefile

CFLAGS := -g -Wall -O2

all:
	gcc eproxy.c -o eproxy.out ${CFLAGS} -DDEBUG

clean:
	rm -f eproxy.out

 

eproxy-original.c

/* 
 * Copyright (c) 2013, Intel Corporation
 * Author: Andi Kleen
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * Simple port forwarder
 * proxy inport outip outport
 * Uses pipes to splice two sockets together. This should give something
 * approaching zero copy, if the NIC driver is capable. 
 * This method is rather file descriptor intensive (4 fds/conn), so make sure you 
 * have enough. 
 * Written 2012 by Andi Kleen
 */
#define _GNU_SOURCE 1
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdbool.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include "list.h"

#define err(x) perror(x), exit(1)
#define NEW(x) ((x) = xmalloc(sizeof(*(x))))
#define MAX(a,b) ((a) > (b) ? (a) : (b))

int connection_timeout = 5; /* XXX configurable */

void oom(void)
{
	fprintf(stderr, "Out of memory\n");
	exit(1);
}

void *xmalloc(size_t size)
{
	void *p = calloc(size, 1);
	if (!p)
		oom();
	return p;
}

void *xrealloc(void *old, size_t size)
{
	void *p = realloc(old, size);
	if (!p)
		oom();
	return p;
}

struct addrinfo *resolve(char *name, char *port, int flags)
{
	int ret;
	struct addrinfo *adr;
	struct addrinfo hint = { .ai_flags = flags };

	ret = getaddrinfo(name, port, &hint, &adr);
		if (ret) {
		fprintf(stderr, "proxy: Cannot resolve %s %s: %s\n",
			name, port, gai_strerror(ret));
		exit(1);
	}
	return adr;
}

void setnonblock(int fd, int *cache)
{
	int flags;
	if (!cache || *cache == -1) {
	 	flags = fcntl(fd, F_GETFL, 0);
		if (cache)
			*cache = flags;
	} else
		flags = *cache;
	fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}

struct buffer {
	int pipe[2];
	int bytes;
};

struct conn {
	struct conn *other;
	int fd;
	struct buffer buf;
	time_t expire;
	struct list_head expire_node;
};

LIST_HEAD(expire_list);

#define MIN_EVENTS 32
struct epoll_event *events;
int num_events, max_events;

int epoll_add(int efd, int fd, int revents, void *conn)
{
	struct epoll_event ev = { .events = revents, .data.ptr = conn };
	if (++num_events >= max_events) {
		max_events = MAX(max_events * 2, MIN_EVENTS);
		events = xrealloc(events, 
				  sizeof(struct epoll_event) * max_events);
	}
	return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
}

int epoll_del(int efd, int fd)
{
	num_events--;
	assert(num_events >= 0);
	return epoll_ctl(efd, EPOLL_CTL_DEL, fd, (void *)1L);
}

/* Create buffer between two connections */
struct buffer *newbuffer(struct buffer *buf)
{
	if (pipe2(buf->pipe, O_NONBLOCK) < 0) {
		perror("pipe");
		return NULL;
	}
	return buf;
}

void delbuffer(struct buffer *buf)
{
	close(buf->pipe[0]);
	close(buf->pipe[1]);
}

void delconn(int efd, struct conn *conn)
{
	list_del(&conn->expire_node);
	delbuffer(&conn->buf);
	epoll_del(efd, conn->fd);
	close(conn->fd);
	free(conn);
}

struct conn *newconn(int efd, int fd, time_t now)
{
	struct conn *conn;
	NEW(conn);
	conn->fd = fd;
	if (!newbuffer(&conn->buf)) {
		delconn(efd, conn);
		return NULL;
	}
	if (epoll_add(efd, fd, EPOLLIN|EPOLLOUT|EPOLLET, conn) < 0) {
		perror("epoll");
		delconn(efd, conn);
		return NULL;
	}
	conn->expire = now + connection_timeout;
	list_add_tail(&conn->expire_node, &expire_list);
	return conn;
}

/* Process incoming connection. */
void new_request(int efd, int lfd, int *cache, time_t now)
{
	int newsk = accept(lfd, NULL, NULL);
	if (newsk < 0) {
		perror("accept");
		return;
	}
	// xxx log
	setnonblock(newsk, cache);
	newconn(efd, newsk, now);
}

/* Open outgoing connection */
struct conn *
openconn(int efd, struct addrinfo *host, int *cache, struct conn *other,
	 time_t now)
{
	int outfd = socket(host->ai_family, SOCK_STREAM, 0);
	if (outfd < 0)
		return NULL;
	setnonblock(outfd, cache);
	int n = connect(outfd, host->ai_addr, host->ai_addrlen);
	if (n < 0 && errno != EINPROGRESS) {
		perror("connect");
		close(outfd);
		return NULL;
	}
	struct conn *conn = newconn(efd, outfd, now);
	if (conn) {
		conn->other = other;
		other->other = conn;
	}
	return conn;
}

#define BUFSZ 16384 /* XXX */

/* Move from socket to pipe */
bool move_data_in(int srcfd, struct buffer *buf)
{
	for (;;) {	
		int n = splice(srcfd, NULL, buf->pipe[1], NULL, 
			   	BUFSZ, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
		if (n > 0)
			buf->bytes += n;
		if (n == 0)
			return false;
		if (n < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK)
				return true;
			return false;
		}
			
	}
	return true;
}

/* From pipe to socket */
bool move_data_out(struct buffer *buf, int dstfd)
{ 
	while (buf->bytes > 0) {
		int bytes = buf->bytes;
		if (bytes > BUFSZ)
			bytes = BUFSZ;
		int n = splice(buf->pipe[0], NULL, dstfd, NULL,
		 	 	 bytes, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
		if (n == 0)
			break;
		if (n < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK)
				break;
			return false;
		}
		buf->bytes -= n;
	}
	/* bytes > 0, add dst to epoll set */
	/* else remove if it was added */
	return true;
}

void closeconn(int efd, struct conn *conn)
{
	if (conn->other)
		delconn(efd, conn->other);
	delconn(efd, conn);		
}

int expire_connections(int efd, time_t now)
{
	struct conn *conn, *tmp;

	list_for_each_entry_safe (conn, tmp, &expire_list, expire_node) {
		if (conn->expire > now)
			return (conn->expire - now) * 1000;
		closeconn(efd, conn);
	}
	return -1;
}

void touch_conn(struct conn *conn, time_t now)
{
	conn->expire = now + connection_timeout;
	list_del(&conn->expire_node);
	list_add_tail(&conn->expire_node, &expire_list);
}

int listen_socket(int efd, char *lname, char *port)
{
	struct addrinfo *laddr = resolve(lname, port, AI_PASSIVE);
	
	int lfd = socket(laddr->ai_family, SOCK_STREAM, 0);
	if (lfd < 0) 
		err("socket");
	int opt = 1;
	if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0) 
		err("SO_REUSEADDR");
	if (bind(lfd, laddr->ai_addr, laddr->ai_addrlen) < 0) 
		err("bind");
	if (listen(lfd, 20) < 0) 
		err("listen");
	setnonblock(lfd, NULL);
	freeaddrinfo(laddr);

	if (epoll_add(efd, lfd, EPOLLIN, NULL) < 0) 
		err("epoll add listen fd");

	return lfd;
}

int main(int ac, char **av)
{
	if (ac != 4 && ac != 5) {
		fprintf(stderr,
			 "Usage: proxy inport outhost outport [listenaddr]\n");
		exit(1);
	}

	struct addrinfo *outhost = resolve(av[2], av[3], 0);

	int efd = epoll_create(10);
	if (efd < 0) 
		err("epoll_create");

	int lfd = listen_socket(efd, av[4] ? av[4] : "0.0.0.0", av[1]);

	int cache_in = -1, cache_out = -1;	
	int timeo = -1;

	for (;;) {
		int nfds = epoll_wait(efd, events, num_events, timeo);
		if (nfds < 0) {
			perror("epoll");
			continue;
		}
		time_t now = time(NULL);

		int i;
		for (i = 0; i < nfds; i++) { 
			struct epoll_event *ev = &events[i];
			struct conn *conn = ev->data.ptr;

			/* listen socket */
			if (conn == NULL) {
				if (ev->events & EPOLLIN)
					new_request(efd, lfd, &cache_in, now);
				continue;
			} 

			if (ev->events & (EPOLLERR|EPOLLHUP)) {
				closeconn(efd, conn);
				continue;
			}

			struct conn *other = conn->other;

			/* No attempt for partial close right now */
			if (ev->events & EPOLLIN) {
				touch_conn(conn, now);
				if (!other)
					other = openconn(efd, outhost, &cache_out,
							 conn, now);
				bool in = move_data_in(conn->fd, &conn->buf);
				bool out = move_data_out(&conn->buf, other->fd);
				if (!in || !out) { 
					closeconn(efd, conn);
					continue;
				}
				touch_conn(other, now);
			}	
				
			if ((ev->events & EPOLLOUT) && other) {
				if (!move_data_out(&other->buf, conn->fd))
					delconn(efd, conn);
				else
					touch_conn(conn, now);

				/* When the pipe filled up could have
 				   lost input events.  Unfortunately
 				   splice doesn't tell us which end
 				   was responsible for 0, so have to ask
				   explicitely. */
				int len = 0;
				if (ioctl(other->fd, FIONREAD, &len) < 0)
					perror("ioctl");
				if (len > 0) {
					if (!move_data_in(other->fd, 
							  &other->buf))
						closeconn(efd, other);
				}
			}
		}	

		timeo = expire_connections(efd, now);
	}
	return 0;
}

eproxy.c

/* 
 * Copyright (c) 2013, Intel Corporation
 * Author: Andi Kleen
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * Simple port forwarder
 * proxy inport outip outport
 * Uses pipes to splice two sockets together. This should give something
 * approaching zero copy, if the NIC driver is capable. 
 * This method is rather file descriptor intensive (4 fds/eproxy_conn), so make sure you 
 * have enough. 
 * Written 2012 by Andi Kleen
 */
#define _GNU_SOURCE 1
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdbool.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include "list.h"

#define err(x) perror(x), exit(1)
#define NEW(x) ((x) = eproxy_xmalloc(sizeof(*(x))))
#define MAX(a,b) ((a) > (b) ? (a) : (b))

#ifdef DEBUG
#define eproxy_debug(fmt...) do{\
    fprintf(stderr,"[%s:%d]:", __func__, __LINE__);\
    fprintf(stderr,fmt);\
}while(0)
#else
#define eproxy_debug(fmt...)
#endif

struct eproxy_buffer {
	int pipe[2];
	int bytes;
};

struct eproxy_conn {
	struct eproxy_conn *other;
	int fd;
	struct eproxy_buffer buf;
	time_t expire;
	struct list_head expire_node;
};

LIST_HEAD(expire_list);

#define eproxy_MIN_EVENTS 32
struct epoll_event *eproxy_events;
int eproxy_num_events, eproxy_max_events;


int connection_timeout = 5; /* XXX configurable */

void eproxy_oom(void)
{
	fprintf(stderr, "Out of memory\n");
	exit(1);
}

void *eproxy_xmalloc(size_t size)
{
	void *p = calloc(size, 1);
	if (!p)
		eproxy_oom();
	return p;
}

void *eproxy_xrealloc(void *old, size_t size)
{
	void *p = realloc(old, size);
	if (!p)
		eproxy_oom();
	return p;
}

struct addrinfo *eproxy_resolve(char *name, char *port, int flags)
{
    eproxy_debug("\n");
	int ret;
	struct addrinfo *adr;
	struct addrinfo hint = { .ai_flags = flags };

	ret = getaddrinfo(name, port, &hint, &adr);
	if (ret) {
		fprintf(stderr, "proxy: Cannot eproxy_resolve %s %s: %s\n",
			name, port, gai_strerror(ret));
		exit(1);
	}
	return adr;
}

void eproxy_setnonblock(int fd, int *cache)
{
    eproxy_debug("\n");
	int flags;
	if (!cache || *cache == -1) {
	 	flags = fcntl(fd, F_GETFL, 0);
		if (cache)
			*cache = flags;
	} else
		flags = *cache;
	fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}


int epoll_add(int efd, int fd, int revents, void *eproxy_conn)
{
    eproxy_debug("\n");
	struct epoll_event ev = { .events = revents, .data.ptr = eproxy_conn };
	if (++eproxy_num_events >= eproxy_max_events) {
		eproxy_max_events = MAX(eproxy_max_events * 2, eproxy_MIN_EVENTS);
		eproxy_events = eproxy_xrealloc(eproxy_events, 
				  sizeof(struct epoll_event) * eproxy_max_events);
	}
	return epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
}

int epoll_del(int efd, int fd)
{
    eproxy_debug("\n");
	eproxy_num_events--;
	assert(eproxy_num_events >= 0);
	return epoll_ctl(efd, EPOLL_CTL_DEL, fd, (void *)1L);
}

/* Create eproxy_buffer between two connections */
struct eproxy_buffer *eproxy_newbuffer(struct eproxy_buffer *buf)
{
    eproxy_debug("\n");
	if (pipe2(buf->pipe, O_NONBLOCK) < 0) {
		perror("pipe");
		return NULL;
	}
	return buf;
}

void eproxy_delbuffer(struct eproxy_buffer *buf)
{
    eproxy_debug("\n");
	close(buf->pipe[0]);
	close(buf->pipe[1]);
}

void eproxy_delconn(int efd, struct eproxy_conn *conn)
{
    eproxy_debug("%p:%p\n", &conn->expire_node, &expire_list);
//    if(!list_empty(&expire_list))
    list_del(&conn->expire_node);
    eproxy_debug("\n");
	eproxy_delbuffer(&conn->buf);
    eproxy_debug("\n");
	epoll_del(efd, conn->fd);
	close(conn->fd);
	free(conn);
}

struct eproxy_conn *eproxy_newconn(int efd, int fd, time_t now)
{
    eproxy_debug("\n");
	struct eproxy_conn *conn;
	NEW(conn);
	conn->fd = fd;
	if (!eproxy_newbuffer(&conn->buf)) {
		eproxy_delconn(efd, conn);
		return NULL;
	}
	if (epoll_add(efd, fd, EPOLLIN|EPOLLOUT|EPOLLET, conn) < 0) {
		perror("epoll");
		eproxy_delconn(efd, conn);
		return NULL;
	}
	conn->expire = now + connection_timeout;
	list_add_tail(&conn->expire_node, &expire_list);
	return conn;
}

/* Process incoming connection. */
void eproxy_new_request(int efd, int lfd, int *cache, time_t now)
{
    eproxy_debug("\n");
	int newsk = accept(lfd, NULL, NULL);
	if (newsk < 0) {
		perror("accept");
		return;
	}
	// xxx log
	eproxy_setnonblock(newsk, cache);
	eproxy_newconn(efd, newsk, now);
}

/* Open outgoing connection */
struct eproxy_conn *
eproxy_openconn(int efd, struct addrinfo *host, int *cache, struct eproxy_conn *other,
	 time_t now)
{
    eproxy_debug("\n");
	int outfd = socket(host->ai_family, SOCK_STREAM, 0);
	if (outfd < 0)
		return NULL;
	eproxy_setnonblock(outfd, cache);
	int n = connect(outfd, host->ai_addr, host->ai_addrlen);
	if (n < 0 && errno != EINPROGRESS) {
		perror("connect");
		close(outfd);
		return NULL;
	}
	struct eproxy_conn *conn = eproxy_newconn(efd, outfd, now);
	if (conn) {
		conn->other = other;
		other->other = conn;
	}
	return conn;
}

#define eproxy_BUFSZ 16384 /* XXX */

/* Move from socket to pipe */
bool eproxy_move_data_in(int srcfd, struct eproxy_buffer *buf)
{
    eproxy_debug("\n");
	for (;;) {	
		int n = splice(srcfd, NULL, buf->pipe[1], NULL, 
			   	eproxy_BUFSZ, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
		if (n > 0)
			buf->bytes += n;
		if (n == 0)
			return false;
		if (n < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK)
				return true;
			return false;
		}
			
	}
	return true;
}

/* From pipe to socket */
bool eproxy_move_data_out(struct eproxy_buffer *buf, int dstfd)
{ 
    eproxy_debug("\n");
	while (buf->bytes > 0) {
		int bytes = buf->bytes;
		if (bytes > eproxy_BUFSZ)
			bytes = eproxy_BUFSZ;
		int n = splice(buf->pipe[0], NULL, dstfd, NULL,
		 	 	 bytes, SPLICE_F_NONBLOCK|SPLICE_F_MOVE);
		if (n == 0)
			break;
		if (n < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK)
				break;
			return false;
		}
		buf->bytes -= n;
	}
	/* bytes > 0, add dst to epoll set */
	/* else remove if it was added */
	return true;
}

void eproxy_closeconn(int efd, struct eproxy_conn *conn)
{
    eproxy_debug("\n");
//	if (conn->other){
//        eproxy_debug("Del other CONN\n");
//		eproxy_delconn(efd, conn->other);
//
//    }
    eproxy_debug("Del CONN\n");
	eproxy_delconn(efd, conn);
}

int eproxy_expire_connections(int efd, time_t now)
{
    eproxy_debug("\n");
	struct eproxy_conn *conn, *tmp;

//    if(!list_empty(&expire_list))
	list_for_each_entry_safe (conn, tmp, &expire_list, expire_node) {
		if (conn->expire > now)
			return (conn->expire - now) * 1000;
		eproxy_closeconn(efd, conn);
        conn = NULL;
	}
	return -1;
}

void eproxy_touch_conn(struct eproxy_conn *conn, time_t now)
{
    eproxy_debug("\n");
	conn->expire = now + connection_timeout;
	list_del(&conn->expire_node);
	list_add_tail(&conn->expire_node, &expire_list);
}

int eproxy_listen_socket(int efd, char *lname, char *port)
{
    eproxy_debug("\n");
	struct addrinfo *laddr = eproxy_resolve(lname, port, AI_PASSIVE);
	
	int lfd = socket(laddr->ai_family, SOCK_STREAM, 0);
	if (lfd < 0) 
		err("socket");
	int opt = 1;
	if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0) 
		err("SO_REUSEADDR");
	if (bind(lfd, laddr->ai_addr, laddr->ai_addrlen) < 0) 
		err("bind");
	if (listen(lfd, 20) < 0) 
		err("listen");
	eproxy_setnonblock(lfd, NULL);
	freeaddrinfo(laddr);

	if (epoll_add(efd, lfd, EPOLLIN, NULL) < 0) 
		err("epoll add listen fd");

	return lfd;
}

int main(int ac, char **av)
{
    eproxy_debug("\n");
	if (ac != 4 && ac != 5) {
		fprintf(stderr,
			 "Usage: proxy inport outhost outport [listenaddr]\n");
		exit(1);
	}

    /* 创建一个转发的目的地址信息 */
	struct addrinfo *outhost = eproxy_resolve(av[2], av[3], 0);

	int efd = epoll_create(10);
	if (efd < 0) 
		err("epoll_create");

    /*  */
	int lfd = eproxy_listen_socket(efd, av[4] ? av[4] : "0.0.0.0", av[1]);

	int cache_in = -1, cache_out = -1;	
	int timeo = -1;

	for (;;) {
		int nfds = epoll_wait(efd, eproxy_events, eproxy_num_events, timeo);
		if (nfds <= 0) {
			eproxy_debug("epoll_wait return %d, %s\n", nfds, strerror(errno));
			continue;
		}
        /* 获取当前时间 */
		time_t now = time(NULL);
        eproxy_debug("nfds = %d, now = %ld.\n", nfds, now);
        
		int i;
		for (i = 0; i < nfds; i++) { 
            eproxy_debug("nfds = %d.\n", nfds);
			struct epoll_event *ev = &eproxy_events[i];
			struct eproxy_conn *conn = ev->data.ptr;

			/* listen socket */
			if (conn == NULL) {
				if (ev->events & EPOLLIN) {
                    eproxy_debug("A New Client.\n");
					eproxy_new_request(efd, lfd, &cache_in, now);
                }
                eproxy_debug("A New Client Done.\n");
				continue;
			} 
            eproxy_debug("-----------------------\n");
			if (ev->events & (EPOLLERR|EPOLLHUP)) {
                eproxy_debug("Close A Client.\n");
				eproxy_closeconn(efd, conn);
				continue;
			}
            eproxy_debug("-----------------------\n");
			struct eproxy_conn *other = conn->other;

			/* No attempt for partial close right now */
			if (ev->events & EPOLLIN) {
                eproxy_debug("Gotta something IN.\n");
				eproxy_touch_conn(conn, now);
				if (!other)
					other = eproxy_openconn(efd, outhost, &cache_out,
							 conn, now);
				bool in = eproxy_move_data_in(conn->fd, &conn->buf);
				bool out = eproxy_move_data_out(&conn->buf, other->fd);
				if (!in || !out) { 
					eproxy_closeconn(efd, conn);
					continue;
				}
				eproxy_touch_conn(other, now);
			}	
				
			if ((ev->events & EPOLLOUT) && other) {
                eproxy_debug("Gotta something OUT.\n");
				if (!eproxy_move_data_out(&other->buf, conn->fd))
					eproxy_delconn(efd, conn);
				else
					eproxy_touch_conn(conn, now);

				/* When the pipe filled up could have
 				   lost input eproxy_events.  Unfortunately
 				   splice doesn't tell us which end
 				   was responsible for 0, so have to ask
				   explicitely. */
				int len = 0;
				if (ioctl(other->fd, FIONREAD, &len) < 0)
					perror("ioctl");
				if (len > 0) {
					if (!eproxy_move_data_in(other->fd, 
							  &other->buf))
						eproxy_closeconn(efd, other);
				}
			}
		}	

        //过期连接
		timeo = eproxy_expire_connections(efd, now);
        eproxy_debug("timeo = %d\n", timeo);
	}
	return 0;
}

list.h

/* Stripped down version of the Linux 2.6.30 list.h */

#include <stddef.h>

/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((void *) 0x00100100)
#define LIST_POISON2  ((void *) 0x00200200)

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({			\
	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})

struct list_head {
	struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
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;
}

/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}


/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
	next->prev = prev;
	prev->next = next;
}

static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}

/**
 * list_is_last - tests whether @list is the last entry in list @head
 * @list: the entry to test
 * @head: the head of the list
 */
static inline int list_is_last(const struct list_head *list,
				const struct list_head *head)
{
	return list->next == head;
}

/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(const struct list_head *head)
{
	return head->next == head;
}


/**
 * list_entry - get the struct for this entry
 * @ptr:	the &struct list_head pointer.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

/**
 * list_first_entry - get the first element from a list
 * @ptr:	the list head to take the element from.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_struct within the struct.
 *
 * Note, that list is expected to be not empty.
 */
#define list_first_entry(ptr, type, member) \
	list_entry((ptr)->next, type, member)

/**
 * list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 */
#define list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); \
        	pos = pos->next)

/**
 * list_for_each_prev	-	iterate over a list backwards
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 */
#define list_for_each_prev(pos, head) \
	for (pos = (head)->prev; pos != (head); \
        	pos = pos->prev)

/**
 * list_for_each_safe - iterate over a list safe against removal of list entry
 * @pos:	the &struct list_head to use as a loop cursor.
 * @n:		another &struct list_head to use as temporary storage
 * @head:	the head for your list.
 */
#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)


/**
 * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
 * @pos:	the &struct list_head to use as a loop cursor.
 * @n:		another &struct list_head to use as temporary storage
 * @head:	the head for your list.
 */
#define list_for_each_prev_safe(pos, n, head) \
	for (pos = (head)->prev, n = pos->prev; \
	     pos != (head); \
	     pos = n, n = pos->prev)

/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#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))


/**
 * list_for_each_entry_reverse - iterate backwards over list of given type.
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#define list_for_each_entry_reverse(pos, head, member)			\
	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
	     &pos->member != (head); 	\
	     pos = list_entry(pos->member.prev, typeof(*pos), member))

/**
 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 * @pos:	the type * to use as a loop cursor.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_struct within the struct.
 */
#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))

static inline void __list_del_entry(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
}

/**
 * list_move_tail - delete from one list and add as another's tail
 * @list: the entry to move
 * @head: the head that will follow our entry
 */
static inline void list_move_tail(struct list_head *list,
				  struct list_head *head)
{
	__list_del_entry(list);
	list_add_tail(list, head);
}

 

相关文章