IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 嵌入式Linux系统编程学习之九基于文件描述符的文件操作 -> 正文阅读

[系统运维]嵌入式Linux系统编程学习之九基于文件描述符的文件操作


前言


一、文件描述符

??一个程序(进程)可以在运行的过程中同时打开多个文件,每个程序运行起来后,系统就有一个记录表专门记录这个程序打开的各个文件。每打开一个文件,记录表就会用一个新的结构体变量来保存这个文件的相关信息。如果打开多个文件,记录表中就会有多个这样的结构体变量分别保存多个文件的相关信息,它们构成了一个结构体数组,而数组的每一个元素的下标就是文件描述符。
??文件描述符是一个较小的非负数(0~1023),它代表记录表的每一项,即上面说的数组中的某个元素的下标。通过文件描述符和一组基于文件描述符的文件操作函数就可以实现对文件的打开、关闭、读写、删除等操作。
??文件描述符的函数有open(打开)、creat(创建)、close(关闭)、read(读取)、write(写入)、ftruncate(改变文件大小)、lseek(定位)、fsync(同步)、fstat(获取文件状态)、fchmod(权限)、flock(加锁)、fcntl(控制文件属性)、dup(复制)、dup2、select和ioctl。
??基于文件描述符的文件操作并非ANSI C(标准C)的函数,而是Linux的系统调用,即Linux下的API函数。

二、打开、创建和关闭文件

open()函数和creat()函数都能打开和创建文件,原型为:

#include <sys/types.h>									//头文件
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);				//文件名 打开方式
int open(const char *pathname, int flags, mode_t mode);	//文件名 打开方式 权限
int creat(const char *pathname, mode_t mode);			//文件名 权限
int close(int fd);										//fd表示文件描述符

??creat()函数等价于open(pathname, O_CREAT | O_TRUNC | O_WRONLY, mode);
??open()和creat()函数出错时返回-1,成功时返回一个整数(即文件描述符);
??flags表示打开或创建的方式;mode表示文件的访问权限;
??对于open()函数来说,第三个参数mode仅当创建新文件时(即O_CREAT)才生效,打开已有的文件不生效。

flags的可选项:

掩码含义
O_RDONLY以只读的方式打开
O_WRONLY以只写的方式打开
O_RDWR以读写的方式打开
O_CREAT如果文件不存在,则创建文件
O_EXCL仅与O_CREAT连用,若文件已存在,则强制open失败
O_TRUNC如果文件存在,则将文件的长度截至0
O_APPEND以追加的方式打开文件,每次调用write时,文件指针自动先移到文件尾,用于多进程写同一个文件。
O_NONBLOCK非阻塞方式打开,无论有无数据读取或等待,都会立即返回进程之中
O_NODELAY非阻塞方式打开
O_SYNC同步打开文件,只有在数据被真正写入物理设备后才返回

mode的可选项有:

掩码掩码值含义
S_IRWLRY00700代表该文件所有者具有可读、可写及可执行的权限。
S_IRUSR 或 S_IREAD00400代表该文件所有者具有可读取的权限
S_IWUSR 或 S_IWRITE00200代表该文件所有者具有可写入的权限
S_ILRYSR 或 S_IEXEC00100代表该文件的所有者具有可执行的权限
S_IRWXG00070代表该文件用户组具有可读、可写及可执行的权限
S_IRGRP00040代表该文件用户组具有可读的权限
S_IWGRP00020代表该文件用户组具有可写入的权限
S_IXGRP00010代表该文件用户组具有可执行的权限
S_IRWXO00007代表其他用户具有可读、可写及可执行的权限
S_IROTH00004代表其他用户具有可读的权限
S_IWOTH00002代表其他用户具有可写入的权限
S_IXOTH00001代表其他用户具有可执行的权限

注意:对于不存在的文件open时必须加上O_CREAT选项,文件使用完毕后,应调用close关闭。

三、读写文件

读写文件的函数原型为:

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);			//文件描述符 缓冲区 长度
ssize_t write(int fd, const void *buf, size_t count);

??对于read()和write()函数,出错返回-1,读取完成返回0,其他情况返回读写个数。

四、改变文件大小

函数原型为:

#include <unistd.h>
int ftruncate(int fd, off_t length);

