Linux串口设置参数
在Linux环境下,串口名从ttyS0开始依次是ttyS1、ttyS2等。在本程序中,使用ttyS0作为通信串口。在打开ttyS0的时候,选项 O_NOCTTY 表示不能把本串口当成控制终端,否则用户的键盘输入信息将影响程序的执行; O_NDELAY表示打开串口的时候,程序并不关心另一端 的串口是否在使用中。在Linux中,打开串口设备和打开普通文件一样,使用的是open()系统调用。比如我么打开串口设备1也就是COM1,只需要:
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY );
打开的串口设备有很多设置选项。本文中使用int setup_com(int fd)设置。在系统头文件<termios.h>中 定义了终端控制结构struct termios,tcgetattr()和tcsetattr()两个系统函数获得和设置这些属性。结构 struct termios中的域描述的主要属性包括:
c_cflag : 控制选项
c_lflag : 线选项
c_iflag : 输入选项
c_oflag :输出选项
c_cc :控制字符
c_ispeed :输入数据波特率
c_ospeed :输出数据波特率
如果要设置某个选项,那么就使用"|="运算,如果关闭某个选项就使用"&="和"~"运算。本文使用的各个选项的意义定义如下:
c_cflag: CLOCAL 本地模式,不改变端口的所有者
CREAD 表示使能数据接收器
PARENB 表示偶校验
PARODD 表示奇校验
CSTOPB 使用两个停止位
CSIZE 对数据的bit使用掩码
CS8 数据宽度是8bit
c_lflag: ICANON 使能规范输入,否则使用原始数据(本文使用)
ECHO 回送(echo)输入数据
ECHOE 回送擦除字符
ISIG 使能SIGINTR,SIGSUSP, SIGDSUSP和 SIGQUIT 信号
c_iflag: IXON 使能输出软件控制
IXOFF 使能输入软件控制
IXANY 允许任何字符再次开启数据流
INLCR 把字符NL(0A)映射到CR(0D)
IGNCR 忽略字符CR(0D)
ICRNL 把CR(0D)映射成字符NR(0A)
c_oflag: OPOST 输出后处理,如果不设置表示原始数据(本文使用原始数据)
c_cc[VMIN]: 最少可读数据
c_cc[VTIME]: 等待数据时间(10秒的倍数)
根据以上设置的定义,串口端口设置函数setup_com()定义如下:
int setup_com(int fd){
struct termios options;
tcgetattr(fd, &options);
/* Set the baud rates to 38400...*/
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
/* Enable the receiver and set local mode...*/
options.c_cflag |= (CLOCAL | CREAD);
/* Set c_cflag options.*/
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
/* Set c_iflag input options */
options.c_iflag &=~(IXON | IXOFF | IXANY);
options.c_iflag &=~(INLCR | IGNCR | ICRNL);
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* Set c_oflag output options */
options.c_oflag &= ~OPOST;
/* Set the timeout options */
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
tcsetattr(fd, TCSANOW, &options);
return 1;
}
6.7.2 设置串口通信参数
串口通信参数指的是波特率、数据位、奇偶校验位和停止位。对串口实现控制的时候同样要用到termio结构体。下面将结合具体的代码说明如何设置这些参数。
1.波特率设置
获得端口波特率信息是通过cfgetispeed函数和cfgetospeed函数来实现的。cfgetispeed函数用于获得结构体 termios_p中的输入波特率信息,而cfgetospeed函数用于获得结构体termios_p 中的输出波特率信息。这两个函数的具体信息如表 6.9所示。
表6.9 cfgetispeed函数和cfgetospeed函数
头文件
<termios.h>
<unistd.h>
函数形式
speed_t cfgetispeed(const struct termios *termios_p);
speed_t cfgetospeed(const struct termios *termios_p);
返回值
成功
失败
是否设置errno
返回termios_p结构中的输入/输出端口的波特率
?1
是
cfsetispeed函数和cfsetospeed函数用于设置端口的输入/输出波特率。一般情况下,输入和输出波特率是相等的。cfsetispeed函数和cfsetospeed函数的函数声明信息如表6.10所示。
表6.10 cfsetispeed函数和cfsetospeed函数
头文件
<termios.h>
<unistd.h>
函数形式
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);
返回值
成功
失败
是否设置errno
返回termios_p结构中的输入/输出端口的波特率
?1
是
cfsetispeed函数和cfsetospeed函数会修改结构体termios_p中的波特率信息,其中参数speed可以使用表6.11中所列出的宏。
表6.11 speed参数常用波特率信息
宏 定 义
波特率(单位:bit/s)
宏 定 义
波特率(单位:bit/s)
B0
0
B1800
1800
B50
50
B2400
2400
B75
75
B4800
4800
B110
110
B9600
9600
B134
134
B19200
19200
B150
150
B38400
38400
B200
200
B57600
57600
B300
300
B115200
115200
B600
600
B230400
230400
B1200
1200
使用cfsetispeed函数和cfsetospeed函数进行串口波特率设置具体代码如下所示:
#include <stdio.h> //头文件定义
#include <unistd.h>
#include < termios.h >
……
struct termios opt; /*定义指向termios 结构类型的指针opt*/
……
//获得串口指向termios结构的指针
tcgetattr(fd, &Opt);
cfsetispeed(&opt,B9600 ); /*指定输入波特率,9600bps*/
cfsetospeed(&opt,B9600);/*指定输出波特率,9600bps*/
//将修改后的termios数据设置到串口中
tcsetattr(fd,TCANOW,&Opt);
……
2.数据位
数据位指的是每字节中实际数据所占的比特数。要修改数据位可以通过修改termios结构体中c_cflag成员来实现。CS5、CS6、CS7和CS8分别表示数据位为5、6、7和8。值得注意的是,在设置数据位时,必须先使用CSIZE做位屏蔽。具体设置代码如下:
#include <stdio.h> //头文件定义
#include <unistd.h>
#include < termios.h >
……
struct termios opt; /*定义指向termios 结构类型的指针opt*/
.......
//获得串口指向termios结构的指针
tcgetattr(fd, &Opt);
…
//屏蔽其他标志
Opt.c_cflag&=~CSIZE;
//将数据位修改为8bit
Opt.c_cflag |=CS8;
…
//将修改后的termios数据设置到串口中
tcsetattr(fd,TCANOW,&Opt);
……
3.奇偶校验位
奇偶校验可以选择偶校验、奇校验、空格等方式,也可以不使用校验。如果要设置为偶校验的话,首先要将termios结构体中c_cflag设置 PARENB标志,并清除PARODD标志。如果要设置奇校验,要同时设置termios结构体中c_cflag设置PARENB标志和PARODD标 志。如果不想使用任何校验的话,清除termios结构体中c_cflag的PARENB位。表6.12所示为设置奇偶校验的具体方法。
表6.12 设置奇偶校验位
设 置
具 体 代 码
无校验
opt.c_cflag &= ~PARENB;
奇校验
opt.c_cflag |= (PARODD | PARENB);
偶校验
opt.c_cflag &= ~ PARENB;
opt.c_cflag &= ~PARODD;
空格
opt.c_cflag &= ~PARENB;
opt.c_cflag &= ~CSTOPB;
下面给出将串口通信的奇偶校验设置为偶校验的例子,具体代码如下:
#include <stdio.h> //头文件定义
#include <unistd.h>
#include < termios.h >
……
struct termios opt; /*定义指向termios 结构类型的指针opt*/
……
//获得串口指向termios结构的指针
tcgetattr(fd, &Opt);
…
opt.c_cflag &= ~ PARENB;
opt.c_cflag &= ~PARODD;
…
//将修改后的termios数据设置到串口中
tcsetattr(fd,TCANOW,&Opt);
……
4.数据流控制
数据流控制指是使用何种方法来标志数据传输的开始和结束。可以选择不使用数据流控制、使用硬件进行流控制和使用软件进行流控制。数据流控制设置如表6.13所示。
表6.13 数据流控制设置
设 置
具 体 代 码
不使用数据流控制
opt.c_cflag &= ~CRTSCTS
硬件
opt.c_cflag |= CRTSCTS
软件
opt.c_cflag | = IXON|IXOFF|IXANY
由于使用硬件流控制需要相应连接的电缆,常用的流控制方法还是使用软件进行流控制。下面给出了设置不使用数据流控制的相关代码:
#include <stdio.h> //头文件定义
#include <unistd.h>
#include < termios.h >
……
struct termios opt; /*定义指向termios 结构类型的指针opt*/
……
//获得串口指向termios结构的指针
tcgetattr(fd, &opt);
…
opt.c_cflag &= ~CRTSCTS…
//将修改后的termios数据设置到串口中
tcsetattr(fd,TCANOW,&Opt);
……
串口操作采用UNIX类似的方式,打开/关闭/发送/接收等基本操作采用类
似文件系统的方式进行,而一些属性的设置和控制则使用termios来进行。
串口对应的设备文件名为”/dev/ttyS0”。
1. 打开串口
fd = open(“/dev/ttyS0”, O_RDWR);
如果只发送数据,可以使用O_WRONLY, 如果只接收数据,可以
设置成O_RDONLY。
2. 关闭串口
close(fd);
3. 接收数据
ret = read(fd, buf, 100);
串口默认的打开方式是非阻塞的,因此本函数只是接收缓冲中的数
据,而并非直接操作IO。如果要加入一些IO的属性,请参见”使用
超时”和”设置串口属性”。
如果缓冲中有接收到的数据,那么本函数将返回实际接收到的数据
长度,当然不会超过指定的100字节。
如果缓冲中没有数据,那么将返回0。
如果接收失败,那么将返回-1,错误代码放在errno中。
4. 发送数据
ret = write(fd, buf, 100);
返回值表示实际发送的数据长度。
5. 设置串口属性
tcgetattr(int fd, struct termios *termios_p);
tcsetattr(int fd, int optional_actions, struct termios *termios_p);
串口打开后,使用的串口属性实际上是上一次关闭串口前的设置。
这个设置也就是一个结构struct termios,其中主要有以下的属性:
tcflag_t c_iflag // 输入属性
tcflag_t c_oflag // 输出属性
tcflag_t c_cflag // 控制属性
tcflag_t c_lflag // 本地属性
cc_t c_cc[NCCS] // 控制字
c_iflag
IGNBRK 忽略接收到的break信号
BRKINT 如果IGNBRK被设置,break信号将被忽
略,否则如果BRKINT被设置,接收到
break信号将导致输入/输出队列被清空,并
且当前控制串口的前台进程将收到一个
SIGINT信号。如果IGNBRK和BRKINT都
没有被设置 ,收到的break信号将被接收为
NULL,即{post.content}。但是如果PARMARK被设
置,接收到的break信号将被接收为7{post.content}
0。
IGNPAR 忽略帧错误或奇偶校验错。
PARMARK 如果没有设置IGNPAR,设置本属性表示在
接收到的带有错误的帧格式或奇偶校验的字符将被前缀
377{post.content}。如果两者都没有设置,带有错误的帧格式或奇偶
校验的字符将被接收为{post.content}。
INPCK 打开输入数据的奇偶校验。
ISTRIP 滤掉第8位。
INLCR 将接收到的NL(换行)转换成CR(回车)。
IGNCR 忽略接收到的CR。
ICRNL 将收到的CR转换成NL(除非设置了
IGNCR)。
IUCLC 将接收到的大写字符转换成小写。
IXON 打开输出的XON/XOFF控制。
IXOFF 打开输入的XON/XOFF控制。
c_oflag
OLCUC 将小写字符转换成大写后输出。
ONLCR 将NL转换成CR-NL后输出。
OCRNL 将CR转换成NL后输出。
ONLRET 不发送CR。
c_cflag
CBAUD 波特率掩码,可以设置为
B2400
B4800
B9600
B19200
B38400
B57600
B115200
CSIZE 字符长度掩码,可以设置为
CS5
CS6
CS7
CS8
CSTOPB 设置为两个停止位。(默认为1个)
CREAD 打开接收功能。
PARENB 打开发送的奇偶校验生成功能和接收的奇偶
校验检查功能,默认的是偶校验。
PARODD 使用奇校验。
CLOCAL 忽略modem信号线。
CRTSCTS 打开RTS/CTS硬件流控。
c_lflag
由于使用该类属性不多,因此在此不作介绍。
c_cc
常用的是c_cc[VMIN],表示调用read函数时等待接收
的最少字符个数。例如设置为1时,read函数至少要读
到1个字符才会返回。
optional_actions
可以设置为
TCSANOW 立即更新当前的设置。
TCSADRAIN 在当前发送缓冲的所由数据发送
完毕后再更新当前设置。
TCSAFLUSH 同TCSADRAIN,只是在更新前
所有为被读取的收到的数据将被丢弃。
6. 使用超时
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
此函数用于控制接收、发送或异常出现之前的超时。
fd_set是句柄的集合,其中的句柄都是被监测的对象。
readfds表示需要监测其中句柄代表的设备是
否可以从中读取数据。
writefds表示需要监测其中句柄代表的设备
是否可以向其写入数据。
exceptfds表示需要监测其中句柄代表的设备
是否出现异常。
timeout是这样一个结构
struct timeval{
long tv_sec; // 秒
long tv_usec; // 微秒
};
n则是所有监测的句柄中的最大值加一。
系统提供一些定义号的操作来操作fd_set:
FD_CLR(int fd, fd_set *fds);
将fd从fds集合中去掉。
FD_ISSET(int fd, fd_set *fds);
检查fd是否在fds集合中。
FD_SET(int fd, fd_set *fds);
将fd加入fds集合中。
FD_ZERO(fd_set *fds);
将fds集合清空。
7. 其它串口操作
int tcdrain(int fd);
等待所有发送缓冲的数据全部发送出去后返回。
int tcflush(int fd, int queue_selector);
queue_selector:
TCIFLUSH
丢弃所有未读取的接收到的数据。
TCOFLUSH
丢弃所有为发送的发送缓冲的数据。
TCIOFLUSH
丢弃上面两种数据。
示例
#include <termios.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
int main()
{
int fd;
struct termios attr;
fd_set fds;
struct timeval tv;
unsigned char buf[1024];
// 打开串口
fd = open(“/dev/ttyS0”, O_RDWR);
if (fd == -1)
return -1;
// 读取串口当前属性
tcgetattr(fd, &attr);
// 设置最少接收字符个数为0
attr.c_cc[VMIN] = 0;
// 不处理iflag、oflag和lflag
attr.c_iflag = 0;
attr.c_oflag = 0;
attr.c_lflag = 0;
// 设置波特率为9600,字符长度为8位,偶校验,允许接收
attr.c_cflag = B9600 | CS8 | PARENB | CLOCAL | CREAD;
// 设置串口属性
tcsetattr(fd, TCSANOW, &attr);
// 发送字符串
write(fd, “12345\n”, 6);
// 清除监测集合
FD_ZERO(&fds);
// 将串口句柄加入到监测集合中
FD_SET(fd, &fds);
// 设置超时为5秒
tv.tv_sec = 5;
tv.tv_usec = 0;
// 监测串口是否有数据接收到,超时为5秒
if (select(fd+1, &fds, NULL, NULL, &tv) <= 0)
return -1;
// 接收最多100个字符
read(fd, buf, 100);
// 关闭串口
close(fd);
return 0;
}
termios结构体内容:
成员 描述
-------------------------------------------
c_cflag 控制模式标志
c_lflag 本地模式标志
c_iflag 输入模式标志
c_oflag 输出模式标志
c_line line discipline
c_cc[NCCS] 控制字符
c_ispeed 输入波特率
c_ospeed 输出波特率
在termios结构中的四个标志控制了输入输出的四个不同部份。输入模式标志c_iflag决定如何解释和处理接收的字符。输出模式标 志 c_oflag决定如何解释和处理发送到tty设备的字符。控制模式标志决定设备的一系列协议特征,这一标志只对物理设备有效。本地模式标 志 c_lflag决定字符在输出前如何收集和处理。
在串口传输中,用波特率来表示传输的速度,1波特表示在1秒钟内可以传输1个码元。波特率设置可以使用 cfsetispeed(& new_termios,B19200)和 cfsetospeed(&new_termios,B19200)这两个函数来完成,默认的波特率为 9600baud。 cfsetispeed()函数用来设置输入的波特率,cfsetospeed()函数用来设置输出的波特率。B19200是 termios.h头文件 里定义的一个宏,表示19200的波特率。
CLOCAL和CREAD是c_cflag成员中与速率相关的标志,在串口编程中,这两个标志一定要有效,以确保程序在突发的作业控制或挂起时,不会成为端口的占有都,同时串口的接收驱动会自动读入数据。设置方法如下:
termios_new.c_cflag |= CLOCAL; //保证程序不会成为端的占有者
termios_new.c_cflag |= CREAD; //使端口能读取输入的数据
设置串口属性不能直接赋值,要通过对termios不同成员进行"与"和"或"操作来实现。在termios.h文件,定义了各种常量,如上面介 绍的CLOCAL,CREAD。这些常量的值是掩码,通过把这些常量与termios结构成员进行逻辑操作就可实现串口属性的设置。在编程时用"|="来 启用属性,用"&=~"来取消属性。
9.3. c_iflag输入标志说明
*
BRKINT和IGNBRK
如果设置了IGNBRK,中断条件被忽略。如果没有设置IGNBRK而设置了BRKINT,中断条件清空输入输出队列中所有的数据并且向tty 的前台进程组中所有进程发送一个SIGINT信号。如果这两个都没有设置,中断条件会被看作一个0字符。这时,如果设置了PARMRK,当检测到一个帧误 差时将会向应用程序发送三个字节'\377''\0''\0',而不是只发送一个'\0'。
*
PARMRK和IGNPAR
如果设定了IGNPAR,则忽略接收到的数据的奇偶检验错误或帧错误(除了前面提到的中断条件)。如果没有设置IGNPAR而设置了 PARMRK,当接收到的字节存在奇偶检验错误或帧错误的时候。将向应用程序发送一个三字节的'\377''\0''\n'错误报告。其中n表示所接收到 的字节。如果两者都没有设置,除了接收到的字节存在奇偶检验错误或帧误差之外的中止条件都会向应用程序发送一个单字节('\0')的报告。
*
INPCK
如果设置,则进行奇偶校验。如果不进行奇偶检验,PARMRK和IGNPAR将对存在的奇偶校验错误不产生任何的影响。
*
ISTRIP
如果设置,所接收到的所有字节的高位将会被去除,保证它们是一个7位的字符。
*
INLCR
如果设置,所接收到的换行字符('\n')将会被转换成回车符('\r')。
*
IGNCR
如果设置,则会忽略所有接收的回车符('\r')。
*
ICRNL
如果设置,但IGNCR没有设置,接收到的回车符向应用程序发送时会变换成换行符。
*
IUCLC
如果IUCLC和IEXTEN都设置,接收到的所有大写字母发送给应程序时都被转换成小写字母。POSIX中没有定义该标记。
*
IXOFF
如果设置,为避免tty设备的输入缓冲区溢出,tty设备可以向终端发送停止符^S和开始符^Q,要求终端停止或重新开始向计算机发送数据。通 过停止符和开始符来控制数据流的方式叫软件流控制,软件流控制方式较少用,我们主要还是用硬件流控制方式。硬件流控制在c_cflag标志中设置。
*
IXON
如果设置,接收到^S后会停止向这个tty设备输出,接收到^Q后会恢复输出。
*
IXANY
如果设置,则接到任何字符都会重新开始输出,而不仅仅是^Q字符。
*
IMAXBEL
如果设置,当输入缓冲区空间满时,再接收到的任何字符就会发出警报符'\a'。POSIX中没有定义该标记。
9.4. c_oflag输出标志说明
OPOST是POSIX定义的唯一一个标志,只有设置了该标志后,其它非POSIX的输出标记才会生效。
*
OPOST
开启该标记,后面的输出标记才会生效。否则,不会对输出数据进行处理。
*
OLCUC
如果设置,大写字母被转换成小写字母输出。
*
ONLCR
如果设置,在发送换行符('\n')前先发送回车符('\r')。
*
ONOCR
如果设置,当current column为0时,回车符不会被发送也不会被处理。
*
OCRNL
如果设置,回车符会被转换成换行符。另外,如果设置了ONLRET,则current column会被设为0.
*
ONLRET
如果设置,当一个换行符或回车符被发送的时候,current column会被设置为0。
*
OXTABS
如果设置,制表符会被转换成空格符。
9.5. c_cflag控制模式标志说明
*
CLOCAL
如果设置,modem的控制线将会被忽略。如果没有设置,则open()函数会阻塞直到载波检测线宣告modem处于摘机状态为止。
*
CREAD
只有设置了才能接收字符,该标记是一定要设置的。
*
CSIZE
设置传输字符的位数。CS5表示每个字符5位,CS6表示每个字符6位,CS7表示每个字符7位,CS8表示每个字符8位。
*
CSTOPB
设置停止位的位数,如果设置,则会在每帧后产生两个停止位,如果没有设置,则产生一个停止位。一般都是使用一位停止位。需要两位停止位的设备已过时了。
*
HUPCL
如果设置,当设备最后打开的文件描述符关闭时,串口上的DTR和RTS线会减弱信号,通知Modem挂断。也就是说,当一个用户通过Modem拔号登录系统,然后注销,这时Modem会自动挂断。
*
PARENB和PARODD
如果设置PARENB,会产生一个奇偶检验位。如果没有设置PARODD,则产生偶校验位,如果设置了PARODD,则产生奇校验位。如果没有设置PARENB,则PARODD的设置会被忽略。
*
CRTSCTS
使用硬件流控制。在高速(19200bps或更高)传输时,使用软件流控制会使效率降低,这个时候必须使用硬件流控制。
9.6. c_cc[]控制字符说明
只有在本地模式标志c_lflag中设置了IEXITEN时,POSIX没有定义的控制字符才能在Linux中使用。每个控制字符都对应一个按键组合(^C、^H等),但VMIN和VTIME这两个控制字符除外,它们不对应控制符。这两个控制字符只在原始模式下才有效。
*
c_cc[VINTR]
默认对应的控制符是^C,作用是清空输入和输出队列的数据并且向tty设备的前台进程组中的每一个程序发送一个SIGINT信号,对SIGINT信号没有定义处理程序的进程会马上退出。
*
c_cc[VQUIT]
默认对应的控制符是^\,作用是清空输入和输出队列的数据并向tty设备的前台进程组中的每一个程序发送一个SIGQUIT信号,对SIGQUIT信号没有定义处理程序的进程会马上退出。
*
c_cc[verase]
默认对应的控制符是^H或^?,作用是在标准模式下,删除本行前一个字符,该字符在原始模式下没有作用。
*
c_cc[VKILL]
默认对应的控制符是^U,在标准模式下,删除整行字符,该字符在原始模式下没有作用。
*
c_cc[VEOF]
默认对应的控制符是^D,在标准模式下,使用read()返回0,标志一个文件结束。
*
c_cc[VSTOP]
默认对应的控制字符是^S,作用是使用tty设备暂停输出直到接收到VSTART控制字符。或者,如果设备了IXANY,则等收到任何字符就开始输出。
*
c_cc[VSTART]
默认对应的控制字符是^Q,作用是重新开始被暂停的tty设备的输出。
*
c_cc[VSUSP]
默认对应的控制字符是^Z,使当前的前台进程接收到一个SIGTSTP信号。
*
c_cc[VEOL]和c_cc[VEOL2]
在标准模式下,这两个下标在行的末尾加上一个换行符('\n'),标志一个行的结束,从而使用缓冲区中的数据被发送,并开始新的一行。POSIX中没有定义VEOL2。
*
c_cc[VREPRINT]
默认对应的控制符是^R,在标准模式下,如果设置了本地模式标志ECHO,使用VERPRINT对应的控制符和换行符在本地显示,并且重新打印当前缓冲区中的字符。POSIX中没有定义VERPRINT。
*
c_cc[VWERASE]
默认对应的控制字符是^W,在标准模式下,删除缓冲区末端的所有空格符,然后删除与之相邻的非空格符,从而起到在一行中删除前一个单词的效果。POSIX中没有定义VWERASE。
*
c_cc[VLNEXT]
默认对应的控制符是^V,作用是让下一个字符原封不动地进入缓冲区。如果要让^V字符进入缓冲区,需要按两下^V。POSIX中没有定义VLNEXT。
要禁用某个控制字符,只需把它设置为_POSIX_VDISABLE即可。但该常量只在Linux中有效,所以如果程序要考虑移植性的问题,请不要使用该常量。
9.7. c_lflag本地模式标志说明
*
ICANON
如果设置,则启动标准模式,如果没有设置,则启动原始模式。
*
ECHO
如果设置,则启动本地回显。如果没有设置,则除了ECHONL之外,其他以ECHO开头的标记都会失效。
*
ECHOCTL
如果设置,则以^C的形式打印控制字符,如:按Ctrl+C显示^C,按Ctrl+?显示^?。
*
ECHOE
如果在标准模式下设定了ECHOE标志,则当收到一个ERASE控制符时将删除前一个显示字符。
*
ECHOK和ECHOKE
在标准模式下,当接收到一个KILL控制符,则在缓冲区中删除当前行。如果ECHOK、ECHOKE和ECHOE都没有设置,则用ECHOCTL表示的KILL字符(^U)将会在输出终端上显示,表示当前行已经被删除。
如果已经设置了ECHOE和ECHOK,但没有设置ECHOKE,将会在输出终端显示ECHOCTL表示的KILL字符,紧接着是换行,如果设置了OPOST,将会通过OPOST处理程序进行适当的处理。
如果ECHOK、ECHOKE和ECHOE都有设置,则会删除当前行。
在POSIX中没有定义ECHOKE标记,在没有定义ECHOKE标记的系统中,设置ECHOK则表示同时设置了ECHOKE标志。
*
ECHONL
如果在标准模式下设置了该标志,即使没有设置ECHO标志,换行符还是会被显示出来。
*
ECHOPRT
如果设置,则字符会被简单地打印出来,包括各种控制字符。在POSIX中没有定义该标志。
*
ISIG
如果设置,与INTR、QUIT和SUSP相对应的信号SIGINT、SIGQUIT和SIGTSTP会发送到tty设备的前台进程组中的所有进程。
*
NOFLSH
一般情况下,当接收到INTR或QUIT控制符的时候会清空输入输出队列,当接收到SUSP控制符时会清空输入队列。但是如果设置了NOFLUSH标志,则所有队列都不会被清空。
*
TOSTOP
如果设置,则当一个非前台进程组的进程试图向它的控制终端写入数据时,信号SIGTTOU会被被发送到这个进程所在的进程组。默认情况下,这个信号会使进程停止,就像收到SUSP控制符一样。
*
IEXIEN
默认已设置,我们不应修改它。在Linux中IUCLC和几个与删除字符相关的标记都要求在设置了IEXIEN才能正常工作。
9.8. 下面介绍一些常用串口属性的设置方法。
*
设置流控制
termios_new.c_cflag &= ~CRTSCTS; //不使用流控制
termios_new.c_cflag |= CRTSCTS; //使用硬件流控制
termios_new.c_iflag |= IXON|IXOFF|IXANY; //使用软件流控制
*
屏蔽字符大小位
termios_new.c_cflag &= ~CSIZE;
*
设置数据位大小
termios_new.c_cflag |= CS8; //使用8位数据位
termios_new.c_cflag |= CS7; //使用7位数据位
termios_new.c_cflag |= CS6; //使用6位数据位
termios_new.c_cflag |= CS5; //使用5位数据位
*
设置奇偶校验方式
termios_new.c_cflag &= ~PARENB; //无奇偶校验
termios_new.c_cflag |= PARENB; //奇校验
termios_new.c_cflag &= ~PARODD;
termios_new.c_cflag |= PARENB; //偶校验
termios_new.c_cflag &= ~PARODD;
*
停止位
termios_new.c_cflag |= CSTOPB; //2位停止位
termios_new.c_cflag &= ~CSTOPB; //1位停止位
*
输出模式
termios_new.c_cflag &= ~OPOST; //原始数据(RAW)输出
*
控制字符
termios_new.c_cc[VMIN] = 1; //读取字符的最小数量
termios_new.c_cc[VTIME] = 1; //读取第一个字符的等待时间
*
关闭终端回显,键盘输入的字符不会在终端窗口显示。
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
int main(void)
{
struct termios ts,ots;
char passbuf[1024];
tcgetattr(STDIN_FILENO,&ts); /* STDIN_FILENO的值是1,表示标准输入的文件描述符 */
ots = ts;
ts.c_lflag &= ~ECHO; /* 关闭回终端回显功能*/
ts.c_lflag |= ECHONL;
tcsetattr(STDIN_FILENO,TCSAFLUSH,&ts); /* 应用新终端设置 */
fgets(passbuf,1024,stdin); /* 输入字符不会在终端显示 */
printf("you input character = %s\n",passbuf);
tcsetattr(STDIN_FILENO,TCSANOW,&ots); /* 恢复旧的终端设备 */
}
Chapter 10. 安全
Table of Contents
10.1. 内核漏洞介绍
Linux内核以稳定和安全著称,但随着Linux使用范围的不断扩展,各种漏洞也慢慢被内核开发人员或黑客发现。这里介绍有关Linux内核和基于Linux的开源软件的安全问题。
10.1. 内核漏洞介绍
*
权限提升类
*
拒绝服务类
*
溢出类
*
IP地址欺骗类
Chapter 11. 数据结构(Data Structure)
Table of Contents
11.1. 基础概念
11.2. 线性数据结构
11.1. 基础概念
在实际解决问题的时候,各种数据都不是孤立的,数据之间总是存在关系,这种数据之间的关系叫做数据结构。我们可以把数据结构的形式归并为四种:
*
集合:数据之间没有对应关系,但同属于一个集合。如汽车是一个集合,编程语言也是一个集合。
*
线性结构:各数据有一一对应的关系,有前驱也有后续。
*
树形结构:各数据间存在一对多的关系,有一个前驱但有多个后续。
*
图:各数据间有多对多的关系,对前驱和后续没有限制。
数据类型是一个值的集合和定义在这个值集上的一组操作的总称。
数据类型可分两类,一类是每个对象仅由单值组成,称为原子类型,如整型、字符型等。另一类是由某种结构组成的类型,叫结构类型,如数组、字符串等。
抽象数据结构(Abstract Data Type,ADT)是一种数据类型及在这个类型上定义的一组合法的操作。
算法(Algorithm)是一个有穷规则(或语句、指令)的有序集合。通俗地说,就是计算机解决问题的过程。算法应具备以下几个重要的特性:
*
输入:一个算法有零个或多个输入。
*
输出:一个算法至少有一个输出,这种输出是同输入有着某些特定关系的量。没有输出的算法是没有意义的。
*
有穷性:一个算法必须总是在执行有穷步之后结束,且每一步都在有穷时间内完成。
*
确定性:算法中每条指令的含义都必须明确,无二义性。对相同的输入,必须有相同的结果。
*
可行性:算法中的每条指令的执行时间都是有限的。
描述算法的工具:自然语言、流程图、形式化语言和程序设计语言。
由瑞士科学家Niklaus Wirthrn提出的计算机界公认的公式:算法 + 数据结构 = 程序
算法设计的要求:正确、可读、健壮、快速、节省存储空间。
11.2. 线性数据结构
线性结构中的数据元素之间是一种线性关系,数据元素一个接一个地排列。如排除的队列、表格中一行行的记录等。数据元素可以包含多个数据项(字段),包含多个数据项的数据元素叫做记录。由大量记录组成的线性表又称为文件。
线性表的数学表示模型:a0,a1,a2,...a(n-1)。
顺序连续存放的线性表是最简单的,称为顺序存储结构线性表。它在内存开辟一片连续的存储空间,让线性表的第一个元素存放在内存空间的第一个位置, 第二个元素存放在第二个位置,其它元素以此类推。数据元素间的前驱和后继关系表现在存放位置的前后关系上。顺序存储结构线性表算法在插入或删除操作时的效 率不高。平均起来,每插入或删除一个元素需要移动一半的元素,最坏的情况更要移动全部的元素。另外,顺序表不利于存储空间的分配。在经常需要进入插入或删 除操作的线性表中,使用顺序存储结构线性表是不合适的。所以我们有了链式存储结构线性表。
[Note]
数组就是顺序存储结构的程序实现。
链式存储结构线性表由结点组成,每个节点由一个数据元素和一个指向下个结点的指针组成。每个结点中如果只有一个指向后续指针的链表,叫单链表。由于链表通过指针指向下一个结点,所以数据元素可以分散存储。
单链表的建立是一种动态内在管理操作,表中的每个节点占用的存储空间无需预先指定,而是在运行时动态申请。
单链表一旦创建就可对链表进行操作。
*
查找值为x的节点,并返回该节点地址。算法分析:从单链表的第一个节点开始,判断当前节点的数据域的值是否为x,若是,则返回该节点的指针域,否则,依据指针域内的指针查找下一节点,直至表结束。若找不到,则返回空。
*
查找第i个节点,返回期指针。算法分析:从单链表的第一个节点开始,依次判断当前节点是否为第i个节点,若是则返回其指针,否则,依据指针域内的指针查找下一节点,直至表结束。若找不到,则返回空。
Chapter 12. 网络编程
Table of Contents
12.1. TCP/IP协议分析
12.2. 入门示例程序
12.1. TCP/IP协议分析
EthernetII帧的结构(DMAC+SMAC+Type+Data+CRC),EthernetII帧的大小是有限制的,最小不能小于64字节,最大不能超过1518字节,否则帧会被丢弃。一个EthernetII帧包括的内容有:
*
DMAC,目的MAC地址,占48个bit,共6个字节。
*
SMAC,源MAC地址,占48个bit,共6个字节。
*
Type,帧类型,如ip,arp等。占16个bit,共2个字节。
*
Data,帧数据,容量是变化的,但最大不能越过1500个字节,最小不能小于46个字节。
*
CRC,校验码,占32个bit,共4个字节。
IP包结构:
tcgetattr(fd0,&ts0);//把fd0(ttyS0)的属性赋值给ts0
//如果要设置某个选项,那么就使用"|="运算:|=
//如果关闭某个选项就使用"&="和"~"运算:&= ~
ts0.c_cflag |= B9600 | CS7 | CLOCAL | CREAD | PARENB ;
//波特率为9600 | 数据位为7位 | 保证程序不会成为端口的占有者 | 使能端口读取输入的数据 |
ts0.c_cflag &= ~CRTSCTS;//取消流控制 |= CRTSCTS 为硬件流控制
//|= IXON | IXOFF |IXANY; 为软件流控制
ts0.c_lflag &= ~ECHO; //取消回显输入字符/* | IEXTEN | ISIG); */
ts0.c_lflag &= ~ECHONL;
ts0.c_iflag &= ~IXOFF;//同下一行为取消软件流控制
ts0.c_iflag &= ~IXON;
ts0.c_cflag &= ~CSIZE;//屏蔽字符大小位
ts0.c_cflag |= CS7;//数据位为7位
ts0.c_cflag |= PARENB;
//ts0.c_cflag &= ~PARENB;//无校验
/*
无校验 ts0.c_cflag &= ~PARENB;
奇校验 ts0.c_cflag |= (PARODD | PARENB);
偶校验 ts0.c_cflag &= ~ PARENB;opt.c_cflag &= ~PARODD;
空格 ts0.c_cflag &= ~PARENB;opt.c_cflag &= ~CSTOPB;
*/
ts0.c_lflag &= ~ICANON;
ts0.c_oflag &= ~ONLCR;
ts0.c_iflag &= ~INLCR;
/*
ISIG 当接收到字符INTR,QUIT,SUSP或DSUSP时,产生相应的信号。
XCASE (不属于POSIX;LINUX下不支持)如果同时设置了ICANON,终端只有大写。输入被
转换为小写,除了以\前缀的字符。输出时,大写字符被前缀\,小写字符被转换成大写。
ECHO 回显输入字符。
ECHOE 如果同时设置了ICANON,字符ERASE擦除前一个输入字符,WERASE擦除前一个词。
ECHOK 如果同时设置了ICANON,字符KILL删除当前行。
ECHONL 如果同时设置了ICANON,回显字符NL,即使没有设置ECHO。
ECHOCTL (不属于POSIX)如果同时设置了ECHO,除了TAB,NL,START和STOP之外的ASCII
控制信号被回显为^x,这里X是比控制信号大0x40的ASCII码。例如字符0x08(BS)被回显为
^H。
ECHOPRT (不属于POSIX)如果同时设置了ICANON和IECHO,字符在删除的同时被打印。
ECHOKE (不属于POSIX)如果同时设置了ICANON,回显KILL时将删除一行中的每个字符,
如同指定了ECHOE和ECHORPT一样。
DEFECHO (不属于POSIX)只在一个进程读的时候回显。
FLUSHO (不属于POSIX;LINUX不支持)输出被刷新。这个标志可以通过键入字符DISCARD
来打开和关闭。
NOFLSH 禁止产生SIGINT,SIGQUIT和SIGSUSP信号时刷新输入和输出队列。
TOSTOP 向试图写控制终端的后台进程组发送SIGTTOU信号。
PENDIN (不属于POSIX;LINUX不支持)在读入一个字符时,输入队列中的所有字符被重新
输出。(bash用他来处理typeahead)。
IEXTEN 启用实现自定义的输入处理。这个标志必须与ICANON同时使用,才能解释特殊字符
EOL2,LNEXT,REPRINT和WERASE,IUCLC标志才有效。
*/
ts0.c_cc[VMIN] = 0;
ts0.c_cc[VTIME] = 0;
/*
IGNBRK 忽略接收到的break信号
BRKINT 如果IGNBRK被设置,break信号将被忽
略,否则如果BRKINT被设置,接收到
break信号将导致输入/输出队列被清空,并
且当前控制串口的前台进程将收到一个
SIGINT信号。如果IGNBRK和BRKINT都
没有被设置 ,收到的break信号将被接收为
NULL,即{post.content}。但是如果PARMARK被设
置,接收到的break信号将被接收为7{post.content}
0。
IGNPAR 忽略帧错误或奇偶校验错。
PARMARK 如果没有设置IGNPAR,设置本属性表示在
接收到的带有错误的帧格式或奇偶校验的字符将被前缀
377{post.content}。如果两者都没有设置,带有错误的帧格式或奇偶
校验的字符将被接收为{post.content}。
INPCK 打开输入数据的奇偶校验。
ISTRIP 滤掉第8位。
INLCR 将接收到的NL(换行)转换成CR(回车)。
IGNCR 忽略接收到的CR。
ICRNL 将收到的CR转换成NL(除非设置了
IGNCR)。
IUCLC 将接收到的大写字符转换成小写。
IXON 打开输出的XON/XOFF控制。
IXOFF 打开输入的XON/XOFF控制。
c_oflag
`
OLCUC 将小写字符转换成大写后输出。
ONLCR 将NL转换成CR-NL后输出。
OCRNL 将CR转换成NL后输出。
ONLRET 不发送CR
*/
ts0.c_cflag &= ~CSTOPB;
ts0.c_iflag |= IGNBRK;
ts0.c_lflag &= ~IEXTEN;
ts0.c_lflag |= NOFLSH;
rc = cfsetospeed(&ts0,B9600);
rc = tcsetattr(fd0,TCSAFLUSH,&ts0);