详细信息参考unix/linux系统编程手册第63章
在I/O多路复用中,进程是通过系统调用(select()或poll())来检查文件描述符上是否可以执行I/O操作。而在信号驱动I/O中,当文件描述符上可执行I/O操作时,进程请求内核为自己发送一个信号。之后进程就可以执行任何其他的任务直到I/O就绪为止,此时内核会发送信号给进程。要使用信号驱动I/O,程序需要按照如下步骤来执行。
1.为内核发送的通知信号安装一个信号处理例程。默认情况下,这个通知信号为SIGIO。
2.设定文件描述符的属主,也就是当文件描述符上可执行I/O时会接收到通知信号的进程或进程组。通常我们让调用进程成为属主。设定属主可通过fcntl()的F_SETOWN操作来完成:
fcntl(STDIN_FILENO,F_SETOWN,getpid()
3.通过设定O_NONBLOCK标志使能非阻塞I/O。
4.通过打开O_ASYNC标志使能信号驱动I/O。这可以和上一步合并为一个操作,因为它们都需要用到fcntl()的F_SETFL操作
flags = fcntl(STDIN_FILENO,F_GETFL); fcntl(STDIN_FILENO,F_SETFL,flags | O_ASYNC | O_NONBLOCK)
5.调用进程现在可以执行其他的任务了。当I/O操作就绪时,内核为进程发送一个信号,然后调用在第1步中安装好的信号处理例程。
6.信号驱动I/O提供的是边缘触发通知。这表示一旦进程被通知I/O就绪,它就应该尽可能多地执行I/O(例如尽可能多地读取字节)。假设文件描述符是非阻塞式的,这表示需要在循环中执行I/O系统调用直到失败为止,此时错误码为EAGAIN或EWOULDBLOCK。
测试代码一:
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif
char buf[1024];
static void sig_func(int sig){
DEBUG_INFO("signal: %d\n", sig);
// scanf("%s",buf);
int res = 0;
do{
memset(buf,0,1024);
res = read(0,buf,sizeof(buf));
DEBUG_INFO("res = %d,buf = %s",res,buf);
}while(res > 0);
int err = errno;
if(err == EAGAIN){
DEBUG_INFO("EAGAIN = %d\n",errno);
}else if(err == EWOULDBLOCK){
DEBUG_INFO("EWOULDBLOCK = %d\n",errno);
}
DEBUG_INFO("wait input:");
}
int main(int argc, char **argv)
{
int flags,j,cnt;
char ch;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = sig_func;
if(sigaction(SIGIO,&sa,NULL)){
perror("sigaction");
exit(-1);
}
if(fcntl(STDIN_FILENO,F_SETOWN,getpid()) == -1){
perror("fcntl");
exit(-1);
}
flags = fcntl(STDIN_FILENO,F_GETFL);
if(fcntl(STDIN_FILENO,F_SETFL,flags | O_ASYNC | O_NONBLOCK) == -1){
perror("fcntl set");
exit(-1);
}
DEBUG_INFO("wait input:");
while (1)
{
/* code */
sleep(1);
}
return 0;
}
实验结果:
/big/modbus/libmodbus/src_mymodbus/sigio1.c:main:59 -- wait input:
555
/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:19 -- signal: 29
/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:25 -- res = 4,buf = 555
/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:25 -- res = -1,buf =
/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:29 -- EAGAIN = 11
/big/modbus/libmodbus/src_mymodbus/sigio1.c:sig_func:34 -- wait input:
实验解析:
输入555,sig_func得到信号SIGIO,在此函数中读取输入的数据,因为SIGIO是边沿触发,一旦得到信号,要尽可能多的读取数据,所以直到读完所有的数据,才退出信号处理代码。读取完毕后,的错误码是11,也就是EAGAIN。
测试二:
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <ctype.h>
#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif
static volatile sig_atomic_t gotsigio = 0;
static void sig_func(int sig){
gotsigio = 1;
}
void sleep_ms(int ms){
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = ms * 1000;
select(0,NULL,NULL,NULL,&tv);
}
int ttySetCbreak(int fd, struct termios *prevTermios)
{
struct termios t;
if (tcgetattr(fd, &t) == -1)
return -1;
if (prevTermios != NULL)
*prevTermios = t;
t.c_lflag &= ~(ICANON | ECHO);
t.c_lflag |= ISIG;
t.c_iflag &= ~ICRNL;
t.c_cc[VMIN] = 1; /* Character-at-a-time input */
t.c_cc[VTIME] = 0; /* with blocking */
if (tcsetattr(fd, TCSAFLUSH, &t) == -1)
return -1;
return 0;
}
int main(int argc, char **argv)
{
int flags,j;
char ch;
int res = 0;
struct termios os;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = sig_func;
if(sigaction(SIGIO,&sa,NULL)){
perror("sigaction");
exit(-1);
}
if(fcntl(STDIN_FILENO,F_SETOWN,getpid()) == -1){
perror("fcntl");
exit(-1);
}
flags = fcntl(STDIN_FILENO,F_GETFL);
if(fcntl(STDIN_FILENO,F_SETFL,flags | O_ASYNC | O_NONBLOCK) == -1){
perror("fcntl set");
exit(-1);
}
if(ttySetCbreak(STDIN_FILENO,&os) == -1){
perror("ttySetCbreak");
exit(-1);
}
DEBUG_INFO("wait input:");
while (1)
{
/* code */
sleep_ms(100);
if(gotsigio){
do{
res = read(STDIN_FILENO,&ch,1);
if(res > 0)
printf("ch=%c\n",ch);
}
while(res > 0);
gotsigio = 0;
if(ch == '#'){
break;
}
}
}
DEBUG_INFO("bye bye");
return 0;
}
测试结果:文章来源:https://www.toymoban.com/news/detail-507427.html
ch=a
ch=b
ch=c
ch=d
ch=e
ch=f
ch=#
/big/modbus/libmodbus/src_mymodbus/sigio2.c:main:102 -- bye bye
小结
详细信息参考unix/linux系统编程手册第63章文章来源地址https://www.toymoban.com/news/detail-507427.html
到了这里,关于Unix/Linux系统编程:信号驱动IO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!