zoukankan      html  css  js  c++  java
  • 嵌入式-文件I/O

    #include <stdio.h> 
    #include <stat.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
      
    int main(int argc, char **argv)
    {
        struct stat     buf;
        if(argc != 2) { 
           printf("Usage: stat "); 
           exit(-1); 
        }
        if(stat(argv[1], &buf) != 0) { 
            printf("stat error."); 
            exit(-1); 
        }
        printf("#i-node:    %ld
    ", buf.st_ino);
        printf("#link:      %d
    ", buf.st_nlink);
        printf("UID:        %d
    ", buf.st_uid);
        printf("GID:        %d
    ", buf.st_gid);
        printf("Size        %ld
    ", buf.st_size);
        exit(0);
    }

    1.实现简单的文件内容复制的功能

    memset函数按字节对内存块进行初始化,所以不能用它将int数组初始化为0和-1之外的其他值,一个字符一个字节的,方便初始化。

      如果用memset(a,1,20),就是对a指向的内存的20个字节进行赋值,每个都用数1去填充,转为二进制后,1就是00000001,占一个字节。一个int元素是4字节,合一起是0000 0001,0000 0001,0000 0001,0000 0001,转化成十六进制就是0x01010101,就等于16843009,就变成了对一个int元素的赋值了。

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main()
    {
        int srcid,dstid;
        unsigned char buf[128];
        int readlen,pos;
        memset(buf,0x00,128);//对于字符指针类型的,剩余的部分通常是不会为0的,防止出现野值
        srcid = open("/home/mm/open.c",O_RDONLY);
        if(srcid<0)
        {
            printf("打开文件出错 
    ");
            return 0;
        }
        dstid = open("/home/mm/open.c.bak",O_WRONLY|O_CREAT);
        if(dstid<0)
        {
            printf("打开文件出错 
    ");
            return 0;
        }
        pos = lseek(srcid,0,SEEK_CUR);
        printf("文件当前位置 %d 
    ",pos);
        while(readlen = read(srcid,buf,128)>0)//大于0说明文件还没有读完
        {
            write(dstid,buf,readlen);
            pos +=readlen;//修改读写指针位置
            lseek(srcid,pos,SEEK_SET);//设置读写指针位置
        }
     
        close(srcid);
        close(dstid);
    }

    针对上面例子的Makefile文档,上面文件目录为  /home/wxd/study/open.c

    open:open.o
        gcc -o open open.o 
    .PHONY:clean
    clean:
        sudo rm -f *.o open open.c.bak ~*

    read

    read()是一个系统调用函数。用来从一个文件中,读取指定长度的数据到 buf 中。

    使用read()时需要包含的头文件: <unistd.h>

    函数原型:

      ssize_t read(int fd, void *buf, size_t count);

      ssize_t是系统头文件中 typedef 定义的数据类型,相当于 signed int。

    参数:

      fd:要从中读取内容的文件的文件描述符。

      count:期望读取的文件字节数。

    返回值:

      成功 返回读到的字节数; 已读到文件结尾返回 0; 出错返回 -1。

     

    Write

     write()是一个系统调用函数,作用是将buf 中的内容写入到文件中。

    使用write()函数时要包含头文件 <unistd.h>。

    函数原型:

      ssize_t write(int fd, char *buf, size_t count)

    参数:

      fd:要从中读取内容的文件的文件描述符。

      count:期望读取的文件字节数。

    返回值:

      写入成功返回实际写入的字节数,出错返回-1。

      不得不提的是,返回-1的常见原因是:磁盘空间已满,超过了一个给定进程的文件长度。    

     

    lseek()的作用是,设置文件内容的读写位置。

      每个打开的文件都有一个“当前文件偏移量”,是一个非负整数,用以度量从文件开始处计算的字节数。通常,读写操作都是从当前文件偏移量处开始,并使偏移量增加所读或写的字节数。默认情况下,你打开一个文件(open),除非指定O_APPEND参数,不然位移量被设为0。

    使用lseek()需要包含的头文件:<sys/types.h>,<unistd.h>

    函数原型:

      off_t lseek(int fd, off_t offset, int whence);

      off_t是系统头文件定义的数据类型,相当于signed int

    参数:

      fd:是要操作的文件描述符

      whence:是当前位置基点。

        SEEK_SET,以文件的开头作为基准位置,新位置为偏移量的大小。

        SEEJ_CUR,以当前文件指针的位置作为基准位置,新位置为当前位置加上偏移量。

        SEEK_END,以文件的结尾为基准位置,新位置位于文件的大小加上偏移量。

      offset:偏移量,要偏移的量。可正可负(向后移、向前移);

    返回值:

      成功返回相对于文件开头的偏移量。    出错返回-1

    使用lseek获取文件大小:

     stat 的基本使用

    stat:返回一个与此命

    需要包含的头文件: <sys/types.h>,<sys/stat.h>,<unistd.h>

    函数原型:

      int stat(const char *path, struct stat *buf);
          int fstat(int fd, struct stat *buf);
          int lstat(const char *path, struct stat *buf);

    参数:

      对于stat() & lstat()来说path,是要查看属性的文件或目录的全路径名称

      对于fstat,fd 是要查看属性文件的文件描述符

      buf:指向用于存放文件属性的结构体,函数成功调用后,buf各个字段存放各个属性。

    返回值 成:

      功返回0;      错误返回 -1;

    给定一个文件:

      stat 函数获得一个与此命名文件有关的信息(到一个struct stat 类型的buf中) 。

      fstat 函数获得文件描述符 fd 打开文件的相关信息(到一个struct stat 类型的buf中) 。

      lstat 函数类似于 stat,但是当命名文件是一个符号连接时, lstat 获取该符号连接的有关信息,而不是由该符号连接引用文件的信息。

    struct stat在系统头文件<stat.h>中,

    fcntlselect函数详解

    第一、fcntl函数详细使用      

    fcntl有强大的功能,它能够复制一个现有的描述符,获得/设置文件描述符标记,获得/设置文件状态标记,获得/设置异步I/O所有权,获得/设置纪录锁。

    当多个用户共同使用,操作一个文件的情况,Linux通常采用的方法就是给文件上锁,来避免共享资源产生竞争的状态。

    fcntl文件锁有两种类型:建议性锁和强制性锁
            
    建议性锁是这样规定的:每个使用上锁文件的进程都要检查是否有锁存在,当然还得尊重已有的锁。内核和系统总体上都坚持不使用建议性锁,它们依靠程序员遵守这个规定。
            
    强制性锁是由内核执行的。当文件被上锁来进行写入操作时,在锁定该文件的进程释放该锁之前,内核会阻止任何对该文件的读或写访问,每次读或写访问都得检查锁是否存在。

             使用fcntl文件锁进行I/O操作必须小心:进程在开始任何I/O操作前如何去处理锁,在对文件解锁前如何完成所有的操作,是必须考虑的。如果在设置锁之前打开文件,或者读取该锁之后关闭文件,另一个进程就可能在上锁/解锁操作和打开/关闭操作之间的几分之一秒内访问该文件。当一个进程对文件加锁后,无论它是否释放所加的锁,只要文件关闭,内核都会自动释放加在文件上的建议性锁(这也是建议性锁和强制性锁的最大区别), 所以不要想设置建议性锁来达到永久不让别的进程访问文件的目的(强制性锁才可以)^_^;强制性锁则对所有进程起作用。

          可以用fcntl 函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File StatusFlag),而不必重新open 文件。
          #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);

    这个函数和open 一样,也是用可变参数实现的,可变参数的类型和个数取决于前面的cmd 参数。

    文件锁包括了 建议性锁 和 强制性锁。

    建议性锁要求每个上锁的文件的进程都要检查是否有锁存在,并且尊重已有的锁,在一般情况下,内核和系统都不使用建议性锁。

    强制性锁是由内核执行的锁,当一个文件被上锁进行读写操作的时候,内核将阻止其他任何文件对其进行读写操作。每次读写操作都要检查是否有锁存在。

    Linux中实现上锁的函数有lock()fcntl()

    lock()
    用于对文件施加建议性锁
    fcntl()
    用于对文件施加建议性锁和强制性锁都行。同时还可以对文件某一条纪录进行上锁,也就是记录锁。

    记录锁分为 读取锁(共享锁,它能够使多个进程都能在文件的同一部分建立读取锁) 和 写入锁(排斥锁,在任何时刻只能有一个进程在文件的某部分建立写入锁。)。

    fcntl
    函数原型
    #include<sys/types.h>
    #include<unistd.h>
    #include<fcntl.h>

    int fcntl(int fd,   //
    文件描述符
              int cmd , //
    不同的命令
              struct flock *lock) //
    设置记录锁的具体状态

    cmd
    取值:
    F_DUPFD  
    复制文件描述符
    F_GETFD  
    获得fdclose-on-exec标志
    F_SETFD  
    设置close-on-exec标志
    F_GETFL  
    获得open设置标志
    F_SETFL  
    设置lock描述的标志
    F_GETLK  
    测试该锁是否被另外一把锁排斥
    F_SETLKW
    如果存在其他锁,则调用进程睡眠,如果捕捉到信号则睡眠中断
    F_GETOWN
    检索收到的SIGIOSIGURG信号的进程号或者进程组号
    F_SETOWN
    设置进程号或进程组号

    这里的lock结构体如下:
    struct flock
    {
        short l_type;   /*F_RDLCK(
    读取锁),F_WRLCK(写入锁),F_UNLCK(解锁)*/
        off_t l_start; /*
    相对偏移量(字节)*/
        short l_whence; /*SEEK_SET ,SEEK_CUR ,SEEK_END */
        off_t l_len;    /*
    加锁区域长度*/
        pid_t l_pid;
    }

    成功:0
    出错:-1
    提示:如果加锁整个文件通常的方法是将l_start设置为0l_whence设置为SEEK_SET, l_len设置为0


          
    下面的例子使用F_GETFLF_SETFL这两种fcntl 命令改变STDIN_FILENO的属性上O_NONBLOCK 选项,实现非阻塞读终端的功能。

    fcntl改变File Status Flag


                 #include <unistd.h>
                 #include <fcntl.h>
                 #include <errno.h>
                 #include <string.h>
                 #include <stdlib.h>
                 #define    MSG_TRY "try again "
                 int main(void)
                 {
                         char buf[10];
                         int n;
                         int flags;
                         flags = fcntl(STDIN_FILENO, F_GETFL);
                         flags |= O_NONBLOCK;
                         if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1)
               
       {
                                 perror("fcntl");
                                 exit(1);
                         }
                 tryagain:
                         n = read(STDIN_FILENO, buf, 10);
                         if (n < 0)
                        {
                                 if (errno == EAGAIN)
                                {
                                         sleep(1);
                                         write(STDOUT_FILENO, MSG_TRY,strlen(MSG_TRY));
                                         goto tryagain;
                                 }
                                 perror("read stdin");
                                 exit(1);
                         }
                         write(STDOUT_FILENO, buf, n);
                         return 0;
                 }

    第二、select函数详细使用   

          select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。

           
    文件在句柄在Linux里很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是的,如man socket可以看到“On success, a file descriptor for the new socket is returned.”man 2 open可以看到“open() and creat() return the new file descriptor”,其实文件句柄就是一个整数,看socket函数的声明就明白了:
    int socket(int domain, int type, int protocol);
            
    当然,我们最熟悉的句柄是012三个,0是标准输入,1是标准输出,2是标准错误输出。012是整数表示的,对应的FILE *结构的表示就是stdinstdoutstderr0就是stdin1就是stdout2就是stderr
    比如下面这两段代码都是从标准输入读入9个字节字符:
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    int main(int argc, char ** argv)
    {
            char buf[10] = "";
            read(0, buf, 9); /*
    从标准输入 0 读入字符 */
            fprintf(stdout, "%s ", buf); /*
    向标准输出 stdout 写字符 */
            return 0;
    }
    /* **
    上面和下面的代码都可以用来从标准输入读用户输入的9个字符** */
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    int main(int argc, char ** argv)
    {
            char buf[10] = "";
            fread(buf, 9, 1, stdin); /*
    从标准输入 stdin 读入字符 */
            write(1, buf, strlen(buf));
            return 0;
    }

            继续上面说的select,就是用来监视某个或某些句柄的状态变化的。select函数原型如下:
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
           
    函数的最后一个参数timeout显然是一个超时时间值,其类型是struct timeval *,即一个struct timeval结构的变量的指针,所以我们在程序里要申明一个struct timeval tv;然后把变量tv的地址&tv传递给select函数。struct timeval结构如下:

    struct timeval {
                 long    tv_sec;         /* seconds */
                 long    tv_usec;        /* microseconds */
             };


    234三个参数是一样的类型: fd_set *,即我们在程序里要申明几个fd_set类型的变量,比如rdfds, wtfds, exfds,然后把这个变量的地址&rdfds, &wtfds, &exfds 传递给select函数。这三个参数都是一个句柄的集合,第一个rdfds是用来保存这样的句柄的:当句柄的状态变成可读的时系统就会告诉select函数返回,同理第二个wtfds是指有句柄状态变成可写的时系统就会告诉select函数返回,同理第三个参数exfds是特殊情况,即句柄上有特殊情况发生时系统会告诉select函数返回。特殊情况比如对方通过一个socket句柄发来了紧急数据。如果我们程序里只想检测某个socket是否有数据可读,我们可以这样:

    fd_set rdfds; /* 先申明一个 fd_set 集合来保存我们要检测的 socket句柄 */
    struct timeval tv; /*
    申明一个时间变量来保存时间 */
    int ret; /*
    保存返回值 */
    FD_ZERO(&rdfds); /*
    select函数之前先把集合清零 */
    FD_SET(socket, &rdfds); /*
    把要检测的句柄socket加入到集合里 */
    tv.tv_sec = 1;
    tv.tv_usec = 500; /*
    设置select等待的最大时间为1秒加500毫秒 */
    ret = select(socket + 1, &rdfds, NULL, NULL, &tv); /*
    检测我们上面设置到集合rdfds里的句柄是否有可读信息 */
    if(ret < 0) perror("select");/*
    这说明select函数出错 */
    else if(ret == 0) printf("
    超时 "); /* 说明在我们设定的时间值1秒加500毫秒的时间内,socket的状态没有发生变化 */
    else { /*
    说明等待时间还未到1秒加500毫秒,socket的状态发生了变化 */
        printf("ret=%d ", ret); /* ret
    这个返回值记录了发生状态变化的句柄的数目,由于我们只监视了socket这一个句柄,所以这里一定ret=1,如果同时有多个句柄发生变化返回的就是句柄的总和了 */
        /*
    这里我们就应该从socket这个句柄里读取数据了,因为select函数已经告诉我们这个句柄里有数据可读 */
        if(FD_ISSET(socket, &rdfds)) { /*
    先判断一下socket这外被监视的句柄是否真的变成可读的了 */
            /*
    读取socket句柄里的数据 */
            recv(...);
        }
    }

    注意select函数的第一个参数,是所有加入集合的句柄值的最大那个值还要加1。比如我们创建了3个句柄:
    /************
    关于本文档********************************************
    *filename: Linux
    网络编程一步一步学-select详解
    *purpose:
    详细说明select的用法
    *********************************************************************/
    int sa, sb, sc;
    sa = socket(...); /*
    分别创建3个句柄并连接到服务器上 */
    connect(sa,...);
    sb = socket(...);
    connect(sb,...);
    sc = socket(...);
    connect(sc,...);

    FD_SET(sa, &rdfds);/*
    分别把3个句柄加入读监视集合里去 */
    FD_SET(sb, &rdfds);
    FD_SET(sc, &rdfds);
    在使用select函数之前,一定要找到3个句柄中的最大值是哪个,我们一般定义一个变量来保存最大值,取得最大socket值如下:

    int maxfd = 0;
    if(sa > maxfd) maxfd = sa;
    if(sb > maxfd) maxfd = sb;
    if(sc > maxfd) maxfd = sc;

    然后调用select函数:

    ret = select(maxfd + 1, &rdfds, NULL, NULL, &tv); /* 注意是最大值还要加1 */

    同样的道理,如果我们要检测用户是否按了键盘进行输入,我们就应该把标准输入0这个句柄放到select里来检测,如下:

    FD_ZERO(&rdfds);
    FD_SET(0, &rdfds);
    tv.tv_sec = 1;
    tv.tv_usec = 0;
    ret = select(1, &rdfds, NULL, NULL, &tv); /*
    注意是最大值还要加1 */
    if(ret < 0) perror("select");/*
    出错 */
    else if(ret == 0) printf("
    超时 "); /* 在我们设定的时间tv内,用户没有按键盘 */
    else { /*
    用户有按键盘,要读取用户的输入 */
        scanf("%s", buf);
    }

     

     使用文件标准I/O进行复制文件:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
     
    #define SRC_FILE_NAME "/home/mm/copy.c" /* 源文件名 */
    #define DEST_FILE_NAME "/home/mm/copy.c.bak" /* 目标文件名文件名 */
     
    int cp_file(char *sfile,char *dfile,u_int32_t uLen)
    {
        FILE *sFile = NULL,*dFile = NULL;
        char *line = NULL;
        int tmpNO;
        if((sFile= fopen(sfile,"rb+")) == (FILE *)NULL)   //打开原文件
        {
            return -1;
        }
        if((dFile=fopen(dfile,"wb+")) == (FILE *)NULL)  //打开新文件
        {
            return -1;
        }
     
        line = (char *)malloc(uLen);
        if(line == NULL)
        {
            return -1;
        }
     
        memset(line,0,uLen);
     
        if(fread(line,sizeof(char),uLen,sFile) != uLen)     //读取原文件内容,如果文件很大,请分块读取
        {
            printf("updatefile:fopen error");
            fclose(sFile);
            free(line);
            return -1;
        }
     
     
        if(fwrite(line,sizeof(char),uLen,dFile) != uLen)      //写入新文件
        {
            printf("updatefile:fopen error");
            fclose(dFile);
            free(line);
            return -1;
        }
     
        tmpNO = fileno(dFile);
        fsync(tmpNO);                                 //刷新内核的块缓存
        fclose(sFile);
        fclose(dFile);
        free(line);
        return 0;
    }
     
    int main(void)
    {
        struct stat buf;
        stat(SRC_FILE_NAME, &buf);
        if(cp_file(SRC_FILE_NAME,DEST_FILE_NAME,buf.st_size)<0)
            printf("copy file error");
        return 0;
    
    }
    1. #include <sys/types.h>
    2. #include <sys/stat.h>
    3. #include <unistd.h>
    4. #include <fcntl.h>
    5. #include <stdio.h>
    6. #include <stdlib.h>
    7. #include <string.h>
    8. int main()
    9. {
    10.     int srcid,dstid;
    11.     unsigned char buf[128];
    12.     int readlen,pos;
    13.     memset(buf,0x00,128);
    14.     srcid = open("/home/mm/open.c",O_RDONLY);
    15.     if(srcid<0)
    16.     {
    17.         printf("打开文件出错  ");
    18.         return 0;
    19.     }
    20.     dstid = open("/home/mm/open.c.bak",O_WRONLY|O_CREAT);
    21.     if(dstid<0)
    22.     {
    23.         printf("打开文件出错  ");
    24.         return 0;
    25.     }
    26.     pos = lseek(srcid,0,SEEK_CUR);
    27.     printf("文件当前位置 %d  ",pos);
    28.     while(readlen = read(srcid,buf,128)>0)
    29.     {
    30.         write(dstid,buf,readlen);
    31.         pos +=readlen;
    32.         lseek(srcid,pos,SEEK_SET);
    33.     }
    34.  
    35.     close(srcid);
    36.     close(dstid);
    37. }
  • 相关阅读:
    《银光志Silverlight 3.0开发详解与最佳实践》出版电子版——风云编著
    Nigel Parker 40分钟视频演示了微软的 31 项技术(附下载)
    《银光志Silverlight 3.0开发详解与最佳实践》书搞目录
    Silverlight 2使用C#遍历XML(兼容Silverlight3)
    为什么要把Silverlight归入Web 2.0?
    Silverlight明年将占据互联网设备半壁江山
    Silverlight Tools 3.0中文正式版发布(附下载地址)
    银客帝国招聘Silverlight兼职开发人员
    再说招聘:学开车一定要摸方向盘
    Expression Blend 4 下载
  • 原文地址:https://www.cnblogs.com/wddx5/p/12444297.html
Copyright © 2011-2022 走看看