??函数ftruncate()会将参数fd指定的文件大小改为参数length指定的大小。参数fd为已打开的文件描述符,且必须是以写入模式打开的文件。若原来的文件大小比参数length大,则超过部分会被删去。执行成功返回0,失败返回-1。

五、文件定位

函数原型:

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

??函数lseek()将文件指针设定到相对于whence偏移值为offset的位置;
whence为以下常量中的一个:

  • SEEK_SET:从文件头开始计算
  • SEEK_CUR:从当前指针开始计算
  • SEEK_END:从文件尾开始计算

??利用该函数可以实现文件空洞(对一个新建的空文件,可以定位到偏移文件开头1024字节的地方,再写入一个字符,则相当于给该文件分配了1025字节的空间,形成文件空洞),通常用于多进程间通信时共享内存。

六、原子操作

对文件的读写原子操作有pread、pwrite:

ssize_t pread(int fd, void *buf, size_t count, off_t offset);//文件描述符 缓冲区 长度 相对于文件头偏移量
ssize_t pwrite(int fd, const void * buf, size_t count, off_t offset);

对文件的读写操作很重要,不能被打断,可以用原子操作函数。

七、进一步理解文件描述符

??如果一个程序打开一个文件,open()函数就会返回一个文件描述符。它的值由系统分配的。实际上,当一个程序打开第一个文件时,分配的文件描述符的值是3,第二个是4,…就这样从此往后排。

八、文件描述符的复制

??函数dup和dup2可以实现文件描述符的复制,此时文件描述符是没有打开的最小文件描述符。原型为:

#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);

??文件描述符的复制是指用另外一个文件描述符指向同一个打开的文件,它完全不同于直接给文件描述符变量赋值;例如描述符变量的直接赋值:

char buf[32];
int fd1 = open("./a.txt", O_RDONLY);
int fd2 = fd1;		//类似于C语言的指针赋值,当释放掉一个时,另一个已经不能操作了
close(fd1);			//导致文件立即关闭
printf("read:%d\n", read(fd2, buf, sizeof(buf)-1));	//读取失败
close(fd2);			//无意义

??在此情况下,两个文件描述符变量的值相同,指向同一个打开的文件,但内核的文件打开引用计数还是1,所以close(fd1)或者close(fd2)都会导致文件立即关闭。
描述符的复制:

char buf[32];
int fd1 = open("./a.txt", O_RDONLY);
int fd2 = dup(fd1);		//内核的文件打开引用计算+1,变成2了
close(fd1);				//当前还不会导致文件被关闭,此时通过fd2依然可以访问文件
printf("read:%d\n", read(fd2, buf, sizeof(buf)-1));
close(fd2);				//内核的引用计数变为0,文件正式关闭

??此时fd2如果修改了文件内容,则文件内容将会改变,及fd1和fd2中有一个修改文件,则对应另一个也变。
??dup2(int fdold, int fdnew)也是进行描述符的复制,采取此种复制,新的描述符由用户用参数fdnew显式指定,而不是像dup一样由内核帮你选定(内核选定的是从最小找的);对于dup2,如果fdnew已经指向一个已经打开的文件,内核会首先关闭掉fdnew所指向的原来的文件。此时再针对fdnew文件描述符操作的文件,则采用的是fdold的文件描述符。如果成功,dup2的返回值与fdnew相同,否则为-1。

九、文件的锁定

??在多进程对同一个文件进行读写访问时,为了保证数据的完整性,有时需要对文件进行锁定。
可以通过fcntl对文件进行锁定和解锁:

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd);	//文件描述符,操作的命令
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock * lock);
int ioctl(int d, int request, ...);	//用来控制硬件,如光驱、硬盘、摄像头、LCD等

返回值:成功返回0,有错误返回-1。
参数struct flock为结构体,表示文件锁信息,如下所示:

struct flock
{
	short int l_type;	//锁定的状态			用F_RDLCK、F_WRLCK、F_UNLCK
	short int l_whence;	//决定l_start位置	用SEEK_SET、SEEK_CUR、SEEK_END
	off_t l_start;		//锁定区域的开头位置
	off_t l_len;		//锁定区域的大小		用struct stat结构体中的st_size可以获得
	pid_t l_pid;		//锁定动作的进程		用getpid()函数获得
};
//通常l_start和l_len都设为0,l_whence为SEEK_SET表示整个文件

