solaris 上的 ppoll
Posted
技术标签:
【中文标题】solaris 上的 ppoll【英文标题】:ppoll on solaris 【发布时间】:2011-04-06 00:48:52 【问题描述】:此代码在 Linux 中编译,但在 Solaris 中编译,因为显然 ppoll() 是特定于 Linux 的(我在使用 GCC 的 Solaris 中遇到未定义符号错误)。任何帮助转换它?我不认为只使用 poll() 是一个好主意,但话又说回来,我没有编写这段代码。 (我从Writing a command line shell with C; trying to use ncurses/C for the first time得到它)
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/** VT100 command to clear the screen. Use puts(VT100_CLEAR_SCREEN) to clear
* the screen. */
#define VT100_CLEAR_SCREEN "\033[2J"
/** VT100 command to reset the cursor to the top left hand corner of the
* screen. */
#define VT100_CURSOR_TO_ORIGIN "\033[H"
struct frame_s
int x;
int y;
char *data;
;
static int draw_frame(struct frame_s *frame)
int row;
char *data;
int attrib;
puts(VT100_CLEAR_SCREEN);
puts(VT100_CURSOR_TO_ORIGIN);
for (row = 0, data = frame->data; row < frame->y; row++, data += frame->x)
/* 0 for normal, 1 for bold, 7 for reverse. */
attrib = 0;
/* The VT100 commands to move the cursor, set the attribute, and the
* actual frame line. */
fprintf(stdout, "\033[%d;%dH\033[0m\033[%dm%.*s", row + 1, 0, attrib, frame->x, data);
fflush(stdout);
return (0);
int main(void)
const struct timespec timeout = .tv_sec = 1, .tv_nsec = 0 ;
struct frame_s frame;
struct termios tty_old;
struct termios tty_new;
unsigned char line[128];
unsigned int count = 0;
int ret;
struct pollfd fds[1];
sigset_t sigmask;
struct tm *tp;
time_t current_time;
/* Set up a little frame. */
frame.x = 80;
frame.y = 5;
frame.data = malloc(frame.x * frame.y);
if (frame.data == NULL)
fprintf(stderr, "No memory\n");
exit (1);
memset(frame.data, ' ', frame.x * frame.y);
/* Get the terminal state. */
tcgetattr(STDIN_FILENO, &tty_old);
tty_new = tty_old;
/* Turn off "cooked" mode (line buffering) and set minimum characters
* to zero (i.e. non-blocking). */
tty_new.c_lflag &= ~ICANON;
tty_new.c_cc[VMIN] = 0;
/* Set the terminal attributes. */
tcsetattr(STDIN_FILENO, TCSANOW, &tty_new);
/* Un-mask all signals while in ppoll() so any signal will cause
* ppoll() to return prematurely. */
sigemptyset(&sigmask);
fds[0].events = POLLIN;
fds[0].fd = STDIN_FILENO;
/* Loop forever waiting for key presses. Update the output on every key
* press and every 1.0s (when ppoll() times out). */
do
fds[0].revents = 0;
ret = ppoll(fds, sizeof(fds) / sizeof(struct pollfd), &timeout, &sigmask);
if (fds[0].revents & POLLIN)
ret = read(STDIN_FILENO, &line[count], sizeof(line) - count);
if (ret > 0)
line[count + ret] = '\0';
if (strcmp(&line[count], "\033[A") == 0)
snprintf(frame.data, frame.x, "up");
count = 0;
else if (strcmp(&line[count], "\033[B") == 0)
snprintf(frame.data, frame.x, "down");
count = 0;
else if (line[count] == '\n')
snprintf(frame.data, frame.x, "entered: %s", line);
count = 0;
else
count += ret;
/* Print the current time to the output buffer. */
current_time = time(NULL);
tp = localtime(¤t_time);
strftime(&frame.data[1 * frame.x], frame.x, "%Y/%m/%d %H:%M:%S", tp);
/* Print the command line. */
line[count] = '\0';
snprintf(&frame.data[(frame.y - 1) * frame.x], frame.x, "$ %s", line);
draw_frame(&frame);
while (1);
/* Restore terminal and free resources. */
tcsetattr(STDIN_FILENO, TCSANOW, &tty_old);
free(frame.data);
return (0);
【问题讨论】:
【参考方案1】:您现在可以将ppoll()
替换为poll()
的原因是不是,因为提供给ppoll()
的信号掩码是空的——这实际上是常见的情况——但是因为ppoll()
之前的信号掩码也可能为空。
这只是可能的,因为 shell 当前不尝试处理任何信号。只要你想让 shell 处理信号,你就需要使用以下序列:
/* Block all signals */
sigprocmask(SIG_SETMASK, all_signals);
/* Check and handle any signals that have occured. */
check_signals();
/* Wait for activity on a file descriptor or a signal */
ppoll(..., empty_set);
ppoll()
在这里是必需的,因为否则会出现竞争条件 - 信号可能会在 check_signals();
和 poll()
调用之间到达,这将被错过。因此,信号在这段时间内被阻塞,然后原子地在ppoll()
中解除阻塞。
由于ppoll()
是Solaris 不提供的GNU 扩展,您需要更改代码以使用pselect()
,这是一个POSIX 标准函数。
要将您的代码转换为使用pselect()
,请将循环的开头替换为:
do
fd_set rdset;
int nfds = STDIN_FILENO + 1;
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO, &rdset);
ret = pselect(nfds, &rdset, NULL, NULL, &timeout, &sigmask);
if (ret < 0)
if (errno == EINTR)
continue;
else
break;
if (FD_ISSET(STDIN_FILENO, &rdset))
ret = read(STDIN_FILENO, &line[count], sizeof(line) - count);
/* ... */
然后您可以删除变量fds
,因为它不再需要。
请注意,我还添加了代码来检查 pselect()
的返回值是否有错误 - 旧代码应该对 ppoll()
执行相同的操作。
【讨论】:
感谢您的信息!你能给我一个如何在这段代码中使用pselect()
的例子吗? pselect()
的参数比 ppoll()
多。
@user691859:我添加了代码,显示如何将ppoll()
替换为pselect()
。我建议彻底阅读这两个函数的手册页。
非常感谢!我相信我明白为什么pselect()
/ppoll()
是必要的。我们最近在课堂上介绍了比赛条件、同步等。 :) 我也相信我找到了对这些功能的一个很好的解释...The relationship between poll() and ppoll() is analogous to the relationship between select() and pselect(): like pselect(), ppoll() allows an application to safely wait until either a file descriptor becomes ready or until a signal is caught.【参考方案2】:
您没有使用亚毫秒超时、会溢出(int)
或非空sigmask
的超时,因此poll()
可以解决此问题。
【讨论】:
好的,那么我应该如何处理 sigmask? 省略它。你唯一能做的就是在上面调用sigemptyset()
,所以它只是为了满足ppoll()
想要看到sigset_t
参数;它实际上并没有做任何事情。
好吧,最后一个问题是关于 poll() 调用...我应该将超时设置为什么?我试过 1,屏幕每隔一秒左右就有点恶心地闪烁。
poll()
超时以毫秒为单位。您使用的超时时间为 1 秒,因此乘以 1000 得到毫秒。以上是关于solaris 上的 ppoll的主要内容,如果未能解决你的问题,请参考以下文章
solaris (libCstd) 上的 std::sort 问题
Solaris 上的 GCC - 在“0x00000002”之前解析错误