kqueue (کوتاه‌شده عبارت Kernel Queue) یک رابط آگاه‌سازی از رویدادهاست که اولین بار در نسخه ۴٫۱ سیستم‌عامل فری‌بی‌اس‌دی معرفی شد و در سیستم‌عامل‌های اوپن‌بی‌اس‌دی، نت‌بی‌اس‌دی، دراگون‌فلی بی‌اس‌دی و مک اواس ده هم پشتیبانی می‌شود. kqueue خطوط ارتباطی ورودی و خروجی کارایی را بین هسته سیستم‌عامل و برنامه‌های موجود در فضای کاربری فراهم می‌کند. بدین‌گون که تغییر دادن فیلترهای رویدادی و همچنین دریافت کردن رویدادهای معلق با استفاده از تنها یک بار فراخوانی kevent()‎ در هر دور تکرار از حلقه رویداد، امکان‌پذیر است. این در تضاد با فراخوان‌های سیستمی مانند poll()‎ و select()‎ است که کارایی کمتری دارند، خصوصاً در حین سرکشی کردن بر روی تعداد زیادی از توصیف‌گرهای پرونده.

kqueue نه تنها رویدادهای مربوط به توصیف‌گرهای پرونده را اداره می‌کند، بلکه همچنین برای انواع مختلف آگاه‌سازی‌های دیگر از جمله نظاره کردن برای تغییر یافتن فایل‌ها، سیگنال‌ها، رویدادهای ورودی/خروجی ناهم‌گام، نظاره کردن تغییر وضعیت فرایند فرزند و تایمرها که از نانو ثانیه هم پشتیبانی می‌کند.

برخی از سیستم‌عامل‌های دیگر که تنها select()‎ poll()‎ را پشتیبانی می‌کنند هم در حال حاضر تعدادی راه حل جایگزین مشابه kqueue ارائه کرده‌اند که از جمله آنها می‌توان به epoll در لینوکس اشاره کرد. استفاده مستقیم از kquque می‌تواند باعث شود تا برنامه پورتابل بودن خود را از دست بدهد، چرا که بعضی از سیستم‌عامل‌ها از kqueue پشتیبانی نمی‌کنند. در عوض، می‌توان از رابط‌های آگاه‌سازی غیر وابسته به پلتفرم همانند libevent استفاده کرد، بدون اینکه تفاوت قابل ملاحظه‌ای در کارایی برنامه از دست برود.

مثال ویرایش

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/event.h>
#include <sys/time.h>
#include <errno.h>

void die(const char*);

int main(int argc, char *argv[])
{
	int sockfd, nev, kq;

	struct addrinfo hints, *res, *res0;

	struct kevent change[2], event[2];

	ssize_t nbytes;

	char buf[BUFSIZ];

	const char *cause = NULL;

	if (3 != argc)
	{
		fprintf(stderr, "Usage: %s address port\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	(void)memset(&hints, '\0', sizeof(struct addrinfo));

	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;

	if (-1 == getaddrinfo(argv[1], argv[2], &hints, &res0))
		die("getaddrinfo");

	sockfd = -1;

	for (res = res0; res; res = res->ai_next)
	{
		if (-1 == (sockfd = socket(res->ai_family, res->ai_socktype,
		 res->ai_protocol)))
		{
			cause = "socket";
			continue;
		}

		if (-1 == connect(sockfd, res->ai_addr, res->ai_addrlen))
		{
			cause = "connect()";
			close(sockfd);
			continue;
		}

		break;

	}

	if (-1 == sockfd)
		die(cause);

	if (-1 == fcntl(sockfd, F_SETFL, O_NONBLOCK))
		die("fcntl1()");

	if (-1 == fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK))
		die("fcntl()");

	if (-1 == (kq = kqueue()))
		die("kqueue()");

	EV_SET(&change[0], STDIN_FILENO, EVFILT_READ, EV_ADD | EV_ENABLE,
	 0, 0, 0);
	EV_SET(&change[1], sockfd, EVFILT_READ, EV_ADD | EV_ENABLE,
	 0, 0, 0);

	for (;;)
	{
		if (-1 == (nev = kevent(kq, change, 2, event, 2, NULL)))
			die("kevent()");

		for (int i = 0; i < nev; i++)
		{
			if (event[i].ident == STDIN_FILENO)
			{
				fgets(buf, sizeof(buf), stdin);

				nbytes = send(sockfd, buf, strlen(buf), 0);

				if (-1 == nbytes)
					if (EWOULDBLOCK != errno)
						die("send()");

			}
			else
			{
				nbytes = recv(sockfd, buf, sizeof(buf), 0);

				if (-1 == nbytes)
					if (EWOULDBLOCK != errno)
						die("recv()");

				buf[nbytes] = '\0';

				write(STDOUT_FILENO, buf, strlen(buf));
			}

		}

	}

	return 0;
}

void die(const char *str)
{
	perror(str);
	exit(EXIT_FAILURE);
}

منابع ویرایش

مشارکت‌کنندگان ویکی‌پدیا. «kqueue». در دانشنامهٔ ویکی‌پدیای انگلیسی.