参数cmd,常用的如下:
加锁解锁
??置为F_SETLK:设置文件锁定的状态。此时flcok结构体的l_type值必须是F_RDLCK、F_WRLCK或F_UNLCK。如果无法建立锁定,则返回-1。
??对于F_RDLCK:共享锁(或读锁),许多不同的进程可以拥有文件同一(或重叠)区域上的共享锁。只要任一进程拥有共享锁,那么就不能再有进程可以获得该区域上的独占锁。为了获得一把共享锁,文件必须为“读”或“读/写”方式打开。
??对于F_WRLCK:独占锁(或写锁),只有一个进程可以在文件的任一特定区域拥有独占锁。一旦一个进程拥有了这样的锁,其他任何进程都无法在该区域上获得任何类型的锁。为了获得一把独占锁,文件必须为“写”或“读/写”方式打开。
??对于F_UNLCK:解锁,用来清除锁。
??置为F_GETLK:获取文件锁定的状态。此时同样需要对第三个参数指向的结构体填充,即设置为相应的锁,fcntl会取得第一个能够阻止事先设置的锁生成的锁。取得的该锁的信息将覆盖传到fcntl()的flock结构的信息。如果没有发现能够阻止本次锁(flock)生成的锁,这个结构体l_type将为F_UNLCK,并不是上锁文件没有被上锁,而是说别人对这个文件设置的锁不会阻碍这个锁的获得,所以对我们来说相当于是解锁的状态。
??给文件加锁的步骤:

int main()
{
	int fd = open("1.dat", O_RDONLY);	//1.open打开文件
	//计算文件长度,因为设置flock结构体时要设置长度l_len
	struct stat st;
	fstat(fd, &st);
	int len = st.st_size;

	struct flock lock;					//2.设置flock结构体的参数
	lock.l_type = F_RDLCK;		//设置加锁类型为读锁
	lock.l_whence = SEEK_SET;	//从头往后
	lock.l_start = 4;			//开始位置
	lock.l_len = len;			//大小
	lock.l_pid = getpid();		//进程
	fcntl(fd, F_SETLK, &lock);			//3.加锁
	sleep(10);
	close(fd);							//4.关闭文件
	return 0;
}

??文件锁有两种类型:读取锁(共享锁)和写入锁(互斥锁)。对于已经加读取锁的文件,再加写入锁将会失败,但是会允许其他进程继续加读取锁;对于已经加写入锁的文件,再加读取锁和写入锁都将会失败。
??注意:文件锁只会对其他试图加锁的进程有效,对直接访问文件的进程无效,也就是说自己对一个文件加了锁,自己是检测不到锁的状态的。自己是可以读写文件,其他程序也可以读写这个加锁的文件。
??加上相应的锁只是告诉别人我此时在访问文件,起一个标识的作用;当多个进程所修改的文件区不同时就可以并行执行,有交叉时就可以互斥执行。
??另外,在对加了锁的文件进行读写操作时,应采用底层的read和write函数,因为高层的fread和fwrite函数会有缓冲区,这样就不能实现同步更新了。

十、获取文件信息

??可以通过fstat和stat函数获取文件信息,调用完毕后,文件信息被填充到结构体struct stat变量中,函数原型为:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char * file_name, struct stat * buf);	//文件名 stat结构体指针
int fstat(int fd, struct stat * buf);				//文件描述符 stat结构体指针
int lstat(const char * file_name, struct stat * buf);

??lstat函数类似于stat,但是当命名的文件是一个符号链接时,lstat得到该符号链接文件的有关信息,而不是该符号链接指向的文件的信息。
??结构体stat的定义为:

struct stat
{
	dev_t		st_dev;		//如果是设备,返回设备表述符,否则为0
	ino_t		st_ino;		//i节点号
	mode_t		st_mode;	//文件类型
	nlink_t		st_nlink;	//链接数
	uid_t		st_uid;		//属主ID
	gid_t		st_gid;		//组ID
	dev_t		st_rdev;	//设备类型
	off_t		st_size;	//文件大小,字节表示
	blksize_t	st_blksize;	//块大小
	blkcnt_t	st_blocks;	//块数
	time_t		st_atime;	//最后访问时间
	time_t		st_mtime;	//最后修改时间
	time_t		st_ctime;	//创建时间
};

