原创 如何使用Unix系统的IO复用

2016-5-20 20:55 1022 23 3 分类: MCU/ 嵌入式 文集: Qt和Cpp
当要进行多个文件的同时读写时,为了避免等待,有以下方法可以选择:

多进程(提高了复杂性)
多线程(需要同步机制)
NONBLOCKING I/O(持续polling消耗了CPU时间)
异步I/O(难以移植,且进程只有拥有一个某类型的信号)
IO multiplexing

其中,IO multiplexing是最好的方法。

为了进行IO复用的实验,我使用了一个STM32F103RBT6开发板作为串口设备。
它的功能是,将自身的串口1设置为115200 8N1的波特率,并返回任何接收到的字符串。
字符串以'\r'作为结束:
比如在minicom下通过键盘发送“123456abc\r”,那么就会接收到:
R: 123456abc
如果连续键入'\r'(minicom默认以此字符作为Enter键的输入),则有:
R:
R:
R:

在主函数里,将串口进行初始化操作:
fd_dev = ::open(DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd_dev < 0) {
    cerr << "Error: Cannot open " << DEVICE << endl;
    return -1;
}
termios attr;
memset(&attr, 0, sizeof(attr));
attr.c_cflag = B115200 | HUPCL | CS8 | CREAD | CLOCAL;
attr.c_iflag = IGNPAR;
attr.c_cc[VMIN] = 1;
if (tcsetattr(fd_dev, TCSANOW, &attr) != 0) {
    cerr << "Error: Cannot set termios!" << endl;
    return -1;
}

然后重新起一个线程,让它每隔一秒发送一串数据:
void *ntid2_print(void *arg)
{
    string str;
    int count = 0, flag;
    while (1) {
        str = "<2> Write " + to_string(count++) + "th times." + "\r";
        cout << "=> " << str.c_str() << endl;
        flag = ::write(fd_dev, str.c_str(), str.size());
        if (flag < 0)
            cerr << "Error: Cannot write!" << endl;
        sleep(1);
    }
    return ((void *)0);
}
执行的结果是:
# ./UnixProg_01
=> <2> Write 0th times.
=> <2> Write 1th times.
=> <2> Write 2th times.
=> <2> Write 3th times.

这样,串口每隔1s就会收到1个字符串,而标准输入会接收键盘数据。
现在,我需要的功能是,主函数既能够读取串口的数据,也能够读取标准IO的,并将它们都打印到标准输出上。
这里需要使用IO复用的select函数:
fd_set rset;
char byte;
while (1) {
    FD_ZERO(&rset);
    FD_SET(fd_dev, &rset);
    FD_SET(STDIN_FILENO, &rset);
    select(fd_dev + 1, &rset, NULL, NULL, NULL);
    if (FD_ISSET(fd_dev, &rset)) {
        flag = ::read(fd_dev, &byte, 1);
        if (flag < 0) {
            cerr << "Error: Cannot read fd_dev!" << endl;
            return -1;
        }
        cout << byte << flush;
    }
    if (FD_ISSET(STDIN_FILENO, &rset)) {
        flag = ::read(STDIN_FILENO, &byte, 1);
        if (flag < 0) {
            cerr << "Error: Cannot read fd_dev!" << endl;
            return -1;
        }
        cout << byte << flush;
    }
}

这样,标准输出既会打印串口的数据,又会打印标准输入的内容。
另外,还需要注意的一点是,STDIN_FILENO的FD_SET在循环里面,这是因为:
http://stackoverflow.com/questions/7637765/why-fd-set-fd-zero-for-select-inside-of-loop

文章评论3条评论)

登录后参与讨论

curton 2019-4-30 19:06

Unix系统的IO复用

curton 2019-4-26 12:52

好资源 学习了

用户1678053 2016-5-30 09:08

看看
相关推荐阅读
DiracFatCat 2018-09-05 12:14
【博客大赛】卡尔曼滤波学习笔记(11)从位置估计速度
卡尔曼滤波器,不仅仅是一个低通滤波器,否则也不会持续发展50年。 示例:桑先生需要测试高速列车的性能。测试的目的是判断列车在直线上能否保持80m/s的速度。速度和位置每0.1秒测量一次,但是由于...
DiracFatCat 2018-08-31 19:32
【博客大赛】卡尔曼滤波学习笔记(10)一个简单的示例
《Kalman Filtering: Theory and Practice Using MATLAB》第三章,看不懂,暂时略过。《Kalman Filtering: Theory and Pract...
DiracFatCat 2018-07-19 15:09
对sed命令的练习
sed是流编辑器。它每次处理一个输入,因此很有效率。官方手册:https://www.gnu.org/software/sed/manual/sed.html学习Linux命令,当然要阅读官方手册,所...
DiracFatCat 2018-06-19 15:10
【博客大赛】卡尔曼滤波学习笔记(八)可观测性和可控制性 ...
可观测性是指,在给定模型的情况下,动力学系统的状态是否由它的输入输出唯一确定。可观测性是系统模型的特征。如果传感器矩阵H是可逆的,则本系统可观测,因为有:如果传感器矩阵H某些时候是不可逆的,则本系统仍...
DiracFatCat 2018-06-19 10:56
【博客大赛】卡尔曼滤波学习笔记(七)Z变换
如果我们仅仅对离散线性系统感兴趣,那么就使用下面这个表达式:如果u是常量,那么可以写成:为了简化表达式,我们可以将上面写成:离散线性时不变系统的Φ求解,可以使用Z变换。(* 由于本人已经忘记了Z变换的...
DiracFatCat 2018-06-19 10:54
【博客大赛】卡尔曼滤波学习笔记(六)拉普拉斯变换
对于线性时变/时不变系统,求解Φ(t)有多种方式,拉普拉斯变换是其中一种。(* 由于本人已经忘记了拉普拉斯变换的内容,因此本节待续。)...
我要评论
3
23
关闭 站长推荐上一条 /2 下一条