• 第六章 文件I/O编程 [notice select() at RIL.pdf] [串口开发]


    前言:本篇重点关注几个I/O的API,理解fcntl和select的用法。

    6.1~6.3 open/close/read/write/lseek/fcntl/select

    6.4 串口

    6.5 标准I/O开发 

    fopen/fdopen/freopen/fread/fwrite/getc/fgetc/getchar/putc/fputc/putchar/

    gets/fgets/puts/fputs/printf/fprintf/sprtinf/vprintf/vfprintf/vsprintf/scanf/fscanf/sscanf

    ===================================================================================

    6.1.1 系统调用按照功能逻辑大致可分为进程控制、进程间通信、文件系统控制、系统控制、存储管理、网络管理、socket控制、用户管理等几类。

    6.1.2 API

    有时,一个API需要几个系统调用来共同完成函数的功能。

    6.2 Linux中文件及文件描述符概述

    Linux中的文件主要分为4种:普通文件、目录文件、链接文件和设备文件。

    通常,一个进程启动时,都会打开3个文件:标准输入、标准输出和标准出错处理。

    这3个文件分别对应文件描述符为0、1和2(STDIN_FILENO STDOUT_FILENO STDERR_FILENO)

    6.3 不带缓存的文件I/O操作

    主要用到5个函数:open / read / write / lseek / close

    6.3.1 open close

    open.c

    open函数返回后的文件描述符一定是最小的未用文件描述符。由于一个进程在启动时自动打开了0、1、2三个文件描述符,因此该文件运行结果中返回的文件描述符为3。

    6.3.2 read write lseek

    ssize_t read(int fd,void *buf,size_t count)
    ssize_t write(int fd,void *buf,size_t count)
    off_t lseek(int fd, off_t offset, int whence);

    write.c

    lseek函数是用于在指定的文件描述符将文件指针定位到相应位置。

    offset是偏移量,每一读写操作(调用read/write)所需要移动的距离,单位是字节的数量,可正可负(向前移,向后移)

    6.3.3 fcntl 在文件已经共享的情况下如何操作。[上锁]文件锁分为建议性锁和强制性锁。

    内核和系统都使用强制性锁,采用强制性锁对性能影响很大,每次读写操作都必须检查是否有锁。

    在Linux中,实现文件上锁的函数有flock和fcntl。

    flock用于对文件施加建议性锁,fcntl不仅可以施加建议性锁,还可以施加强制锁。

    同时,fcntl还能对文件的某一记录进行上锁,也就是记录锁???

    记录锁分为读取锁和写入锁,其中读取锁称为共享锁,它能够使多个进程都能在 文件的同一部分建立读取锁。

    而写入锁称为排斥锁。

    int fcntl(int fd,int cmd,struct flock *lock)

    cmd F_SETLK

    6.3.4 select

    fcntl函数解决了文件的共享问题,接下来该处理I/O复用的情况了。

    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

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

    将句柄给readfds,当句柄可读时,select返回TRUE

    将句柄给writefds,当句柄可写时,select返回TRUE

    将句柄给exceptfds,当句柄出现异常时,select返回TRUE

    然后再用FD_ISSET(句柄, readfds)判断此句柄可读了。

    因为上面3个都是集合,select会同时检测多个句柄。(不知道这种理解是否正确)

    参考:

    https://groups.google.com/forum/#!msg/zhong1985624/aNoXVZc1InE/fIDcXwx0ZbEJ

    http://www.examw.com/linux/all/146463/

    以下为API:

    6.4 嵌入式Linux串口应用开发

    UART 是一个并行输入成为串行输出的芯片

    因为计算机内部采用并行数据,不能直接把数据发到modem,必须经过UART整理才能进行异步传输。

    其过程为:CPU先把准备写入串行设备的数据放到UART寄存器(临时内存块)中,再通过FIFO传送到串行设备。

    如果数据不压缩,波特率等于每秒钟传输的数据位数

    6.4.1

    串口一: /dev/ttyS0

    串口二: /dev/ttyS1

     6.4.2 串口设置详解

    #include<termios.h>

    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*/

    };

    1.保存原先串口配置

    if(tcgetattr(fd, &oldtio) != 0){

        perror("SetupSerial 1");

        return -1;

    }

    2. 激活选项有CLOCAL和CREAD

    newtio.c_cflag |= CLOCAL | CREAD;

    3.设置波特率

    cfsetispeed(&newtio, B115200);

    cfsetospeed(&newtio, B115200);

    4.设置字符大小

    options.c_cflag &= ~CSIZE;/*mask the character size bits*/

    options.c_cflag |= CS8;

    5. 设置奇偶校验位

    使能奇校验:

    newtio.c_cflag |= PARENB;

    newtio.c_cflag |= PARODD;

    newtio.c_iflag |= (INPCK | ISTRIP);

    使能偶校验:

    newtio.c_iflag |= (INPCK | ISTRIP);

    newtio.c_cflag |= PARENB;

    newtio.c_cflag &= ~PARODD;

    6. 设置停止位

    newtio.c_cflag &= ~CSTOPB;

    7. 设置最少字符和等待时间

    newtio.c_cc[VTIME] = 0;

    newtio.c_cc[VMIN] = 0;

    8. 处理要写入的引用对象

    tcflush(fd, TCIFLUSH);

    9.激活配置

    tcsetattr(fd, OPTION, &newtio);

    if((tcsetattr(fd, TCSANOW, &newtio)) != 0){

        perror("com set error");

        return -1;

    }

    SetPort
    #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 set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
    {
        struct termios newtio,oldtio;
        /*保存测试现有串口参数设置,在这里如果串口号等出错,会有相关的出错信息*/
        if ( tcgetattr( fd,&oldtio) != 0) {
            perror("SetupSerial 1");
            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;
        break;
        }   
        /*设置奇偶校验位*/
        switch( nEvent )
        {   
        case 'O': //奇数
            newtio.c_cflag |= PARENB;
            newtio.c_cflag |= PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
        break;
        case 'E': //偶数
            newtio.c_iflag |= (INPCK | ISTRIP);
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
        break;
        case 'N': //无奇偶校验位
            newtio.c_cflag &= ~PARENB;
        break;
        }
        /*设置波特率*/
        switch( nSpeed )
        {
        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;
        }
        /*设置停止位*/
        if( nStop == 1 )
            newtio.c_cflag &= ~CSTOPB;
        else if ( nStop == 2 )
            newtio.c_cflag |= CSTOPB;
        /*设置等待时间和最小接收字符*/
        newtio.c_cc[VTIME] = 0;
        newtio.c_cc[VMIN] = 0;
    /*处理未接收字符*/
        tcflush(fd,TCIFLUSH);
        /*激活新配置*/
        if((tcsetattr(fd,TCSANOW,&newtio))!=0)
        {
            perror("com set error");
            return -1;
        }
        printf("set done!
    ");
        return 0;
    }

    6.4.3 串口使用详解

    1.打开串口

    fd = open("/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);

    O_NOCTTY 标志用于通知Linux系统,这个程序不会成为对应这个端口的控制终端。如果没有指定这个标志,那么任何一个输入(诸如键盘中止信号等)都会影响用户的进程。

    O_NDELAY标志通知Linux系统,这个程序不关心DCD信号线所处的状态(端口的另一端是否激活或停止)。如果用户指定了这个标志,则进程将会一直处在睡眠状态,直到DCD信号线被激活。

    接下来可恢复串口的状态为阻塞状态,用于等待串口数据的读入。

    fcntl(fd, F_SETFL, 0);

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

    isatty(STDIN_FILENO);该函数调用成功则返回0,若失败则返回-1。

    这时,一个串口就已经成功打开了,接下来就可以对这个串口进行读、写操作。

    下面给出一个完整的打开串口的函数。

    Open port
    /*打开串口函数*/
    int open_port(int fd,int comport)
    {
        char *dev[]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};
        long vdisable;
        if (comport==1)//串口 1
        {   
            fd = open( "/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);
            if (-1 == fd){
                perror("Can't Open Serial Port");
                return(-1);
            }   
        }   
        else if(comport==2)//串口 2
        {   
            fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY);
            if (-1 == fd){
                perror("Can't Open Serial Port");
                return(-1);
            }   
        }   
        else if (comport==3)//串口 3
        {   
            fd = open( "/dev/ttyS2", O_RDWR|O_NOCTTY|O_NDELAY);
            if (-1 == fd){
                perror("Can't Open Serial Port");
                return(-1);
            }   
        }   
        /*恢复串口为阻塞状态*/
        if(fcntl(fd, F_SETFL, 0)<0)
            printf("fcntl failed!
    ");
        else
            printf("fcntl=%d
    ",fcntl(fd, F_SETFL,0));
        /*测试是否为终端设备*/
        if(isatty(STDIN_FILENO)==0)
            printf("standard input is not a terminal device
    ");
        else
            printf("isatty success!
    ");
        printf("fd-open=%d
    ",fd);
        return fd; 
    }

    2. 读写串口

    write(fd,buff,8);
    read(fd,buff,8);

    main of set and open port
    /*写串口程序*/
    #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 main(void)
    {
        int fd; 
        int nread,i;
        char buff[]="Hello
    ";
        if((fd=open_port(fd,1))<0){//打开串口
            perror("open_port error");
            return;
        }   
        if((i=set_opt(fd,115200,8,'N',1))<0){//设置串口
            perror("set_opt error");
            return;
        }   
        printf("fd=%d
    ",fd);
        fd=3;
        nread=read(fd,buff,8);//读串口
        printf("nread=%d,%s
    ",nread,buff);
        close(fd);
        return;
    }

    6.5 标准I/O开发

    基本I/O控制不带缓存的,标准I/O操作都是基于流缓冲的。

    全缓冲 malloc

    行缓冲 fputc

    不带缓冲 write stderr

    6.5.1 打开和关闭文件

    FILE* fopen(const char*  path, const char * mode);

    FILE* fdopen(int fd, const char * mode);

    FILE* freopen(const char *path, const char * mode, FILE* stream);

    int fclose(FILE* stream);

    6.5.2 文件读写

    size_t fread(void * ptr, size_t size, size_t nmemb, FILE* stream);

    size_t fwrite(const void * ptr, size_t size, size_t nmemb, FILE* stream);

    6.5.3 输入输出

    文件打开之后,根据一次读写文件中字符的数目可分为字符输入输出、行输入输出和格式化输入输出

    1.字符输入输出

    int getc(FILE* stream); int fgetc(FILE* stream); int getchar(void);

    int putc(int c, FILE* stream); int fputc(int c, FILE* stream); int putchar(int c);

    将标准输入复制到标准输出中去:

    fputc(fgetc(stdin), stdout);

    2.行输入输出

    char* gets(char * s); char fgets(char * s, int size, FILE* stream);

    int puts(const char *s); int fputs(const char * s, FILE* stream);

    fputs(fgets(s, 80, stdin), stdout);

    3.格式化输入输出

    int printf(const char *format, ...);

    int fprintf(FILE* fp, const char *format, ...);

    int sprintf(char * buf, const char *format, ...);

    int vprintf(const char * format, va_list arg);

    int vfprintf(FILE* fp, const char *format, va_list arg);

    int vsprintf(char *buf, const char *format, va_list arg);

    int scanf(const char * format, ...);

    int fscanf(FILE *fp, const char *format, ...);

    int sscanf(char *buf, const char * format, ...);

    实验:

    int main()
    {
        int fd;
        int count = 0;
        char s[10]={0};
        struct flock lock;
        lock.l_start = 0;
        lock.l_whence = SEEK_SET;
        lock.l_len = 0;
    
        if((fd = open("note",O_RDWR | O_CREAT, 0666)) < 0){
          perror("open:");
          exit(1);
        } else
          printf("open file ok:%d
    ",fd);
    
        lock.l_type = F_WRLCK;
        if(fcntl(fd, F_SETLK, &lock) == -1){
          perror("write lock");
          exit(1);
        }
    
        if((count = write(fd, "hello", sizeof("hello"))) < 0){
          perror("write:");
          exit(1);
        } else
          printf("write:hello
    ");
    
        lseek(fd,1,SEEK_SET);
        lock.l_type = F_UNLCK;
        if(fcntl(fd, F_SETLK, &lock) == -1){
          perror("unlock");
          exit(1);
        }
    
        fcntl(fd, F_GETLK, &lock);
        if(lock.l_type != F_UNLCK){
    
        } else
          printf("lock.l_type is F_UNLCK
    ");
    
    
        lock.l_type = F_RDLCK;
        if(fcntl(fd, F_SETLK, &lock) == -1){
          perror("read lock:");
          exit(1);
        }
    
        if((count = read(fd, s, 3))<0){
           perror("read:");
           exit(1);
        } else
           printf("read from file:%s
    ",s);
    
    
        if(close(fd)<0){
          perror("close:");
          exit(1);
        } else
          printf("Close file
    ");
    
        exit(0);
    
    }
     
  • 相关阅读:
    eclipse远程调试Tomcat方法(转)
    Django表单字段汇总
    Django表单API详解
    django使用表单
    django自定义模板标签和过滤器
    django人类可读性
    django特殊的标签和过滤器
    Django内置模板标签
    Django模板语言详解
    django 动态生成PDF文件
  • 原文地址:https://www.cnblogs.com/jimwind/p/2830762.html
Copyright © 2020-2023  润新知