信号之中断的系统调用、可重入函数、SIGCLD语义以及可靠信号术语和语义

本文来自个人博客:https://dunkwan.cn

中断的系统调用

早期UNIX系统的一个特性是:如果进程在执行一个低速系统调用而阻塞期间捕捉到一个信号,则该系统调用就被中断不再继续执行。该系统调用返回出错,其errno设置为EINTR。这样处理是因为一个信号发生了,进程捕捉到它,这意味着已经发生了某种事情,所以是个好机会应当唤醒阻塞的系统调用。

这里系统调用应当与函数进行区分,当捕捉到某个信号时,被中断的是内核中执行的系统调用。

为了支持这种特性,将系统调用分为两类:低速系统调用和其他系统调用。低速系统调用是可能会使进程永远阻塞的一类系统调用,包括:

  • 如果某些类型文件的数据不存在,则读操作可能会使调用者永远阻塞;
  • 如果这些数据不能被相同的类型文件立即接受,则写操作可能会使调用者永远阻塞;
  • 在这种条件发生之前打开某些类型文件,可能会发生阻塞;
  • pause函数和wait函数;
  • 某些ioctl操作;
  • 某些进程间通信函数。

下面是几种实现所提供的与信号有关的函数及它们的语义。

可重入函数

在信号处理程序中保证调用安全的函数,这些函数是可重入函数并被称之为异步信号安全的。以下是一些异步信号安全的函数。

测试示例:

#include "../../include/apue.h"
#include <pwd.h>

static void my_alarm(int signo)
{
    struct passwd *rootptr;

    printf("in signal handler\n");
    if((rootptr = getpwnam("root")) == NULL)
        err_sys("getpwnam(root) error");
    alarm(1);
}

int main(void)
{
    struct passwd *ptr;

    signal(SIGALRM, my_alarm);
    alarm(1);
    for(;;){
        if((ptr = getpwnam("dunk")) == NULL)
            err_sys("getpwnam error");
        if(strcmp(ptr->pw_name, "dunk") != 0)
            printf("return value corrupted!, pw_name = %s\n", ptr->pw_name);
    }

}

在运行该程序时,结果具有随机性。从这个示例中可以看出,如果在信号处理程序中调用一个非可重入函数,则其结果是不可知的。

SIGCLD语义

SIGCLDSIGCHLD这两个信号很容易被混淆。SIGCLDsystem V的一个信号名,其语义与名为SIGCHLD的BSD信号不同。POSIX.1采用BSD的SIGCHLD信号。

对于SIGCLD的早期处理方式是:

  1. 如果进程明确地将该信号的配置设置为SIG_IGN,则调用进程的子进程将不产生僵尸进程。
  2. 如果将SIGCLD的配置设置为捕捉,则内核立即检查是否有子进程准备好被等待,如果是这样,则调用SIGCLD处理程序。

测试示例:

#include "../../include/apue.h"
#include <sys/wait.h>

static void sig_cld(int);

int main(void)
{
    pid_t pid;

    if(signal(SIGCHLD, sig_cld) == SIG_ERR)
        perror("signal error");

    if((pid = fork()) < 0){
        perror("fork error");
    }else if(pid == 0){
        sleep(2);
        _exit(0);
    }

    pause();
    return 0;
}

static void sig_cld(int signo)
{
    pid_t pid;
    int status;

    printf("SIGCHLD received\n");

    if(signal(SIGCHLD, sig_cld) == SIG_ERR)
        perror("signal error");

    if((pid = wait(&status)) < 0)
        perror("wait error");

    printf("pid = %d\n", pid);
}

结果如下:

可靠信号术语和语义

  1. 造成信号的事件:硬件异常(除数为0)、软件条件(alarm定时器超时)、终端产生的信号或调用kill函数。
  2. 当一个信号产生时,内核通常在进程表中以某种形式设置一个标志,这种行为称之为向进程递送了一个信号。在信号产生和递送之间的时间间隔内,称信号是未决的(pending)。
  3. POSIX.1允许系统递送该信号一次或多次,如果信号被递送多次,则称信号进行了排队。
  4. 每个进程都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集。
  5. 信号编号可能会超过一个整型所包含的二进制位数,因此POSIX.1定义了一个新数据类型sigset_t,它可容纳一个信号集
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值