-
tcdrain()
函数
< br>标准
C
语言
tcdrain(f
d)
等待直到所有写入
fd
引用的对象的输出都被传输
fcntl()
函数
#include
定义函数
int fcntl(int fd , int cmd);
int fcntl(int fd,int
cmd,long arg);
int
fcntl(int fd,int cmd,struct flock * lock);
fcntl()
用来操作
文件描述符
的一些特性。
参数
fd
代表欲设置的文件描述词。
参数
cmd
代表欲操作的指令。
有以下几种情况
:
F_DUPFD
用来查找大于或等于参数
arg
的最小且仍未使用的文件描述词,并且复制参数
fd
< br>的文
件描述词。执行成功则返回新复制的文件描述词。请参考
dup2()
。
F_GETFD
取得
close-
on-exec
旗标。
若此旗标的<
/p>
FD_CLOEXEC
位为
0
,代表在调用
exec()
相关函数时文件将不会
关闭。
F_SETFD
设置
close-
on-exec
旗标。该旗标以参数
arg
的
FD_CLOEXEC
位决定。
F_GETFL
取得文件描述词状态旗标,此旗标为
open
()的参数
flags
。
F_SETFL
设置文件描述词状
态旗标,参数
arg
为新旗标,但只允许
O_APPEND
、
O_NONBLOCK
< br>和
O_ASYNC
位的改变,其他位的改变将不受影响。
F_GETLK
取得文件锁定的状态。
F_SETLK
设置文件锁定的状态。
此时
flcok
结构的
l_type
值必须是
F_RDLCK
、
F_WRLCK
或
F_UNLCK
。如果无法建立
锁定,则返回
-1
,错误代码为
EACCES
或
EAGAIN
。<
/p>
F_SETLKW
与
F_SETLK
作用相同,
但是无法建立锁定时,
此调
用会一直等到锁定动作成功为止。
若在等待锁定的过程中被信号中断时,会立即返回
p>
-1
,错误代码为
EINTR
。
参数
lock
指针为
flock
结构指针,定义如下
struct flcok
{
short int l_type;
short int l_whence;
off_t l_start;
off_t l_len;
pid_t l_pid;
};
l_type
有三种状态
:
F_RDLCK
建立一个供读取用的锁定
F_WRLCK
建立一个供写入用的锁定
F_UNLCK
删除之前建立的锁定
l_whence
也有三种方式
:
SEEK_SET
以文件开头为锁定的起始位置。
SEEK_CUR
以目前文件读写位置为锁定的起始位置
SEEK_END
以文件结尾为锁定的起始位置。
返回值
成功则返回
< br>0
,若有错误则返回
-1
,错误
原因存于
errno.
ioctl()
函数
一、
ioctl
是设备驱动程序中对
设备的
I/O
通道进行管理的函数。
所谓对
I/O
通道进行管理,就是对设
备的一些特性进行控制,例如串口的传输波特率、马达的转速等
等。它的调用形式如下:
int ioctl(int fd, ind cmd,
…)
;
其中
fd
就是用户程序打开设备时使用
open
函数返回的文件标示符,
cmd
就是用户程序对设备的控制
命令,至于后面的省略号,那是一些补充参数,
一般最多一个,
有或没有是和
cmd
的意义相关的。
ioctl
函数是文件结构中的一个
属性分量
< br>,就是说如果你的驱动程序提供了对
ioctl
的支
p>
持,用户就可以在用户程序中使用
ioc
tl
函数控制设备的
I/O
通道。
p>
二、
ioctl
的必要性
如果不用
ioctl
的话,也可以实现
对设备
I/O
通道的控制,但那就是蛮拧了。例如,我们可以在
驱动程
序中实现
write
的时候
检查一下是否有特殊约定的数据流通过,
如
果有的话,
那么后面就跟着控制命令
(一
般在
socket
编程中常常这样做)
。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员自
己也会头昏眼
花的。
所以,我们就使用
ioctl
来实现控制的功能。要记住,用户程序所作的只是通过命令码告
诉驱动程序它
想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要
做的事情。
三、
ioctl
如何实现
这是一个很麻烦的问题,我是能省则省。要说清楚它,没有四五千字是不行的,所以我这
里是不可能
把它说得非常清楚了,不过如果有读者对用户程序怎么和驱动程序联系起来感
兴趣的话,可以看我前一阵
子写的《
write
的奥秘》
。读者只要把
write
换成
ioctl
,就知道用户程序的
ioctl
是怎么和驱动程序中的
ioctl
实现联系在一起的了。
我这里说一个大概思路,因为
我觉得《
Linux
设备驱动程序》这本书已经说的非常清楚了
,但是得花
一些时间来看。
在驱动程序中实现的
ioctl
函数体内,实际上是有一个
p>
switch{case}
结构,每一个
c
ase
对应一个命令
码,做出一些相应的操作。怎么实现这些操
作,这是每一个程序员自己的事情,因为设备都是特定的,这
里也没法说。
关键在于怎么样组织命令码,
因为在
ioctl
中
命令码是唯一联系用户程序命令和驱动
程序支持的途径。
命令码的组织是有一些讲究的,因为我们一定要做到命令和设备是一一
对应的,这样才不会将正确的命令
发给错误的设备,或者是把错误的命令发给正确的设备
,或者是把错误的命令发给错误的设备。这些错误
都会导致不可预料的事情发生,而当程
序员发现了这些奇怪的事情的时候,再来调试程序查找错误,那将
是非常困难的事情。<
/p>
所以在
Linux
< br>核心中是这样定义一个命令码的:
____________________________________
|
设备类型
|
序列号
|
方向
|
数据尺寸
|
|----------
-----|------------|---------|------------|
| 8 bit
|
8 bit
|2 bit
|8~14 bit|
|---------------|-------
-----|---------|------------|
这样一来,一个命令就变成了
一个整数形式的命令码。但是命令码非常的不直观,所以
Linux
< br>Kernel
中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者
是从命令码得到一些用户可以理解的
字符串以标明这个命令对应的设备类型、设备序列号
、数据传送方向和数据传输尺寸。
这些宏我就不在这里解释了,
具体的形式请读者察看
Linux
核心源代码中的文件里给出了
这些宏完整
的定义。这里我只多说一个地方,那就是
幻数
。幻数是一个字母,数据长度也是
8
,所以就用一个特定的
字母来标明设备类型,这
和用一个数字是一样的,只是更加利于记忆和理解。就是这样,再没有更复杂的
了。
p>
更多的说了也没有,读者还是看一
看源代码吧,推荐各位阅读《
Linux
设备驱动程序》所带
源代码中
的
short
一例,因为它比
较短小,功能比较简单,可以看明白
ioctl
的功能和细节。
四、
cmd
参数如何得出
这里确实要说一说,
cmd
参数在用户程序端由一些宏根据设备类型、序列号、传送方向、数
据尺寸等
生成,这个整数通过系统调用传递到内核中的驱动程序,再由驱动程序使用解码
宏从这个整数中得到设备
的类型、序列号、传送方向、数据尺寸等信息,然后通过
switch{case}
结构进行相应的操作。
要透彻理解,只能是通过阅读源代码,我这篇文章实际上只是一个引子。
Cmd
参数的组织还是比较复
杂的,我认为要搞熟它还
是得花不少时间的,但是这是值得的,驱动程序中最难的是对中断的理解。
五、小结
ioctl
其实没有什么很难
的东西需要理解,
关键是理解
cmd
命
令码是怎么在用户程序里生成并在驱动程
序里解析的,
程序员最
主要的工作量在
switch{case}
结构中,
因为对设备的
I/O
控制都是通过这一部分的代<
/p>
码实现的。
flock()
函数
当用户想使用函数
flock()
来锁定一个文件时,
可以使用两种不同的锁定类型:一个是共享锁定,另
一个是互斥锁定。如果用户采用共享
锁定,若干个进程可以对某个文件拥有一个共同的锁定;如果用户申
请了一个互斥锁定,
其他进程都无法对该文件进行锁定。因此,根据它们的原理,用户可以对执行
读取操
p>
作
的系统采用一个共享锁定,而对执行
写入
操作
的系统采用一个互斥锁定。
(摘自《
Linux
编程宝典》
)
那么我就有疑问了
:
1.
既然读取操作的时候是不会对文件内容进行改变的
,各个进程是不会相互冲突的,那么为什么要锁
定?锁定的意义何在?
< br>
2.
读取操作的时候使用共享锁定,也就是
说,其它进程也可以锁定,那么是不是进行写操作的进程也
可以锁定也可以写入呢?共享
锁定没有什么作用?
1.
2.
若干进程在读,他们不会互相影
响,但是,如果有进程写则会影响它们,所以这个共享锁是防
止有进程对文件写
第二个问题和第一个问题关联,共享锁和独占锁互斥。
只有当一个程序试图施加它自己的锁
时,锁才会起作用;而没有尝试对文件上
锁的程序仍然能够访问该文件。因此,锁只能在协
同工作的程序间起作用。
”(摘自《
GNU/Linux
编程指南》
)
3.
据程序试验结果:
当我对一个文件进行互斥锁定以后,
然后进行共享锁定时,
锁定没有成功——
这是因为互斥锁定
是排斥其他锁定的;
当我对一个文件进行共享锁定以后,
然后进
行互斥锁定,
锁定成功!——难道互斥锁定不排斥之前的共享锁定?
请解答。
4.
我错在先关闭了文件描述符,
当然共享锁定也就关闭了,
随之互斥锁定必然也就能够成功,
我
错了,不好意思。但是,
据我试验:如果一个进程对文件进行了互斥锁定,另外一个进程没有
对文件进行尝试锁定
操作而是直接对文件进行写操作,结果是可以写入!?
5.
6.
那么也就是说,
如果进程没有考虑到锁定的问题时,
还是会破坏我锁定想保持不变的文件?那
么这个锁定的意义是不是大打折扣
了?
呵呵!
这样的锁叫建议锁和建议一样
可听可不听
好像还有强制锁
不过我没用过
呵呵
APUE
上有介绍。
如果你和别的程序混用无法控制他们的话,
建议你
采用
QMAIL
一样的原子操作法,
不
需要锁。
UART
的
CTS
与
RTS
在
RS232
中本来
C
TS
与
RTS
有
明确的意义,
但自从贺氏
(
HAYES
)
推出了聪明猫
< br>(SmartModem)
后就有点混淆了。
在
RS232
中
RTS
与
CTS
是用来半双工模式下的方向切换;
HAYES
Modem
中的
RTS
,
CTS
是用来进
行硬件流控的。通常
UART
的
RTC
、
CTS
的含义指后者,即用来做硬流控的。
硬流控的
RTS
、
CTS
:
RTS
(
Require To Send
,
发送请求)为输出信号,用于指示本设备准备
好可接收;
CTS
(
Clear To Send
p>
,发送清除)为输入信号,有效时停止发送。假定
A
、
B
两设备通信,
A
设备的
RTS
连接<
/p>
B
设备的
CTS
;
A
设备的
C
TS
连接
B
设备
的
RTS
。
前一路信号控制
< br>B
设备的发
送,后一路信号控制
A
设备的发送。对
B
设备的发送(
p>
A
设备接收)来说,如果
A
设备接收缓冲快满的时
发出
RTS
信号(意思
通知
B
设备停止发送),
B
设备
通过
CTS
检测到该信号,停止发送
;一段时间后
A
设备接收缓冲有了空余,发出
< br>RTS
信号,指示
B
设备开始发送数据。
A
设备发(
B
设备接收)
类似。
上述功能也能在数据流中插入
Xoff
(特殊
字符)和
Xon
(另一个特殊字符)信号来实现。
A
设备一旦接收
到
B
设备发送过来的
Xoff
,立刻停止发
送;反之,如接收到
B
设备发送过来的
Xon
,则恢复发送数据给
B
设备。同理,
B
设备也类似
,从而实现收发双方的速度匹配。
半双工的方向切换:
RS232
中使用
DTR
(
Date Terminal Ready
,数据终端准
备)与
DSR
(
Data
Set Ready
,数据设备准备好)进行主流控,类似上
述的
RTS
与
CTS
。
对半双工的通信的
DTE
(
Date
Terminal Equipment
,数据终端设备)与<
/p>
DCE
(
Data circuit
Equipment
)来说,默认的方向是
DTE
接收,
DCE
发送。如果
DTE
要发送数据,必须发出
RTS
信号,请求发送数据。
DCE
收到后如
果
空闲则
发出
CTS
回
应
RTS
信
号,表示响应请求,这样通信方向
就变为
DTE->TCE
,同时
RTS
与
CTS
信号
必须一直保持。从这里可以看出,
CTS
,
TRS
虽然也有点流控的意思(如
CTS
没有发出,
DTE
也不能发
送数据),但主要是用来进行方向切
换的。
如果
UART
只有
RX
、
TX
两个信号,要流控的话只能是软流控;如果有
RX
,
TX
,
CTS
,
RTS
四
个
信号,
则多半是支持硬流控的
UAR
T
;
如果有
RX
,
TX
,
CTS
,
RTS
,
DTR
,
DSR
六个信号的话,
RS232
标准的可能性比较大。
顺便提一下:
DCD
(
Data Carrier
Detect
,
数据载波检测):<
/p>
DCE
向
DTE
指示,线路上检测到载波。
RI
(
Ring Indicator
,振铃指示):
DCE
向
DTE
指示,有呼叫接入。
linux
基础复习(
7
)串口应用开发
据通信的基本方式可分为并行通信与串行通信两种。
·
并行通信是指利用多条数据传输线
将一个资料的各位同时传送。它的特点是传输速度
快,适用于短距离通信,但要求传输速度较高的应用场合。
·
串行通信是指利用一条传输线将资
料一位位地顺序传送。特点是通信线路简单,利用
简单的线缆
就可实现通信,降低成本,适用于远距离通信,但传输速度慢的应用场合。
串口设置详解
本节主要讲解设置串口的主要方法。
如前所述,设置串口中最基本的包括波特率设置,校验位和停止位设置。串口的设置主要是设置
< br>struct termios
结构体的各
成员值,如下
所示:
#
include
struct termio
{
unsigned short c_iflag; /*
输入模式标志
*/
unsigned
short c_oflag; /*
输出模式标志
*/
unsigned short c_cflag; /*
控制模式标志
*/
unsigned short c_lflag;
/*
本地模式标志
*/
unsigned
char c_line; /* line discipline */
unsigned char c_cc[NCC];
/* control characters */
};
在这个结构中最为重要的是
c_cflag
,通过对它的赋值,
用户可以设置波特率、字符大小、
数据位、停止位、奇偶校验
位和硬件流控等。另外
c_iflag
和
c_cc
也是比较常用的标志。在
此主要对这
3
个成员进行详细说明。
c_cflag
支持的常量名称
CBAUD
波特率的位掩码
B0
0
波特率(放弃
DTR
)
B1800
1800
波特率
B2400
2400
波特率
B4800
4800
波特率
B9600
9600
波特率
B19200
19200
波特率
B38400
38400
波特率
B57600
57600
波特率
B115200
115200
波特率
EXTA
外部时钟率
EXTB
外部时钟率
CSIZE
数据位的位掩码
CS5
5
个数据位
CS6
6
个数据位
CS7
7
个数据位
CS8
8
个数据位
CSTOPB
2
个停止位(不设则是
1
个停止位)
CREAD
接收使能
PARENB
校验位使能
PARODD
使用奇校验而不使用偶校验
HUPCL
最后关闭时挂线(放弃
DTR
)
CLOCAL
本地连接(不改变端口所有者)
LOBLK
块作业控制输出
CNET_CTSRTS
硬件流控制使能
c_iflag
支持的常量名称
INPCK
奇偶校验使能
IGNPAR
忽略奇偶校验错误
PARMRK
奇偶校验错误掩码
ISTRIP
除去奇偶校验位
IXON
启动出口硬件流控
IXOFF
启动入口软件流控
IXANY
允许字符重新启动流控
IGNBRK
忽略中断情况
BRKINT
当发生中断时发送
SIGINT
信号
INLCR
将
NL
映射到
CR
IGNCR
忽略
CR
ICRNL
将
CR
映射到
NL
IUCLC
将高位情况映射到低位情况
IMAXBEL
当输入太长时回复
ECHO
c_cc
支持的常量名称
VINTR
中断控制,对应键为
CTRL+C
VQUIT
退出操作,对应键为
CRTL+Z
VERASE
删除操作,对应
键为
Backspace
(
BS
)
VKILL
删除行,对应键为
CTRL+U
VEOF
位于文件结尾,对应键为
CTRL+D
VEOL
位于行尾,对应键为
Carriage return
(
CR
)
VEOL2
位于第二行尾,对应键为
Line feed
< br>(
LF
)
VMIN
指定了最少读取的字符数
VTIME
指定了读取每个字符的等待时间
串口控制函数
Tcgetattr
取属性
(termios
结构
)
Tcsetattr
设置属性
(termios
结构
)
cfgetispeed
得到输入速度
Cfgetospeed
得到输出速度
Cfsetispeed
设置输入速度
Cfsetospeed
设置输出速度
Tcdrain
等待所有输出都被传输
tcflow
挂起传输或接收
tcflush
刷清未决输入和
/
或输出
Tcsendbreak
< br>送
BREAK
字符
tcgetpgrp
得到前台进程组
ID
tcsetpgrp
设置前台进程组
ID
完整的串口配置
模板,实用!把常用的选项在函数里面列出,可大大方便用户的调试使用
int set_opt(int fd,int nSpeed, int
nBits, char nEvent, int nStop)
{
struct
termios newtio,oldtio;
<
/p>
/*
保存测试现有串口参数设置,在这里如果串口号等出错,会有
相关的出错信息
*/
if ( tcgetattr( fd,&oldtio) != 0)
{
perror(
return -1;
}
bzero( &newtio, sizeof(
newtio ) );
/*
步骤一,设置字符大小
*/
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
/*
设置停止位
*/
switch( nBits
)
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;