опрос на солярисе

этот код компилируется в Linux, но не в Solaris, потому что, по-видимому, ppoll() специфичен для Linux (я получаю ошибку неопределенного символа в Solaris с GCC). Любая помощь преобразовать его? Я не думаю, что просто использовать poll() — хорошая идея, но опять же, я не писал этот код. (Я получил это от Пишем оболочку командной строки на C; впервые пытаемся использовать ncurses/C)

#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(&current_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);
}

person 2rs2ts    schedule 06.04.2011    source источник


Ответы (2)


Причина, по которой вы можете заменить ppoll() на poll() здесь на данный момент, заключается в том, что не потому, что маска сигнала, предоставленная ppoll(), пуста — это фактически обычный случай, — а потому, что маска сигнала до ppoll(), вероятно, так же пусто.

Это возможно только потому, что в настоящее время оболочка не пытается обрабатывать какие-либо сигналы. Как только вы захотите сделать сигналы дескриптора оболочки, вам нужно будет использовать следующую последовательность:

/* 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() является расширением GNU, которое Solaris не предоставляет, вам потребуется изменить код, чтобы использовать 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().

person caf    schedule 06.04.2011
comment
Спасибо за информацию! Можете ли вы привести пример использования pselect() в этом коде? pselect() имеет больше параметров, чем ppoll(). - person 2rs2ts; 07.04.2011
comment
@ user691859: Я добавил код, показывающий, как заменить ppoll() на pselect(). Я предлагаю внимательно прочитать справочные страницы для обеих функций. - person caf; 07.04.2011
comment
Благодаря тонну! Кажется, я понимаю, зачем нужны pselect()/ppoll(). Совсем недавно мы обсуждали условия гонки, синхронизацию и т. д. в классе. :) Я также считаю, что нашел хорошее объяснение этим функциям... Взаимосвязь между poll( ) и ppoll() аналогичны отношениям между select() и pselect(): как и pselect(), ppoll() позволяет приложению безопасно ожидать, пока либо дескриптор файла не будет готов, либо пока не будет перехвачен сигнал. - person 2rs2ts; 07.04.2011

Вы не используете ни тайм-ауты менее миллисекунды, ни тайм-аут, который переполнил бы (int), ни непустой sigmask, поэтому poll() подходит для этого.

person geekosaur    schedule 06.04.2011
comment
Итак, что мне делать с сигмаской? - person 2rs2ts; 06.04.2011
comment
Опустить это. Единственное, что вы когда-либо делали с ним, — это вызывали его sigemptyset(), так что он нужен только для того, чтобы удовлетворить ppoll() желание видеть sigset_t параметр; на самом деле ничего не делает. - person geekosaur; 06.04.2011
comment
Хорошо, последний вопрос касается вызова poll()... какое время ожидания установить? Я попробовал 1, и экран как бы болезненно мигает каждую секунду или около того. - person 2rs2ts; 06.04.2011
comment
С poll() время ожидания указывается в миллисекундах. Вы используете тайм-аут в 1 секунду, поэтому умножьте на 1000, чтобы получить миллисекунды. - person geekosaur; 06.04.2011