企业营销型网站的内容,服务公司荡神,jq效果较多的网站,阿里云网站建设素材1. 术语 1.1. 慢系统调用#xff08;Slow system call#xff09; 该术语适用于那些可能永远阻塞的系统调用。永远阻塞的系统调用是指调用永远无法返回#xff0c;多数网络支持函数都属于这一类。如#xff1a;若没有客户连接到服务器上#xff0c;那么服务器的accept调用… 1. 术语 1.1. 慢系统调用Slow system call 该术语适用于那些可能永远阻塞的系统调用。永远阻塞的系统调用是指调用永远无法返回多数网络支持函数都属于这一类。如若没有客户连接到服务器上那么服务器的accept调用就会一直阻塞。 慢系统调用可以被永久阻塞包括以下几个类别 1读写‘慢’设备包括pipe终端设备网络连接等。读时数据不存在需要等待写时缓冲区满或其他原因需要等待。读写磁盘文件一般不会阻塞。 2当打开某些特殊文件时需要等待某些条件才能打开。例如打开中断设备时需要等到连接设备的modem响应才能完成。 3pause和wait函数。pause函数使调用进程睡眠直到捕获到一个信号。wait等待子进程终止。 4某些ioctl操作。 5某些IPC操作。 2. EINTR介绍 2.1. EINTR错误产生的原因 早期的Unix系统如果进程在一个慢系统调用(slow system call)中阻塞时当捕获到某个信号且相应信号处理函数返回时这个系统调用被中断调用返回错误设置errno为EINTR相应的错误描述为“Interrupted system call”。 怎么看哪些系统条用会产生EINTR错误呢用man啊 如下表所示的系统调用就会产生EINTR错误当然不同的函数意义也不同。 系统调用函数 errno为EINTR表征的意义 write 由于信号中断没写成功任何数据。 The call was interrupted by a signal before any data was written. open 由于信号中断没读到任何数据。 The call was interrupted by a signal before any data was read. recv 由于信号中断返回没有任何数据可用。 The receive was interrupted by delivery of a signal before any data were available. sem_wait 函数调用被信号处理函数中断。 The call was interrupted by a signal handler. 2.2. 如何处理被中断的系统调用 既然系统调用会被中断那么别忘了要处理被中断的系统调用。有三种处理方式 ◆ 人为重启被中断的系统调用 ◆ 安装信号时设置 SA_RESTART属性该方法对有的系统调用无效 ◆ 忽略信号让系统不产生信号中断 2.2.1. 人为重启被中断的系统调用 人为当碰到EINTR错误的时候有一些可以重启的系统调用要进行重启而对于有一些系统调用是不能够重启的。例如accept、read、write、select、和open之类的函数来说是可以进行重启的。不过对于套接字编程中的connect函数我们是不能重启的若connect函数返回一个EINTR错误的时候我们不能再次调用它否则将立即返回一个错误。针对connect不能重启的处理方法是必须调用select来等待连接完成。 这里的“重启”怎么理解 一些IO系统调用执行时如 read 等待输入期间如果收到一个信号系统将中断read 转而执行信号处理函数. 当信号处理返回后 系统遇到了一个问题 是重新开始这个系统调用 还是让系统调用失败早期UNIX系统的做法是 中断系统调用并让系统调用失败 比如read返回 -1 同时设置 errno 为EINTR中断了的系统调用是没有完成的调用它的失败是临时性的如果再次调用则可能成功这并不是真正的失败所以要对这种情况进行处理 典型的方式为 [cpp] view plaincopy again: if ((n read(fd buf BUFFSIZE)) 0) { if (errno EINTR) goto again; /* just an interrupted system call */ /* handle other errors */ } 可以去github上看看别人怎么处理EINTR错误的。在github上搜索“EINTR”关键字就有一大堆了。摘取几个看看 [cpp] view plaincopy …… while ((r read (fd buf len)) 0 errno EINTR) /*do nothing*/ ; …… [cpp] view plaincopy ssize_t Read(int fd void *ptr size_t nbytes) { ssize_t n; again: if((n read(fd ptr nbytes)) -1){ if(errno EINTR) goto again; else return -1; } return n; } 2.2.2. 安装信号时设置 SA_RESTART属性 我们还可以从信号的角度来解决这个问题 安装信号的时候 设置 SA_RESTART属性那么当信号处理函数返回后 不会让系统调用返回失败而是让被该信号中断的系统调用将自动恢复。 [cpp] view plaincopy struct sigaction action; action.sa_handler handler_func; sigemptyset(action.sa_mask); action.sa_flags 0; /* 设置SA_RESTART属性 */ action.sa_flags | SA_RESTART; sigaction(SIGALRM, action, NULL); 但注意并不是所有的系统调用都可以自动恢复。如msgsnd喝msgrcv就是典型的例子msgsnd/msgrcv以block方式发送/接收消息时会因为进程收到了信号而中断。此时msgsnd/msgrcv将返回-1errno被设置为EINTR。且即使在插入信号时设置了SA_RESTART也无效。在man msgrcv中就有提到这点 msgsnd and msgrcv are never automatically restarted after being interrupted by a signal handler, regardless of the setting of the SA_RESTART flag when establishing a signal handler. 2.2.3. 忽略信号 当然最简单的方法是忽略信号在安装信号时明确告诉系统不会产生该信号的中断。 [cpp] view plaincopy struct sigaction action; action.sa_handler SIG_IGN; sigemptyset(action.sa_mask); sigaction(SIGALRM, action, NULL); 3. 测试代码 为了方便大家测试这里附上两段测试代码。 3.1. 测试代码一 闹钟信号SIGALRM中断read系统调用。安装SIGALRM信号时如果不设置SA_RESTART属性信号会中断read系统过调用。如果设置了SA_RESTART属性read就能够自己恢复系统调用不会产生EINTR错误。 [cpp] view plaincopy #include signal.h #include stdio.h #include stdlib.h #include error.h #include string.h #include unistd.h void sig_handler(int signum) { printf(in handler\n); sleep(1); printf(handler return\n); } int main(int argc, char **argv) { char buf[100]; int ret; struct sigaction action, old_action; action.sa_handler sig_handler; sigemptyset(action.sa_mask); action.sa_flags 0; /* 版本1:不设置SA_RESTART属性 * 版本2:设置SA_RESTART属性 */ //action.sa_flags | SA_RESTART; sigaction(SIGALRM, NULL, old_action); if (old_action.sa_handler ! SIG_IGN) { sigaction(SIGALRM, action, NULL); } alarm(3); bzero(buf, 100); ret read(0, buf, 100); if (ret -1) { perror(read); } printf(read %d bytes:\n, ret); printf(%s\n, buf); return 0; } 3.2. 测试代码二 闹钟信号SIGALRM中断msgrcv系统调用。即使在插入信号时设置了SA_RESTART也无效。 [cpp] view plaincopy #include stdio.h #include stdlib.h #include unistd.h #include errno.h #include signal.h #include sys/types.h #include sys/ipc.h #include sys/msg.h void ding(int sig) { printf(Ding!\n); } struct msgst { long int msg_type; char buf[1]; }; int main() { int nMsgID -1; // 捕捉闹钟信息号 struct sigaction action; action.sa_handler ding; sigemptyset(action.sa_mask); action.sa_flags 0; // 版本1:不设置SA_RESTART属性 // 版本2:设置SA_RESTART属性 action.sa_flags | SA_RESTART; sigaction(SIGALRM, action, NULL); alarm(3); printf(waiting for alarm to go off\n); // 新建消息队列 nMsgID msgget(IPC_PRIVATE, 0666 | IPC_CREAT); if( nMsgID 0 ) { perror(msgget fail ); return; } printf(msgget success.\n); // 阻塞 等待消息队列 // // msgrcv会因为进程收到了信号而中断。返回-1errno被设置为EINTR。 // 即使在插入信号时设置了SA_RESTART也无效。man msgrcv就有说明。 // struct msgst msg_st; if( -1 msgrcv( nMsgID, (void*)msg_st, 1, 0, 0 ) ) { perror(msgrcv fail); } printf(done\n); exit(0); } 4. 总结 慢系统调用(slow system call)会被信号中断系统调用函数返回失败并且errno被置为EINTR错误描述为“Interrupted system call”。 处理方法有以下三种①人为重启被中断的系统调用②安装信号时设置 SA_RESTART属性③忽略信号让系统不产生信号中断。 有时我们需要捕获信号但又考虑到第②种方法的局限性设置 SA_RESTART属性对有的系统无效如msgrcv所以在编写代码时一定要“人为重启被中断的系统调用”。