• linux下 文件IO 相关


    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.
    ", getpid());//current pid
                else
                if(lock.l_type==F_WRLCK)
                    printf("set write lock by pid %d.
    ", getpid());
                else
                if(lock.l_type==F_UNLCK)
                    printf("release lock by pid %d.
    ", 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.
    ", lock.l_pid);
                else
                if(lock.l_type==F_WRLCK)
                    printf("write lock already set by pid %d.
    ", 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.
    ");
        if((fd_write = open("ooo.c", O_RDWR|O_CREAT, 0666))<0)
            perror("open write.
    ");
    
        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.
    ");
    
        /*测试是否为终端设备*/
        if(isatty(STDIN_FILENO)==0)
            printf("standard input is not a terminal device.
    ");
        else
            printf("isatty success.
    ");
    
        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;
    }

    嗯 先这样吧





  • 相关阅读:
    [ACM]线段树
    [ACM]树形结构基础 & 字典树
    [ACM]前缀和 & 差分 & 位运算 & Hash函数
    [ACM] 贪心 & 栈 & 队列 & 优先队列
    [ACM] BFS & 双端BFS & A* & 双边BFS
    [ACM]Two Point & 尺取 & 离散化 & C++STL( struct重写,容器应用 )
    JavaWeb期末
    [数据结构]权值线段树与可持久化线段树(主席树)
    [数字图像处理](六)插值运算
    [数字图像处理](五)AHE
  • 原文地址:https://www.cnblogs.com/snake-hand/p/3143038.html
Copyright © 2020-2023  润新知