文件I/O实践(1) --基础API

来源:互联网 发布:public class java 编辑:程序博客网 时间:2024/05/21 13:55

什么是I/O

输入/输出是内存和外设之间拷贝数据的过程:

   设备->内存: 输入操作

   内存->设备: 输出操作


 高级I/O: ANSI C提供的标准I/O库函数成为高级I/O, 也称为带缓冲的I/O;

 低级I/O: Linux 提供的系统调用通常也称为不带缓冲的I/O;

 

文件描述符

  对于Linux内核而言, 所有的文件或设备都对应一个文件描述符(Linux的设计哲学: 一切皆文件), 这样可以简化系统编程的复杂程度;

  当打开/创建一个文件的时候, 内核向进程返回一个文件描述符(是一个非负整数). 后续对文件的操作只需通过该文件描述符即可进行, 内核记录有关这个打开文件的信息;

  一个进程启动时, 默认已经打开了3个文件, 标准输入(0, STDIN_FILENO), 标准输出(1, STDOUT_FILENO), 标准错误输出(2, STDERR_FILENO), 这些常量定义在unistd.h头文件中; 

  其中, 文件描述符基本上是与文件描述指针(FILE*)一一对应的, 如文件描述符0,1,2 对应 stdin, stdout, stderr;

 

文件指针与文件描述符的转换

fileno: 将文件指针转换成文件描述符

       int fileno(FILE *stream);

fdopen: 将文件描述符转换成文件指针

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

#include <iostream>#include <unistd.h>#include <stdlib.h>#include <stdio.h>using namespace std;//示例int main(){    cout << "fileno(stdin) = " << fileno(stdin) << endl;    cout << "fileno(stdout) = " << fileno(stdout) << endl;    cout << "fileno(stderr) = " << fileno(stderr) << endl;    return 0;}
.PHONY:clean allCC =g++CFLAGS = -Wall -gBIN=01filenoall:$(BIN)%.o:%.cpp$(CC) $(CFLAGS) -c $< -o $@clean:rm -f *.o $(BIN)

文件I/O API

1.open系统调用

#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);

参数:

   pathname:  文件名, 可以包含[绝对/相对]路径名;

   flags: 文件打开模式;

   mode: 用来指定对文件所有者, 文件用户组以及系统中的其他用户的访问权限;

   返回值:

       打开成功,返回文件描述符;

       打开失败,返回-1

注意: newMode = mode & ~umask

flags常用值

   所有这些标志值的符号名称可以通过#inlcude<fcntl.h>访问

//示例1#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <iostream>#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h> //strerror()函数的头文件using namespace std;int main(){    int fd = open("test.txt", O_RDONLY);    if (fd == -1)    {        cerr << "file open error, errno = " << errno <<             "\nstrerror: " << strerror(errno) << endl;        perror("perror");        exit(EXIT_FAILURE);    }    cout << "file open success" << endl;        return 0;}

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <iostream>#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>using namespace std;#define ERR_EXIT(m) \        do \        { \                perror(m); \                exit(EXIT_FAILURE); \        }while(0)int main(){    int fd = open("test.txt", O_RDONLY);    if (fd == -1)    {        ERR_EXIT("open error");    }    cout<<"open succ"<<endl;    return 0;}
//示例3inline void err_exit(std::string message){    perror(message.c_str());    exit(EXIT_FAILURE);}int main(){    umask(0000);    int fd = open("test.txt", O_RDWR|O_CREAT|O_EXCL, 0666);    if (fd == -1)        err_exit("file open error");    else        cout << "file descriptor = " << fd << endl;}

[附]

(1)  umask API

   //改变umask值

   mode_t umask(mode_t mask);

(2)  ulimit -a

   查看系统中的各种限制;

   其中-n: 查看一个进程所能够打开的最大文件数

(3)  cat /proc/sys/fs/file-max 

   查看一个系统能够支持的最大打开文件数(该数与内存大小有关)

(4) errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义

(5) perror(s) 用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。

    头文件  #include<stdio.h>  #include<stdlib.h>

(6) strerrpr 通过标准错误的标号,获得错误的描述字符串 ,将单纯的错误标号转为字符串描述,方便用户查找错误。 头文件: #include <string.h>


2.close系统调用

#include <unistd.h>int close(int fd);

关闭文件描述符, 使得文件描述符得以重新利用

参数:

   -fd:要关闭的文件的文件描述符

返回值:

   如果出现错误,返回-1,调用成功返回0

 

3.read系统调用

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

  一旦有了与一个打开文件描述相连的文件描述符,只要该文件是用O_RDONLY或O_RDWR标志打开的,就可以用read()系统调用从该文件中读取字节。

参数:

  fd:想要读的文件的文件描述符

  buf:指向内存块的指针,从文件中读取来的字节放到这个内存块中

  count:从该文件复制到buf中的字节个数

返回值:

  错误: -1

  到达文件尾: 0

  成功: 返回从文件复制到规定缓冲区的字节数

 

4.wirte系统调用

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

返回值:

   错误: -1

   什么都没做: 0

   成功: 返回成功写入文件的字节数

 

注意:

   write返回大于0时, 并不代表buf的内容已经写入到磁盘上的文件中了, 其仅仅代表buf中的数据已经copy到相应的内核缓冲区了. 要实现将缓冲区的内容真正”冲洗”到磁盘上的文件, 需要调用fsync函数;

     int fsync(int fd);

   其将内核缓冲区中尚未写入磁盘的内容同步到文件系统中;

   其实在open调用的时候也可以指定同步选项:O_SYNC O_SYNC The file is opened for synchronous I/O.   Any  write(2)s  on  the  resulting  file  descriptor will block the calling process until the data has been physically written to the underlying hardware.

   write会等到将buf的内容真正的写入到磁盘才真正返回;

