系统错误码EINTR以及如何模拟出一个EINTR

本文深入探讨了EINTR错误产生的原因及其与慢系统调用的关系,特别是网络编程中常见的accept、read、write等函数遇到信号中断时的行为。文章通过具体代码示例,演示了如何模拟慢系统调用被信号中断的场景,以及如何处理connect函数返回EINTR错误的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为什么会有EINTR错误,什么是慢系统调用:

下面这段话我是从网上找的,总结的挺好,我就直接拿来用

  • 慢系统调用:适用于那些可能永远阻塞的系统调用。永远阻塞的系统调用是指调用有可能永远无法返回,多数网络支持函数都属于这一类;
  • 常见慢系统调用: 例如:accept、read、write、select、和open之类的函数来说,是可以进行重启的,函数出错为Interrupted system call,包括设置等待时间为永久阻塞等待的select、poll、epoll_wait也是一样的道理;
  • EINTR错误的产生:当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。例如:在socket服务器端,设置了信号捕获机制,有子进程,当在父进程阻塞于慢系统调用时由父进程捕获到了一个有效信号时,内核会致使accept返回一个EINTR错误(被中断的系统调用);
  • 碰到EINTR错误的时候,可以采取有一些可以重启的系统调用要进行重启(重新调用),而对于有一些系统调用是不能够重启的;不过对于套接字编程中的connect函数我们是不能重启的,若connect函数返回一个EINTR错误的时候,我们不能再次调用它,否则将立即返回一个错误。针对connect不能重启的处理方法是,必须调用select来等待连接完成

如何模拟慢系统调用被信号中断?

这段代码着重看epoll_wait()前后,如何模拟系统被系统调用打断的场景:

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <assert.h>
#include <pthread.h>
#include <signal.h>

void sig_alrm(int signo) { return; }

struct fds {
    int epollfd;
    int sockfd;
};

#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10

int setnonblocking(int fd) {
    int old_option = fcntl(fd, F_GETFD);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

void addfd(int epollfd, int fd, bool oneshot) {
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET; //给fd注册EPOLLIN和EPOLLET
    if (oneshot) {
        event.events |= EPOLLONESHOT; //给fd上注册EPOLLONESHOT事件
    }
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

//重置fd上的事件,
void reset_oneshot(int epollfd, int fd) {
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}

//工作线程
void* worker(void* arg) {
    int sockfd = ((fds*)arg)->sockfd;
    int epollfd = ((fds*)arg)->epollfd;

    printf("This is a new work thread to recive data on fd,id = %u\n", pthread_self());
    char buf[BUFFER_SIZE];
    memset(buf, 0, BUFFER_SIZE);

    while (1) {
        int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);
        if (ret == -1 && errno == EAGAIN) {
            reset_oneshot(epollfd, sockfd);
            printf("read later \n");
            break;
        }
        else if (ret == 0) {
            close(sockfd);
            printf("connection was closed\n");
            break;
        }
        else {
            printf("get data :%s\n", buf);
            //模拟 该数据处理过程,假定使用了5秒
            sleep(5);
        }
    }
    //printf();
}

const char* ip = "127.0.0.1";
int port = 6600;

int main() {
    int ret = 0;
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(listenfd != -1);

    ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
    if (ret == -1) {
        perror("bind failed");
        exit(-1);
    }

    ret = listen(listenfd, 5);
    if (ret == -1) {
        perror("listen failed");
        exit(-1);
    }

    epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);
    assert(epollfd != -1);

    addfd(epollfd, listenfd, false);

    while (1) {
        /*
        timeout == -1,表示将永远阻塞
        如果此时没有客户去连接这个fd,那么epoll_wait将一直阻塞
        我们将在这里模拟如何用信号中断这样的阻塞,并且打印出对应的errno
        */
        signal(SIGALRM,sig_alrm);
        alarm(2);
        int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
        if (ret < 0) {
            perror("epoll wait failed");
            break;
        }
        for (int i = 0; i < ret; i++) {
            int sockfd = events[i].data.fd;

            if (sockfd == listenfd) {
                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof(client_address);
                int connectfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
                addfd(epollfd, connectfd, true);//对connfd开启ET模式
            }
            else if (events[i].events & EPOLLIN) {
                pthread_t thread;
                fds fds_for_new_worker;
                fds_for_new_worker.epollfd = epollfd;
                fds_for_new_worker.sockfd = sockfd;

                //pthread_create(&thread, NULL, worker, (void*)&fds_for_new_worker);
            }
            else {
                printf("something else happend \n");
            }
        }
    }
    close(listenfd);
    return 0;
}

输出:

epoll wait failed: Interrupted system call
### RT-Thread 中线程操作遇到中断错误 (RT_EINTR) 的原因及处理 当在 RT-Thread 操作系统中执行线程操作时,如果返回 `RT_EINTR` 错误码,则表示该调用被信号打断。这通常发生在阻塞函数等待某些条件满足的过程中接收到信号或发生异常情况。 #### 可能的原因 1. **外部中断触发** 当前正在运行的线程可能因为外部硬件中断而被迫停止当前的操作并切换到中断服务程序(ISR),从而导致操作未能完成[^1]。 2. **软件定时器超时或其他内部事件** 如果设置了特定条件下终止等待状态(例如通过定时器),那么一旦这些条件达成也会引发类似的中断行为,进而使原生API提前退出并报告此错误代码[^2]。 3. **其他高优先级任务抢占CPU资源** 实时多任务环境中存在多个不同级别的进程/线程竞争处理器时间片;如果有更紧急的任务需要立即得到调度权,则较低级别者会被挂起直到前者释放控制权为止,在这段时间里发生的任何变化都有可能导致最初发起的动作失败,并给出相应的提示信息即`RT_EINTR`。 #### 处理方案 针对上述提到的各种可能性,可以采取如下措施来应对: - 对于由外部因素引起的意外终止情形,建议开发者仔细审查应用程序逻辑设计以及所使用的外设驱动接口文档,确保所有必要的防护机制均已到位; - 若是因为设定好的时限到达所致,则应考虑调整参数配置使之更加合理化,或者改用非阻塞性质的方法代替原有方式实现相同功能需求; - 面对由于上下文切换带来的影响,一方面可以通过优化算法减少不必要的延迟开销,另一方面也可以尝试提高目标对象本身的重要性等级以便获得更多的计算机会窗口期。 ```c // 示例:重试机制处理 RT_EINTR int result; do { result = rt_sem_take(sem, RT_WAITING_FOREVER); } while (result == -RT_EINTR); if(result != RT_EOK){ // handle other errors... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值