??对于结构体的成员st_mode,有一组宏可以进行文件类型的判断,如下表:

描述
S_ISLNK(mode)判断是否是符号链接
S_ISREG(mode)判断是否是普通文件
S_ISDIR(mode)判断是否是目录
S_ISCHR(mode)判断是否是字符型设备
S_ISBLK(mode)判断是否是块设备
S_ISFIFO(mode)判断是否是命名管道
S_ISSOCK(mode)判断是否是套接字

通常用于判断:if(S_ISDIR(st.st_mode)) { }

十一、access函数

??Linux下不同用户对一个文件的可操作权限不同,access函数可以检测当前用户对某文件是否有某权限。按实际用户ID和实际组ID进行权限许可测试的。

#include <unistd.h>
int access(const char * pathname, int mode);	//要检测的文件名  要检测的权限

??mode可以是以下宏:R_OK读,W_OK写,X_OK执行,F_OK测试文件是否存在;有相应权限返回0,没有返回-1。

十二、标准输入/输出文件描述符

??与标准输入/输出流对应,在更底层的实现是用标准输入、标准输出、标准错误文件描述符表示的,它们分别用STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO三个宏表示,值分别是0、1、2三个整型数字。
??标准输入文件描述符–>STDIN_FILENO–>0
??标准输出文件描述符–>STDOUT_FILENO–>1
??标准错误输出文件描述符–>STDERR_FILE–>2
??这就是打开一个文件返回的文件描述符从3开始分配的原因。

十三、时间和日期相关函数

??日历时间:从一个标准时间点到此时的时间经过的秒数。Linux系统中,这个标准时间点是1970年1月1日 00:00:00。用time_t数据类型来表示从那一刻到现在所经过的秒数。
??格林威治时间:即国际标准时间
??本地时间:本地时区的时间

#include <time.h>
time_t time(time_t *t);
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);

??time_t:保存日历时间的数据类型
??tm:保存时间的结构体
struct tm结构体如下:

struct tm {
	int tm_sec;
	int tm_min;
	int tm_hour;
	int tm_mday;
	int tm_mon;
	int tm_year;
	int tm_wday;
	int tm_yday;
	int tm_isdst;
};

注:由于年是从1900年开始计算的,所以要加上1900,月份要加1。我国是东8区,所以本地时间的小时比国际标准时间多8小时。

十四、处理的模型(补充)

??阻塞I/O模型:在这种模型下,若所调用的I/O函数没有完成相关的功能,则会使进程挂起,直到相关数据到达才会返回。如对管道设备、终端设备和网络设备进行读写时经常会出现这种情况。
??非阻塞模型:在这种模型下,当请求的I/O操作不能完成时,则不让进程睡眠,而且立即返回。非阻塞I/O使用户可以调用不会阻塞的I/O操作,如open()、write()、read()。如果该操作不能完成,则会立即返回出错或者返回0。
??I/O多路转接模型:在这种模型下,如果请求的I/O操作阻塞,且它不是真正阻塞I/O,而是让其中一个函数等待,在这期间,I/O还能进行其他操作,如select函数。
??信号驱动I/O模型:在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动I/O,这是由内核通知用户何时可以启动一个I/O操作决定的。
??异步I/O模型:在这种模型下,当一个描述符已经准备好,可以启动I/O时,进程会通知内核。
select函数:

#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptionset, const struct timeval *timeout);

?返回:就绪描述字的正数目,0—超时,-1—出错
?select函数参数:
??maxfd:最大的文件描述符
??readset:内核读操作的描述符字集合
??writeset:内核写操作的描述符字集合
??exceptionset:内核异常操作的描述符字集合
??timeout:等待描述符就绪需要多少时间;NULL代表永远等下去,一个固定值代表等待固定时间,0代表根本不等待,检查描述字之后立即返回。
fd_set集合的相关操作:

void FD_ZERO(fd_set *fdset);		//将所有fd清零
void FD_SET(int fd, fd_set *fdset);	//增加一个fd
void FD_CLR(int fd, fd_set *fdset);	//删除一个fd
int FD_ISSET(int fd, fd_set *fdset);//判断一个fd是否有设置

