linux下操作文件或设备,需要一个文件描述符 file descriptor,fd 来引用。fd是一个非负整数,实际上是一个索引值,指向文件的记录表,对文件的操作都需要fd。默认的几个:标准输入流 STDIN_FILENO 实际为0;标准输出流 STDOUT_FILENO 实际为1;标准错误流 STDERR_FILENO 实际为2。下面介绍几个文件操作相关函数:open close read write lseek fcntl
1、open
#include <sys/types.h>//提供pid_t定义 #include <sys/stat.h> #include <fcntl.h> int open(char *filepath, int flag, int perms) /* * flags: O_RDONLY(只读) * O_WRONLY(只写) O_RDWR(读写) O_CREAT(若文件不存在则创建) O_EXCL(若使用O_CREAT时文件已存在,则返回错误,用来验证文件是否存在) O_TRUNC(先删除文件原内容) O_NOCTTY O+APPEND(以追加方式打开,文件指针指向文件末尾) 除前三个外,其余可以相互|组合; perms 文件权限; 成功返回fd,失败返回-1; */
2、close
#include <unistd.h> int close(int fd) /* 成功返回 0 失败返回-1 */
3、read
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count) /* 读完一行 或 读到count个byte,返回 成功返回读到的字节数;读到文件末尾返回0 失败返回-1 */
4、write
#include <unistd.h> ssize_t write(int fd, void *buf, size_t count) /* 成功返回已写的字节数 失败返回-1 */
5、lseek
#include <unistd.h> #include <sys/types.h> off_t lseek(int fd, off_t offset, int whence) /* 移动文件指针,whence为基准,取值为SEEK_SET:文件开头;SEEK_CUR:当前位置;SEEK_END:文件末尾。实际偏移值为 offset+whence offset可正可负。 成功返回当前位置 失败返回-1 */
6、fcntl
fcntl函数的其中一个功能是用来给文件加锁,给文件的某一记录加的锁成为记录锁,记录锁又分为读取锁和写入锁。读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁;写入锁又称为排斥锁,任何时候只能有一个进程在文件的某个部分上建立写入锁。文件的同一部分不能同时建立读取锁和写入锁。
#include <sys/types.h> #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, struct flock *lock) /* cmd: F_DUPFD 复制fd; F_SETFD F_GETFD F_GETFL 得到open时设置的标志 F_SETFL 设置open时设置的标志 F_GETLK 根据lock的描述决定是否上文件锁 F_SETLK 设置lock的文件锁 F_SETLKW F_GETOWN 检索将收到SIGIO或SIGURG信号的进程号或进程组号 F_SETOWN 设置进程号或进程组号 struct flock { short l_type; off_t l_start; short l_whence; off_t l_len; pid_t l_pid; } l_type可取 F_RDLCK读取锁;F_WRLCK写入锁;F_UNLCK解锁。 加锁区域为从l_start+l_whence开始,l_len长度的区域。加锁整个文件的做法通常为l_start为0, l_whence为SEEK_SET,l_len为0. 成功返回0 失败返回-1 */
文件加锁实例:
#include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> void lock_set(int fd, int type) { struct flock lock; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; while(1) { lock.l_type = type; if(fcntl(fd, F_SETLK, &lock)==0) {//lock set successful if(lock.l_type==F_RDLCK) printf("set read lock by pid %d.\n", getpid());//current pid else if(lock.l_type==F_WRLCK) printf("set write lock by pid %d.\n", getpid()); else if(lock.l_type==F_UNLCK) printf("release lock by pid %d.\n", getpid()); return; } /*set lock failed, print why and wait usr press any key to set lock again*/ fcntl(fd, F_GETLK, &lock); if(lock.l_type!=F_UNLCK) { if(lock.l_type==F_RDLCK) printf("read lock already set by pid %d.\n", lock.l_pid); else if(lock.l_type==F_WRLCK) printf("write lock already set by pid %d.\n", lock.l_pid); getchar(); } } }
读取锁是共享的,一个进程加锁后,还允许其他进程加锁;写入锁是互斥的,同一时间只允许一个进程加锁。
7、select
IO多路转接模型,应用于IO复用。
#include <sys/types.h> #include <sys/time.h> #include <unistd.h> int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exeptfds, struct timeval *timeout) numfds:监视的最高文件描述符+1; readfds:监视的读文件描述符集合; writefds:监视的写文件描述符集合; exepetfds:监视的异常处理文件描述符集合; timeout:NULL表示永远等待直到捕捉到信号,0表示从不等待,立即返回。 成功返回准备好的文件描述符,失败返回-1.
相关的几个操作宏:
FD_ZERO(fd_set *fds)//清除文件描述符集 FD_SET(int fd, fd_set *fds)//将fd加入到fds中 FD_CLR(int fd, fd_set *fds)//将fd从fds中清除 FD_ISSET(int fd, fd_set *fds)//判断fds中fd是否变化
另:struct timeval
struct timeval { long tv_sec; long tv_unsec;//microsecond 1/1000000 s }
一个select实现IO多路转接的实例:
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> int select_test() { int fd_read, fd_write; int ret; fd_set readfds, writefds; struct timeval tv; if((fd_read = open("xxx.c", O_RDWR|O_CREAT, 0666))<0) perror("open read.\n"); if((fd_write = open("ooo.c", O_RDWR|O_CREAT, 0666))<0) perror("open write.\n"); lseek(fd_read, 0, SEEK_SET); FD_ZERO(&readfds); FD_ZERO(&writefds); FD_SET(fd_read, &readfds); FD_SET(fd_write, &writefds); tv.tv_usec = 0; tv.tv_sec = 2; while(FD_ISSET(fd_read, &readfds)||FD_ISSET(fd_write, &writefds)) { ret = select(fd_write+1, &readfds, &writefds, NULL, &tv); if(ret < 0) perror("select"); else { if(FD_ISSET(fd_read, &readfds)) { //read fd_read } if(FD_ISSET(fd_write, &writefds)) { //write fd_write } } } return 0; }
以上的文件操作是基于文件描述符的基本的IO控制,是不带缓存的, 属于POSIX标准。下面介绍ANSI C标准的IO处理,是基于流缓冲的。ANSIC C标准提供三种类型的缓冲存储:全缓冲 (当填满缓存后才执行实际IO操作);行缓冲 (当遇到换行字符时才执行实际IO操作);不带缓冲(stderr流通常是不带缓冲的)。下面介绍相关操作函数:
1、fopen fdopen freopen
成功都返回FILE类型指针,失败返回NULL。参数不同。
mode取值:
r或rb:打开只读文件,该文件必须存在;
w或wb:打开可读写文件,该文件必须存在;
w+或w+b:打开可读写文件,若文件存在则文件长度清为0,若文件不存在则建立该文件;
a或ab:以附加方式打开只写文件,若文件不存在则先建立该文件,否则数据加到原文件末尾;
a+或a+b:以附加方式打开可读写文件,若文件不存在则建立该文件,否则数据加到原文件末尾;
b用来说明打开的文件为二进制文件。
2、fclose
成功返回0, 失败返回EOF
3、fread
#include <stdio.h> size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) /* 成功返回读取到的数,失败返回EOF */
4、fwrite
#include <stdio.h> size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) /* 成功返回实际写入的数,失败返回EOF */
5、几个输入输出函数
#include <stdio.h> int getc(FILE *stream) int fgetc(FILE *stream) int getchar(void) /*成功返回字符失败返回EOF*/ int putc(int c, FILE *stream) int fputc(int c, FILE *stream) int putchar(int c) /*成功返回c,失败返回EOF*/
char *fgets(char *s, int size, FILE *stream) char *gets(char *s) /* 成功返回s失败返回NULL */ int puts(const char *s) int fput(const char *s, FILE *stream)
差不多了,下面研究下linux下串口应用程序开发
关于串口本身不想多说,linux中,串口对应的设备名为/dev/ttyS0、/dev/ttyS1.。。分别对应串口1、串口2.。。。linux下对设备的操作方法与对文件的操作方法完全一样,因此对串口的读写就是read write等,对串口的一些参数需要另做配置,这是不同之处。
linux下串口的设置主要是设置struct termios
#include <termios.h> struct termios { 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 char }
通常是这样的:
int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop) { struct termios newtio, oldtio; if(tcgetattr(fd, &oldtio)!=0) { perror("set serial."); return -1; } bzero(&newtio, sizeof(newtio)); /*set serial*/ /*step 1:设置字符大小*/ newtio.c_cflag |= CLOCAL|CREAD; newtio.c_cflag &= ~CSIZE; /*step 2:设置停止位*/ switch(nBits) { case 7: newtio.c_cflag |= CS7; break; case 8: newtio.c_cflag |= CS8; break; } /*step3:设置奇偶校验位*/ switch(nEvent) { case 'O': /*奇校验*/ newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD; newtio.c_cflag |= INPCK|ISTRIP; break; case 'E': /*偶校验*/ newtio.c_cflag |= INPCK|ISTRIP; newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD; break; case 'N': /*无校验*/ newtio.c_cflag &= ~PARENB; break; } /*step4: 设置波特率*/ switch(nSpeed) { /*不能直接操作c_cflag,使用特定函数*/ case 2400: cfsetispeed(&newtio, B2400);//接收波特率 cfsetospeed(&newtio, B2400);//发送波特率 break; case 4800: cfsetispeed(&newtio, B4800); cfsetospeed(&newtio, B4800); break; case 9600: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; case 115200: cfsetispeed(&newtio, B115200); cfsetospeed(&newtio, B115200); break; case 460800: cfsetispeed(&newtio, B460800); cfsetospeed(&newtio, B460800); break; default: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; } /*step5:设置停止位*/ if(nStop==1) newtio.c_cflag &= ~CSTOPB; else if(nStop==2) newtio.c_cfalg |= CSTOPB; /*stcp6:设置等待时间和最小接收字符*/ newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 0; /*处理未接收字符*/ tcflush(fd, TCIFLUSH); /*激活新配置*/ if((tcsetattr(fd, TCSANOW, &newtio))!=0) { perror("set serial"); return -1; } return 0; }
而打开串口通常是这样的:
int open_port(int comport) { char *dev[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2"}; long vdisable; int fd; char *comdev = NULL; if(comport==1) /*串口1*/ comdev = "/dev/ttyS0"; if(comport==2) comdev = "/dev/ttyS1"; if(comport==3) comdev = "/dev/ttyS2"; if(comdev==NULL) return -1; if((fd = open(comdev, O_RDWR|O_NOCTTY|O_NDELAY))<0) return -1; /*恢复串口阻塞状态*/ if(fcntl(fd, F_SETFL, 0)<0) printf("fcntl failed.\n"); /*测试是否为终端设备*/ if(isatty(STDIN_FILENO)==0) printf("standard input is not a terminal device.\n"); else printf("isatty success.\n"); return fd; }
测试一下:
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <errno.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <termios.h> #include <stdlib.h> int com_test() { int fd; int nread, i; if((fd = open_port(1))<0) { perror("open port failed."); return -1; } if((i = set_opt(fd, 115200, 8, 'N', 1))<0) { perror("set port failed."); return -1; } //read(fd, buf, 8); close(fd); return 0; }
嗯 先这样吧