LinuxC—高级IO

高级IO

1 非阻塞IO/有限状态机编程

1.1 基本概念

定义

有限状态机(Finite State Machine) 缩写为 FSM,状态机有 3 个组成部分:状态、事件、动作。

  • 状态:所有可能存在的状态。包括当前状态和条件满足后要迁移的状态。
  • 事件:也称为转移条件,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。
  • 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是* 必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

作用

用来解决复杂流程的问题:

  • 简单流程:一个程序的自然流程是结构化
  • 复杂流程:一个程序的自然流程不是结构化的

自然流程即解决问题的直接思路

1.2 有限状态机实现文件复制

分析

实现的功能就是从文件1中复制文件到文件2或者从文件2中复制文件到文件1,有限状态机共有4个状态,分别是read读态,write写态,exception出错态和terminate结束态。

FSM模型图如下:

在这里插入图片描述

实现

#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUFSIZE 1024
enum {
   
    STATE_R = 1, //读态
    STATE_W = 2, //写态
    STATE_EX = 3, //异常态
    STATE_T = 4 //退出态
};

struct fsm_st {
   
    int state;//当前状态机的状态
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;
};

//状态机推动函数
static void fsm_driver(struct fsm_st *fsm) {
   
    int ret;
    switch (fsm->state) {
   
        case STATE_R:
            fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);
            if (fsm->len == 0) fsm->state = STATE_T; // 读完切换到结束态
            else if (fsm->state < 0) {
   
                if (errno == EAGAIN) fsm->state = STATE_R; // 假错接着读
                else {
   
                    fsm->state = STATE_EX; // 真错切换到异常态
                    fsm->errstr = "read()";
                }
            } else {
   
                fsm->pos = 0;
                fsm->state = STATE_W; // 读成功切换为写态
            }
            break;
        case STATE_W:
            ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
            if (ret < 0) {
   
                if (errno == EAGAIN) fsm->state = STATE_W;
                else {
   
                    fsm->errstr = "write()";
                    fsm->state = STATE_EX;
                }
            } else {
   
                fsm->pos += ret;
                fsm->len -= ret;
                if (ret == 0) fsm->state = STATE_R;
                else fsm->state = STATE_W;
            }
            break;
        case STATE_EX:
            perror(fsm->errstr);
            fsm->state = STATE_T;
            break;
        case STATE_T:
            break;
        default:
            abort();
    }
}

static void relay(int fd1, int fd2) {
   
    int fd1_save, fd2_save;
    struct fsm_st fsm12, fsm21;

    // 在该模块中保证文件是以非阻塞方式打开(获取原来的文件描述符操作属性并加上非阻塞的属性)
    fd1_save = fcntl(fd1, F_GETFL);
    fcntl(fd1, F_SETFL, fd1_save | O_NONBLOCK);
    fd2_save = fcntl(fd2, F_GETFL);
    fcntl(fd2, F_SETFL, fd2_save | O_NONBLOCK);
    // 状态机初始化
    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;
    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

    while (fsm12.state != STATE_T || fsm21.state != STATE_T) {
   
        fsm_driver(&fsm12);
        fsm_driver(&fsm21);
    }

    //恢复文件描述符之前的状态
    fcntl(fd1, F_SETFL, fd1_save);
    fcntl(fd2, F_SETFL, fd2_save);

}

int main(int argc, char **argv) {
   

    // 模拟用户打开两个设备的操作
    int fd1, fd2;
    fd1 = open(TTY1, O_RDWR);
    if (fd1 < 0) {
   
        perror("open()");
        exit(1);
    }
    fd2 = open(TTY2, O_RDWR | O_NONBLOCK);
    if (fd2 < 0) {
   
        close(fd1);
        perror("open()");
        exit(1);
    }
    // 调用数据中继函数
    relay(fd1, fd2);

    close(fd1);
    close(fd2);
    exit(0);
}

1.3 中继引擎

头文件

#ifndef LINUX_RELAYER_H
#define LINUX_RELAYER_H

#include <stdint-gcc.h>
#include <time.h>

#define REL_JOBMAX 10000
enum {
   
    STATE_RUNNING = 1,
    STATE_CANCELED,
    STATE_OVER
};


/**
 * 表示任务状态的结构体,这个结构体是我们希望用户看到的,当我们实际操作的时候可以不是这个结构体
 * 即实现结构体部分的隐藏和封装
 */
struct rel_stat_st {
   
    int state;
    int fd1;
    int fd2;
    int64_t count12, count21; //通讯的字节数
    time_t start, end; //记录任务的起始时间
};

/**
 * 添加任务
 * @param fd1
 * @param fd2
 * @return >=0 表示成功,返回当前任务id;-EINVAL 表示失败参数非法; -ENOSPC 表示失败,任务数组满
 * -ENOMEM 表示失败,内存分配有误
 */
int rel_addjob(int fd1, int fd2);

/**
 * 取消任务
 * @param id
 * @return =0 表示指定任务已经成功取;-EINVAL 表示失败,参数非法; -EBUSY 失败,任务被重复取消
 */
int rel_canceljob(int id);

/**
 * 给任务收尸
 * @param id 任务id
 * @param old 被收尸任务的状态
 * @return =0 成功,指定任务已终止并返回状态; -EINVAL 失败,参数非法
 */
int rel_waitjob(int id, struct rel_stat_st *old);

/**
 * 返回id任务的状态
 * @param id 任务id
 * @param stat 任务状态
 * @return 0 成功,指定任务状态已返回; -EINVAL 失败,参数非法
 */
int rel_statjob(int id, struct  rel_stat_st *stat);

#endif //LINUX_RELAYER_H

实现

#include <malloc.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "relayer.h"

#define 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值