sigaction()与signal()有何不同?

时间:2017-07-31 09:42:19

标签: tty signals pty

prog2未在CTRL+D退出。为什么prog1会在CTRL+D退出?更奇怪的是,信号处理程序不会执行任何操作,尽管它会以某种方式影响最终结果......

以下两个程序仅与prog1.c sigaction()中使用的程序不同,并且使用了prog2.c signal()

@@ -39,10 +39,7 @@
     /* read from loopback tty */
     if (cpid > 0) {
         /* this is the strange part */
-        struct sigaction sa;
-        sa.sa_handler = child_handler;
-        sa.sa_flags = 0;
-        sigaction(SIGCHLD, &sa, NULL);
+        signal(SIGCHLD, child_handler);

         struct termios tty;
         tcgetattr(fd, &tty);

这些程序中的每一个都只是打开一个环回tty并将自身分成两个进程,其中一个进程从tty读取响应,另一个进程将数据写入tty设备。然后,从环回tty虚拟设备接收的数据输出到控制终端。

使用prog1选项编译prog2-lutil。启动每个程序并键入hello<CTRL+D>。这导致以下输出:

$ ./prog1
hello$

$ ./prog2
hello

BTW,应在sigaction()中设置哪些标记以复制signal()的行为?

以下是计划:

prog1.c的

#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pty.h>

int fd;

void child_handler(int s)
{
    (void) s;
}

int main(void)
{
    char c;

    /* open loopback tty device */
    pid_t lpid;
    lpid = forkpty(&fd, NULL, NULL, NULL);
    if (lpid == -1) {
        exit(1);
    }
    if (lpid == 0) {
        char *args[] = { "cat", NULL };
        execv("/bin/cat", args);
    }

    /* create parallel process */
    pid_t cpid;
    cpid = fork();
    if (cpid == -1) {
        close(fd);
        exit(1);
    }

    /* read from loopback tty */
    if (cpid > 0) {
        /* this is the strange part */
        struct sigaction sa;
        sa.sa_handler = child_handler;
        sa.sa_flags = 0;
        sigaction(SIGCHLD, &sa, NULL);

        struct termios tty;
        tcgetattr(fd, &tty);
        cfmakeraw(&tty);
        tcsetattr(fd, TCSANOW, &tty);

        while (read(fd, &c, 1) != -1)
            write(STDOUT_FILENO, &c, 1);
    }

    /* write to loopback tty */
    if (cpid == 0) {
        struct termios stdtio_restore;
        struct termios stdtio;
        tcgetattr(STDIN_FILENO, &stdtio_restore);
        tcgetattr(STDIN_FILENO, &stdtio);
        cfmakeraw(&stdtio);
        tcsetattr(STDIN_FILENO, TCSANOW, &stdtio);

        while (1) {
            read(STDIN_FILENO, &c, 1);
            if (c == 0x04) break;
            write(fd, &c, 1);
        }

        tcsetattr(0, TCSANOW, &stdtio_restore);
        close(fd);
        exit(0);
    }

    return 0;
}

prog2.c

#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pty.h>

int fd;

void child_handler(int s)
{
    (void) s;
}

int main(void)
{
    char c;

    /* open loopback tty device */
    pid_t lpid;
    lpid = forkpty(&fd, NULL, NULL, NULL);
    if (lpid == -1) {
        exit(1);
    }
    if (lpid == 0) {
        char *args[] = { "cat", NULL };
        execv("/bin/cat", args);
    }

    /* create parallel process */
    pid_t cpid;
    cpid = fork();
    if (cpid == -1) {
        close(fd);
        exit(1);
    }

    /* read from loopback tty */
    if (cpid > 0) {
        /* this is the strange part */
        signal(SIGCHLD, child_handler);

        struct termios tty;
        tcgetattr(fd, &tty);
        cfmakeraw(&tty);
        tcsetattr(fd, TCSANOW, &tty);

        while (read(fd, &c, 1) != -1)
            write(STDOUT_FILENO, &c, 1);
    }

    /* write to loopback tty */
    if (cpid == 0) {
        struct termios stdtio_restore;
        struct termios stdtio;
        tcgetattr(STDIN_FILENO, &stdtio_restore);
        tcgetattr(STDIN_FILENO, &stdtio);
        cfmakeraw(&stdtio);
        tcsetattr(STDIN_FILENO, TCSANOW, &stdtio);

        while (1) {
            read(STDIN_FILENO, &c, 1);
            if (c == 0x04) break;
            write(fd, &c, 1);
        }

        tcsetattr(0, TCSANOW, &stdtio_restore);
        close(fd);
        exit(0);
    }

    return 0;
}

1 个答案:

答案 0 :(得分:4)

行为上的差异可能是因为signal的行为就像在SA_RESTART上设置sigaction标志一样。从signal(2)手册页:

  

BSD语义等同于用sigaction(2)调用   以下标志:

sa.sa_flags = SA_RESTART;
     

Linux的情况如下:

     
      
  • 内核的signal()系统调用提供了System V语义。

  •   
  • 默认情况下,在glibc 2及更高版本中,signal()包装函数不会调用内核系统调用。相反,它打电话   sigaction(2)使用提供BSD语义的标志...

  •   

使用SA_RESTART标志时,会自动重启某些系统调用。如果未使用,则调用将返回错误,并将错误号设置为EINTR

因此,在prog1中的“从环回读取”过程中会发生以下情况:

  1. 您的流程已在read
  2. 中被阻止
  3. 它接收SIGCHLD并运行处理程序。
  4. read系统调用它被阻止返回-1
  5. 循环基于while条件退出,并退出流程。
  6. prog2中,SA_RESTART行为意味着在(2)中运行信号处理程序后,read调用将重新启动。

    要使prog1的行为与prog2相同,请设置SA_RESTART

    sa.sa_flags = SA_RESTART;
    

    有关SA_RESTART行为的更多详细信息,请参阅signal(7)手册页中的“信号处理程序中断系统调用和库函数”部分。