Vxworks学习(二)_中断、POSIX与Wind比较、I/O系统
声明本学习记录为笔者的学习记录笔记,其中大量借鉴了多本已出版书籍与网络资料,并会在文章末尾注明出处,同时笔者无虚拟机镜像。
一、中断
中断为实时操作系统的核心。举个例子,一辆带有实时操作系统的自动驾驶的汽车在车头安装了一个距离传感器(现实中可能是用其他的传感器)用于检测前方是否有障碍物以便随时准备改变行驶线路或停车,当检测到的距离小于危险距离时,此时该传感器发给系统一个硬件中断信号,操作系统陷入内核处理该中断,并执行响应的中断服务程序,该程序可设定下一步要进行的策略等。
注意到,这个过程中有一个很重要的点就是时间,如果是软实时系统,该系统可能处理该中断不及时,车里的人岂不是要GG,此时体现硬实时操作系统的重要性。
中断处理函数:
- 中断处理程序在所有任务上下文之外的一个特殊上下文内执行;
- 中断处理不涉及到任务上下文切换;
- 系统硬件中断处理程序可替代Vx中断程序;
- 所有中断服务程序使用相同的中断堆栈;
- 中断服务程序存在很多函数限制;
- 中断程序没有任务控制块;
- 中断程序禁止调用导致调用者堵塞的函数;
- 不能调用malloc与free;
二、POSIX 与 Wind
POSIX特点不在此处赘述,请自行查阅。
Wind内核包括了POSIX接口以及专门用于Vx设计的接口。
注意 许多操作系统执行内存分页与交换功能,即将内存块书籍复制到磁盘,或从磁盘复制回内存,同时允许使用比物理内存更大的虚拟内存,但执行时会存在严重的、不可预测的演示,实时系统一般不使用分页和交换功能,Wind内核从不使用它们。
2.1 POSIX和Wind调度方法比较
需要注意的是,Wind调度中没有进程与线程的概念,均是基于任务的。
POSIX和Wind调度函数的区别如下:
(1) POSIX调度基于进程,而Wind调度基于任务。
(2) POSIX标准使用了FIFO 调度术语,VxWorks 文档使用了基于优先级的抢占式调度。两者仅表达不同,都使用了相同的基于优先级的策略。
(3) POSIX在进程到进程的基础上应用调度算法;Wind的调度算法应用于整个系统,即所有任务既可使用轮转调度,也可使用基于优先级的抢占调度。
(4) POSIX 的优先级编号方法与 Wind相反。在 POSIX 中编号越大,其优先级也越高,但是在Wind中编号越小,其优先级越高,0编号是最高的优先级。相应的,使用POSIX调度库schedPxLib与其他所有VxWorks组件使用的优先级不匹配。用户可以不使用默认值,设置全局变量posixPriorityNumbering 为 FALSE从而解决这一问题。如果使用该设置schedPxLib使用Wind的编号策略(小编号对应高优先级),并且它的优先级编号与VxWorks其他组件使用的Wind优先级相匹配。
2.2 POSIX和Wind信号量比较
POSIX信号量属于计数信号量,即它们会跟踪信号量被释放的次数。除下列 Wind信号量提供的附加特征外,Wind信号量机制与POSIX中信号量机制类似;而且下列特性起重要作用时,Wind信号量性能更加优越。
- 优先级继承
- 任务删除安全性
- 对于单任务多次获取信号量的能力
- 互斥信号量的所有权
- 信号量超时
- 队列机制选择
2.3 POSIX和Wind消息队列比较
用消息队列通信:
任务可以调用==mq_notify()==向空状态队列发出请求,在有消息到来时进行通告,以避免任务阻塞。
三、输入/输出系统
Vx的I/O系统包括 基本I/O系统和 缓冲I/O系统。
整体结构
Vxworks同Linux一样,均是通过操作文件描述符来进行读写。
3.1 基本I/O接口
它在标准C语言库中与I/0原语兼容。
0 = 标准输入设备
1 = 标准输出设备
2 = 标准错误输出设备
** 全局重定向操作**
ioGlobalStdSet(1, fileFd);
//1 : 标准输出设备 (被重定向)
//fileFd : (重定向目标文件描述符)(已打开的文件)
任务的重定向操作
ioTaskStdSet(0, 1, fildFd);
//@param1 : 任务ID
//@param2 : 标准输出设备 (被重定向)
//@param3 : (重定向目标文件描述符)(已打开的文件)
打开和关闭文件
fd = open("name", flags, mode);
//@param2 : O_RDONLY \ O_WRONLY \ O_RDWR \ O_CREAT \ O_TRUNC
//@param3 : 必选的,无效时填 0
close(fd);
//新建和删除
fd = create("name", flag);
delete("name");
读写操作
nBytes = read(fd, &buffer, maxBytes);
@return : 实际读取的字节数, 若抵达文件尾部则返回 0。
actualBytes = write(fd, &buffer, nBytes);
**基于多文件描述符的挂起操作 select() **
需要包含头文件 selectLib.h
此函数即相关具体内容,感兴趣的同学可以去看《C++服务器开发精髓》,讲的非常全面,对select,poll,epoll等等的使用都进行了详细的介绍
select函数的主要作用,个人理解为,当模型为 客户端-服务器时,服务器要与多个远程客户端保持一个通信,这是靠socket套接字实现的,即通过监听相应的文件描述符,对于客户端的请求,服务器需要有一个较好的响应速度。当服务器等待某个客户端的请求或响应时,会处于一个挂起状态,即停止接收其他客户端的请求,这将导致一定程度的响应延迟。
select函数就是可以同时监视所有socktet和pipe传来的请求。
示例程序
#include <vxWorks.h>
#include <selectLib.h>
#include <fcntl.h>
#define MAX_FDS 2
#define MAX_DATA 1024
#define PIPEHI "/pipe/highPriority"
#define PIPENORM "/pipe/normalPriority"
STATUS selServer(void) {
struct fd_set readFds;
int fds[MAX_FDS];
int width;
int i;
char buffer[MAX_DATA];
}
if ((fds[0] = open(PIPEHI, O_RDONLY, 0)) == ERROR) {
close(fds[0]);
return ERROR;
}
if ((fds[1] = open(PIPENORM, O_RDONLY, 0)) == ERROR) {
close(fds[0]);
close(fds[1]);
return ERROR;
}
FOREVER {
FD_ZERO(&readFds);
FD_SET(fds[0], &readFds);
FD_SET(fds[1], &readFds);
width = (fds[0] > fds[1]) ? fds[0] : fds[1];
width++;
if (select(width, &readFds, NULL, NULL, NULL) == ERROR) {
close(fds[0]);
close(fds[1]);
return ERROR;
}
for (i = 0; i < MAX_FDS; i++) {
if (FD_ISSET(fds[i], &readFds)) {
read(fds[i], buffer, MAX_DATA);
printf("SELSERVER reading from %s : %s\n", (i == 0) ? PIPEHI : PIPENUM, buffer);
}
}
}
3.2 缓冲I/O : stdio
普通I/O函数(read)在读取每一个字符时均需要调用,均产生系统开销。
为了使I/O操作更加有效,stdio包提供了缓冲机制,由包中的宏自动控制,即使用fopen函数。
fp = fopen("usr/foo", "r");
@return : 文件指针
//使用 fread(), fwrite()进行批量读写。
//getc(), putc() 一次只读入或写入一个字符。
3.3 异步输入/输出操作
异步输入/输出操作(AIO)能够在执行普通的内部处理时,同时执行输入/输出操作。当I/O操作与具体的任务间逻辑上是独立的时候,异步输入/输出操作使得用户能够将IO操作与具体的任务分离。
==优点: == 极大提高系统处理效率。减少了一些由同步输入/输出操作引起的不必要的任务拥塞现象。
==注意: ==开启该功能需要在内核中添加“INCLUDE_POSIX_AIO”和“INCLUDE_POSIX_AIO_SYSDRV”组件。
3.4 Vx系统中的设备
tty设备分为原始模式与线性模式。
原始模式即每一个字符一旦从输入设备输入,接收方立即获得该字符。
线性模式即输入字符先进入缓冲区,当输入NEWLINE字符后,整行字符同时导入唤醒存储区。
3.5 内部结构
VxWorks操作系统与其他大多数操作系统的区别在于对用户IO请求的响应方式上。VxWorks 操作系统将这个过程分为与设备无关的IO系统和设备驱动程序本身。