在C语言编程中,有限状态机是一种编程思想。在编程过程中,也可以把程序分为几个状态,比如读数据状态、写数据状态、异常状态、超时状态等,用程序实现几个状态间的互相转换。使用有限状态机的思想编程,可以使自己的程序变得更加灵活,足够应对程序编写出来后更改需求的情况。一般只要该需求,整个程序都要重新写,若用上有限状态机的思想就可以很大程度上地应对该需求的情况。在程序中,程序可以大概按下图的模式去编写:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#define TTY1 "/dev/tty11"//宏定义tty11文件路径
#define TTY2 "/dev/tty12"//宏定义tty12文件路径
#define BUFSIZE 10
enum//定义一个枚举,表示程序运行的各种状态
{
STATUS_R=1,//表示读态STATUS_R=1
STATUS_W,//表示写态STATUS_W=2
STATUS_AUTO,//用于区分读写状态和异常停止状态
STATUS_E,//表示异常态STATUS_E=3
STATUS_T//表示停止态STATUS_T=4
};
typedef struct//定义一个结构体,用于文件及其状态的切换
{
int sfd;//源文件描述符,始终从这里面读数据
int dfd;//目标文件描述符,始终从这里面写数据
int stat;//用于保存程序状态,其值为枚举的四个状态值
ssize_t res;//读数据的字节大小,定义在结构体中才能有效地实现跨函数读写操作
char buf[BUFSIZE];//读数据存放的内存空间,用于保存读取到的数据
char *errstr;//存放错误提示文本
}stat_t;
/*relay_driver函数:负责程序的各个状态要做的事情
实现一个有限状态机*/
void relay_driver(stat_t *fms)
{
ssize_t writeres;
switch(fms->stat)//判断程序状态
{
case STATUS_R://读态
fms->res=read(fms->sfd,fms->buf,BUFSIZE);
if(fms->res<0)
{
if(errno==EAGAIN)
fms->stat=STATUS_R;//错误为非阻塞,继续保持读数据状态,要继续读取数据
else
{
fms->errstr="read()";//保存错误原因
fms->stat=STATUS_E;//程序状态换成异常态
}
}
else if(fms->res==0)
{
fms->stat=STATUS_R;//没有数据可读,就继续保持读态
}
else
fms->stat=STATUS_W;//读取到一定字节的数据,则立刻转换为写态,向另一设备写数据
break;
case STATUS_W://写态
if((writeres=write(fms->dfd,fms->buf,fms->res))<0)
{
if(errno==EAGAIN)
fms->stat=STATUS_W;//错误为非阻塞,继续保持写数据状态,要继续向另一外设写数据
else
{
fms->errstr="write()";//保存错误原因
fms->stat=STATUS_E;//程序状态换成异常态
}
}
if(writeres==0)
fms->stat=STATUS_R;//没有数据可写,表示数据写完,则切换到读态去读取数据
else
fms->stat=STATUS_R;//已经写入一定字节的数据,则切换到读态去读取数据
break;
case STATUS_E://异常态
perror(fms->errstr);//报告异常错误原因
fms->stat=STATUS_T;//出现异常,则切换到停止态
break;
case STATUS_T://停止态
break;
}
}
/*max函数:比较两个数的大小,用于select函数*/
static int max(int a,int b)
{
return a>b?a:b;
}
void relay(int fd1,int fd2)
{
int save_fd1,save_fd2;//保存两个文件被改变前的状态
stat_t fms12,fms21;//两个外部设备
char buf1[32]="----------TTY11----------\n";
char buf2[32]="----------TTY12----------\n";
fd_set rfds,wfds;
save_fd1=fcntl(fd1,F_GETFL);//获取文件描述符对应文件被打开的状态
save_fd2=fcntl(fd2,F_GETFL);
fcntl(fd1,F_SETFL,save_fd1|O_NONBLOCK);//设置追加文件为非阻塞
fcntl(fd2,F_SETFL,save_fd2|O_NONBLOCK);
write(fd1,buf1,sizeof(buf1));
write(fd2,buf2,sizeof(buf2));
/*初始化两个外设,确定两个文件的源文件和目标文件,确定两个文件的初始化状态*/
fms12.sfd=fd1;
fms12.dfd=fd2;
fms12.stat=STATUS_R;
fms21.sfd=fd2;
fms21.dfd=fd1;
fms21.stat=STATUS_R;
/*只要两个文件的状态不是停止态,就一直循环(轮询)*/
while(fms12.stat!=STATUS_T||fms21.stat!=STATUS_T)
{
FD_ZERO(&rfds);//首先清空读集合
FD_ZERO(&wfds);//清空写集合
/*根据状态决定相应的文件描述符加入到哪一个集合。
对于即将要向某个文件描述符写数据,
那就将这个文件描述符加入到写集合中,其余的同理*/
if(fms12.stat==STATUS_R)
FD_SET(fms12.sfd,&rfds);
if(fms12.stat==STATUS_W)
FD_SET(fms12.dfd,&wfds);
if(fms21.stat==STATUS_R)
FD_SET(fms21.sfd,&rfds);
if(fms21.stat==STATUS_W)
FD_SET(fms21.dfd,&wfds);
if(select(max(fd1,fd2)+1,&rfds,&wfds,NULL,NULL)<0)
{
if(errno==EINTR)
continue;//如果被信号打断,只能重新初始化集合
perror("select()");
exit(1);
}
if(FD_ISSET(fms12.sfd,&rfds)||FD_ISSET(fms12.dfd,&wfds)||fms12.stat>STATUS_AUTO)
relay_driver(&fms12);
if(FD_ISSET(fms21.sfd,&rfds)||FD_ISSET(fms21.dfd,&wfds)||fms21.stat>STATUS_AUTO)
relay_driver(&fms21);
}
fcntl(fd1,F_SETFL,save_fd1);//最后要还原文件进入当前函数时的状态
fcntl(fd2,F_SETFL,save_fd2);
}
int main(void)
{
int fd1,fd2;
fd1=open(TTY1,O_RDWR|O_NONBLOCK);
if(fd1<0)
{
perror("open()");
exit(1);
}
fd2=open(TTY2,O_RDWR|O_NONBLOCK);
if(fd2<0)
{
perror("open()");
exit(1);
}
relay(fd1,fd2);
close(fd1);
close(fd2);
exit(0);
}
转载自原文链接, 如需删除请联系管理员。
原文链接:有限状态机的编程思想,转载请注明来源!