??一般来说,在使用select函数之前,首先要使用FD_ZERO和FD_SET来初始化文件描述符集,在使用select函数时,可循环使用FD_ISSET测试描述符集,在执行完对相关文件描述符之后,使用FD_CLR来清除描述符集。
struct timeval结构体如下:

struct timeval
{
	long tv_sec;	//秒
	long tv_usec;	//微秒
}

十五、串口编程

??who命令可以查看当前打开了多少个终端,每个终端在/de/pts目录下都有一个对应的设备文件。
??系统只开一个终端时:

#who
root	tty1	2021-09-15 16:19 (:0)
root	pts/0	2021-09-15 08:48 (:0.0)

??说明:上面输出的第2行就是第一个终端,对应 /dev/pts/0这个设备。
??如果要把内容输入到第1个终端,则可以直接使用Linux的echo命令完成。
??如下:

echo "hello" >/dev/pts/0

??串口概述:用户常见的数据通信的基本方式分为并行通信和串行通信。
??并行通信:利用多条数据传输线将一个资料的各位同时传送。特点是传输速度快,适用于短距离通信,但要求传输速度较高的应用场合。
??串行通信:利用一条传输线将资料一位一位地顺序传送。特点是通信线路简单,利用简单的线缆就可以实现通信,降低成本,适用于远距离通信,但传输速度慢的应用场合。
??常见的一些串口参数的配置:

序号信号名称符号流向功能
2发送数据TXDDTE->DCEDTE发送串行数据
3接收数据RXDDTE<-DCEDTE接收串行数据
4请求发送RTSDTE->DCEDTE请求DCE将线路切换到发送方式
5允许发送CTSDTE<-DCEDCE告诉DTE线路已接通可以发送数据
6数据设备准备好DSRDTE<-DCEDCE准备好
7信号地信号公共地
8载波检测DCDDTE<-DCE表示DCE接收到远程载波
20数据终端准备好DTRDTE->DCEDTE准备好
22振铃指示RIDTE<-DCE表示DCE与线路接通,出现振铃

??Linux中,串口文件位于/dev下,串口1为/dev/ttyS0,串口2为/dev/ttyS1。
??tty看当前自己的设备文件名称,stty -a看终端所有属性。
??用如下命令配置终端:

	#stty erase x(将退格键设置为x)
	#stty eof ^E(将文件结束设置为Ctrl+E)
	#stty - echo(取消回显)
	#stty echo(加上回显)
	#minicom -s(选port设置,按A:/dev/ttyS0,按E:先按E再按Q)

??串口的操作步骤如下:
??在串口编程之前,需要执行下面的操作,首先你的计算机上要安装Serial Port(串口);例如,在VM下的settings…下的"Hardware"下可以执行Add添加,然后在列表中选中"Serial Port",在右边选择"Use output file",然后浏览保存到windows下的一个文件中,这样就可以直接简单的进行串口编程了。

	int fd = open("/dev/ttyS0", O_RDWR | O_SYNC);
	char buf[8] = {"hello"};
	write(fd, buf, 8);
	close(fd);