//示例: 带有O_SYNC选项 #include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <iostream>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>using namespace std;inline void err_exit(std::string message){    perror(message.c_str());    exit(EXIT_FAILURE);}int main(int argc, char *argv[]){    if (argc < 3)    {        cerr << "Usage : " << argv[0] << " src dest" << endl;        exit(EXIT_FAILURE);    }    int infd = open(argv[1], O_RDONLY);    if (infd == -1)        err_exit("file O_RDONLY error");    int outfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0666);    if (outfd == -1)        err_exit("file O_WRONLY error");    char buf[1024];    int readBytes, writeBytes;    while ((readBytes = read(infd, buf, sizeof(buf))) > 0)    {        writeBytes = write(outfd, buf, readBytes);        cout << "readBytes = " << readBytes             << ", writeBytes = " << writeBytes << endl;    }    close(infd);    close(outfd);    return 0;}

文件的随机读写

5.lseek

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

功能:对应于C库函数中的fseek,通过指定相对于开始位置、当前位置或末尾位置的字节数来重定位curp,这取决于lseek()函数中指定的位置。

参数:

    fd:需设置的文件标识符

    offset:偏移量

    whence:搜索的起始位置

返回值: 新的文件偏移值;

 

Whence取值:

SEEK_SET

   The offset is set to offset bytes. 从文件开始出计算偏移

SEEK_CUR

   The offset is set to its current location plus offset bytes. 从当前文件的偏移值计算偏移

SEEK_END

   The offset is set to the size of the file plus offset bytes. 从文件的结束出计算偏移

[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. //示例1  
  2. int main(int argc, char *argv[])  
  3. {  
  4.     int fd = open("test.txt", O_RDONLY);  
  5.     if (fd == -1)  
  6.         err_exit("open error");  
  7.     char buf[1024] = {0};  
  8.     int readBytes = read(fd, buf, 5);  
  9.     cout << "readBytes = " << readBytes << ", buf: " << buf << endl;  
  10.     int seekCount = lseek(fd, 0, SEEK_CUR);  
  11.     cout << "current offset = " << seekCount << endl;  
  12. }  
[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. //示例2: 产生空洞文件  
  2. int main(int argc, char *argv[])  
  3. {  
  4.     int fd = open("hole.txt", O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0666);  
  5.     if (fd == -1)  
  6.         err_exit("open error");  
  7.   
  8.     if (write(fd, "ABCDE", 5) == -1)  
  9.         err_exit("first write error");  
  10.     //创建一个1G的文件  
  11.     if (lseek(fd, 1024*1024*1024, SEEK_CUR) == -1)  
  12.         err_exit("lseek error");  
  13.     if (write(fd, "Hello", 5) == -1)  
  14.         err_exit("second write error");  
  15.     close(fd);  
  16. }  

[附]

-查看hole.txt文件

  od -c hole.txt

  cat -A hole.txt

-查看该文件大小

  du -h hole.txt

  du -b hole.txt

  du -k hole.txt

  du -m hole.txt

 

目录访问

6.opendir

[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <dirent.h>  
  3. DIR *opendir(const char *name);  

返回值:

   成功: 返回目录指针;

   失败: 返回NULL;

 

7.readdir

[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. struct dirent *readdir(DIR *dirp);  

返回值:

   成功: 返回一个指向dirent结构的指针, 它包含指定目录的下一个连接的细节;

   没有更多连接时, 返回0;

[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. struct dirent  
  2. {  
  3.     ino_t          d_ino;       /* inode number */  
  4.     off_t          d_off;       /* not an offset; see NOTES */  
  5.     unsigned short d_reclen;    /* length of this record */  
  6.     unsigned char  d_type;      /* type of file; not supported 
  7.                                          by all filesystem types */  
  8.     char           d_name[256]; /* filename */  
  9. };  

8.closedir: 关闭目录   

[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. int closedir(DIR *dirp);  
[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. //示例: 简单的ls程序  
  2. int main(int argc, char *argv[])  
  3. {  
  4.     if (argc < 2)  
  5.     {  
  6.         cerr << "Usage : " << argv[0] << " <directory>" << endl;  
  7.         exit(EXIT_FAILURE);  
  8.     }  
  9.   
  10.     DIR *dir = opendir(argv[1]);  
  11.     if (dir == NULL)  
  12.         err_exit("opendir error");  
  13.   
  14.     struct dirent *ent;  
  15.     while ((ent = readdir(dir)) != NULL)  
  16.     {  
  17.         //过滤掉隐藏文件  
  18.         if (ent->d_name[0] == '.')  
  19.             continue;  
  20.         cout << ent->d_name << "\ti-node: " << ent->d_ino  
  21.              << ", length: " << ent->d_reclen << endl;  
  22.     }  
  23.     closedir(dir);  
  24. }  

9.mkdir

[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. int mkdir(const char *pathname, mode_t mode);  

功能:

       用来创建一个称为pathname的新目录,它的权限设置为mode


10.rmdir: 删除空目录

[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. int rmdir(const char *pathname);  

11. chmod, fchmod更改权限

[cpp] view plain copy
print?在CODE上查看代码片派生到我的代码片
  1. int chmod(const char *path, mode_t mode);  
  2. int fchmod(int fd, mode_t mode);  

参数:

      path:文件的路径名

      fd:文件描述符

返回值:

     成功调用返回0,失败返回-1

12.chown,fchown更改文件所有者/所属组

int chown(const char *path, uid_t owner, gid_t group);int fchown(int fd, uid_t owner, gid_t group);

参数:

      path:文件的路径名
      owner:所有者识别号
      group:用户组识别号      
返回值:
     调用成功返回0,失败返回-1

0 0
原创粉丝点击