??也可以直接利用echo “asjdfl” >/dev/ttyS0往里面写内容。
(1)打开串口时通过使用标准的文件打开函数操作:

	int fd = ("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);//以读写方式打开串口
	if(-1 == fd)
	{
		perror("提示错误!");
		exit(-1);
	}

??O_NOCTTY标志通知Linux系统,这个程序不会成为对应这个端口的控制终端;如果没有指定这个标志,那么任何一个输入都将会影响用户的进程。
??O_NDELAY标志通知Linux系统,这个程序不关心DCD信号线所处的状态;如果用户指定了这个标志,则进程将会一直处于睡眠状态,直到DCD信号线被激活。
??接下来可以恢复串口的状态为阻塞状态,用于等待串口数据的读入;可以用fcntl函数实现:

	fcntl(fd, F_SETFL, 0);

??再接下来可以测试打开文件描述符是否引用一个终端设备,以进一步确认串口是否正确打开:

	isatty(STDIN_FILENO);

(2)设置串口的波特率、校验位和停止位,设置信息在 struct termios 结构体中:

	struct termios {
		unsigned short c_iflag;		//输入模式
		unsigned short c_oflag;		//输出模式
		unsigned short c_cflag;		//控制模式
		unsigned short c_lflag;		//本地模式
		unsigned short c_cc[NCC];	//控制字符
	};

??在这个结构体中,最重要的是c_cflag,通过对它的赋值,用户可以设置波特率、字符大小、数据位、停止位、奇偶校验位和硬件流控等;其中设置波特率为相应的波特率前加上’B’,还有c_iflag和c_cc也是比较常用的标志;
f_cflag支持的常量名称:

符号定义符号定义
CBAUD波特率的位掩码CSIZE数据位的位掩码
B00波特率CS55个数据位(发送和接收时使用5比特)
B18001800波特率CS66个数据位
B24002400波特率CS77个数据位
B48004800波特率CS88个数据位
B96009600波特率CSTOPB2个停止位(不设为1个停止位)
B1920019200波特率CREAD接收使能
B3840038400波特率PARENB PARODD校验位使能使用奇校验而不适用偶校验
B5760057600波特率HUPCL最后关闭时挂线(放弃DTR)
B115200115200波特率CLOCAL本地连接(不改变端口所有者)
EXTA外部时钟率LOBLK块作业控制输出
EXTB外部时钟率CNET_ CTSRTS硬件流控制使能

??在这里,对c_cflag成员不能直接对其初始化,而要将其通过“与”、“或”操作使用其中某些选项。

??c_iflag支持的常量名称:

符号定义符号定义
INPCK奇偶校验使能IGNBRK忽略中断情况
IGNPAR忽略奇偶校验错误的字符BRKINT当发生中断时发送SIGINT信号
PARMRK对奇偶校验错误做出标记INLCR将NL映射到CR(新行符->回车符)
ISTRIP去掉第8位,将所有接收到的字符裁剪为7比特IGNCR忽略CR(忽略回车符)
IXON启动出口硬件流控ICRNL将CR映射到NL(回车符->新行符)
IXOFF启动入口硬件流控IUCLC将高位情况映射到低位情况
IXANY允许字符重新启动流控IMAXBEL当输入太长时恢复ECHO

??c_cc支持的常量名称:

符号定义符号定义
VINTR中断控制,对应键为Ctrl + CVEOL位于行尾,对应键为 Carriage return (CR)
VQUIT退出操作,对应键为Ctrl + ZVEOL2位于第二行尾,对应键为 Line feed (LF)
VERASE删除操作,对应键为Backspace (BS)VMIN指定了最少读取的字符数
VKILL删除行,对应键为Ctrl + UVTIME指定了读取每个字符的等待时间
VEOF位于文件结尾,对应键为Ctrl + D

?下面详细讲解设置串口属性的基本流程:
?①保存原先串口配置
??首先,为了安全起见和以后调试程序方便,可以先保存原先串口的配置,在这里可以使用函数 tcgetattr(fd, &oldtio)。该函数得到与fd指向对象的相关参数,并将它们保存于oldtio引用的termios结构体中,该函数可以测试配置是否正确,该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为-1。
??使用方式如下:

	struct termios newtio, oldtio;
	if(tcgetattr(fd, &oldtio) == -1)
	{
		perror("tcgetattr error");
		exit(-1);
	}
	bzero(&newtio, sizeof(struct termios));
	//->memset(&newtio, 0, sizeof(struct termios));

?②激活选项有 CLOCAL 和 CREAD
??CLOCAL 和 CREAD 用于本地连接和接收使能,因此,首先要通过位掩码的方式激活这两项:

	newtio.c_cflag |= CLOCAL | CREAD;

?③设置波特率
??设置波特率有专门的函数,用户不能直接通过位掩码方式来操作。设置波特率的主要函数有cfsetispeed和cfsetospeed,这两个函数的使用很简单,如下所示:

	cfsetispeed(&newio, B115200);
	cfsetospeed(&newio, B115200);

??一般情况下,用户需将输入/输出的波特率设为一样。函数成功返回0,失败返回-1。
?④设置字符大小
??与设置波特率不同,设置字符大小并没有现成可用的函数,需要用位掩码。一般首先去除数据位中的位掩码,再重新按要求设置,如下所示:

	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8;

?⑤设置奇偶校验位
??设置奇偶校验位需要用到两个termios中的成员:c_cflag和c_iflag。首先要激活c_cflag中的检验位使能标志PARENB和是否进行偶校验,同时还要激活c_iflag中的奇偶校验使能。
??如使能奇校验时,代码如下:

	newtio.c_cflag |= PARENB;
	newtio.c_cflag |= PARODD;
	newtio.iflag |= (INPCK | ISTRIP);

??如使能偶校验时,代码如下:

	newtio.c_cflag |= PARENB;
	newtio.c_cflag &= ~PARODD;
	newtio.c_iflag |= (INPCK | ISTRIP);

??无奇偶校验时,代码如下:

	newtio.c_cflag &= ~PARENB;

?⑥设置停止位
??设置停止位是通过激活c_cflag中的CSTOPB而实现的。若停止位为1,则清除CSTOPB,若停止位为2,则激活CSTOPB。下面是停止位为1时的代码:

	newtio.c_cflag &= ~CSTOPB;

?⑦设置最少字符和等待时间
??在对接收字符和等待时间没有特殊要求的情况下,可以将其设置为0,如下所示:

	newtio.c_cc[VTIME] = 0;
	newtio.c_cc[VMIN] = 0;

?⑧处理要写入的引用对象
??由于串口在重新设置之后,在此之前写入的引用对象要重新处理,这时就可以调用函数tcflush(fd, queue_selector) 来处理要写入引用的对象。对于尚未传输的数据,或者收到的但尚未读取的数据,其处理方式取决于queue_selector的值。
??这里,queue_selector可能的取值有以下几种:
???TCIFLUSH:刷新收到的数据但是不读;
???TCOFLUSH:刷新写入的数据但是不传送;
???TCIOFLUSH:同时刷新收到的数据但是不读,并刷新写入的数据但是不传送
??示例:

	tcflush(fd, TCIFLUSH);

?⑨激活配置
??在完成全部串口配置之后,要激活刚才的配置并使配置生效,这里用到的函数是tcsetattr,这个函数原型是tcsetattr(fd, OPTION, &newtio);;成功返回0,失败返回-1.
??这里的newtio就是termios类型的变量,OPTION可能的取值有以下三种:
???TCSANOW:改变的配置立即生效;
???TCSADRAIN:改变的配置在所有写入fd的输出都结束后生效;
???TCSAFLUSH:改变的配置在所有写入fd引用对象的输出都被结束后生效,所有已接受但未读入的输入都在改变发生前丢弃。
(3)读写串口
??读写串口就是把串口当文件去读写。
??发送数据:

	char buffer[1024];
	int nwrite = write(fd, buffer 1024);

??读取串口数据:

	char buff[1024];
	while((nread = read(fd, buff, 1024)) > 0)
	{
		buff[nread + 1] = '\0';
		printf("\n%s", buff);
	}

(4)关闭串口就是关闭文件

	close(fd);

十六、日志

??许多应用程序需要记录它们的活动,系统程序经常需要向控制台或日志文件写消息。这些消息可能指示错误、警告或者是与系统有关的一般消息。通常是在/var/log目录下的messages中包含了系统信息。通过syslog可以向系统的日志发送日志信息。
??函数原型如下:

	#include <syslog.h>
	void syslog(int priority, const char * message, arguments...);

??对于priority有如下几个常见的:
???LOG_ EMERG 紧急情况
???LOG_ ALERT 高优先级故障(如:数据库崩溃)
???LOG_ CRIT 严重错误(如:硬件错误)
???LOG_ ERR 错误
???LOG_ WARNING 警告
???LOG_ NOTICE 需要注意的特殊情况
???LOG_ INFO 一般信息
???LOG_ DEBUG 调试信息(写不到message里面)
??利用tail -10 /var/log/message可以查看,还可以通过函数openlog来改变日志信息的表达方式,openlog的原型如下:

	#include <syslog>
	void openlog(const char * ident, int logopt, int facility);
	void closelog(void);

??它可以设置一个字符串ident,该字符串会添加在日志信息的前面。可以通过它来指明是哪个应用程序创建了这条信息。facility的值为LOG_USER。logpot参数对后续syslog调用的行为进行配置,如下:

	LOG_PID		在日志信息中包含进程标识符,这是系统分配给每个进程的一个唯一值
	LOG_CONS	如果信息不能被记录到日志文件中,就把他们发送到控制台

总结

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-18 10:39:18  更:2021-09-18 10:41:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 15:27:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码