c/c++语言开发共享(P4-P5)文件与IO:open、close、creat、read、write

文章目录标准C库系统调用IO标准C库访问文件,使用的是文件指针:FILE* fp系统调用访问文件,使用int fp系统调用标准C库STDIN_FILENOstdinSTDOUT_FILENOstdoutSTDERR_FILENOstderr该文件指针对应的文件描述符为0若注释掉umask(0),则与预期设置的0666不一样,这是由于umask造成的666去掉022的权限(6的权限是4和2,去掉2的权限就是4),所以最终是644

文章目录

    • 1.什么是I/O
    • 2.文件描述符
    • 3.文件系统调用

1.什么是I/O

  • 输入/输出是主存和外部设备之间拷贝数据的过程
    (1)设备——>内存(输入操作)
    (2)内存——>设备(输出操作)
  • 高级I/O:标准C库
    ANSI C提供的标准库称为高级I/O,通常也称为带缓冲的I/O
  • 低级I/O:系统调用IO
    通常也称为不带缓冲的I/O

2.文件描述符

  • 对于Linux而言,所有对设备或文件的操作都是通过文件描述符进行的
  • 当打开或者创建一个文件的时候,内核向进程返回一个文件描述符(非负整数)。
    后序对文件的操作只需通过该文件描述符,内核记录有关这个打开文件的信息。
  • 一个进程启动时,默认打开了3个文件,标准输入、标准输出、标准错误,对应文件描述符是0(STDIN_FILENO),1(STDOUT_FILENO),2(STDERR_FILENO),这些常量定义在unistd.h头文件中。
  • 注意:
标准C库访问文件,使用的是文件指针:FILE* fp 系统调用访问文件,使用int fp  系统调用				  标准C库 STDIN_FILENO 		  stdin STDOUT_FILENO 		  stdout STDERR_FILENO 	      stderr 
  • 代码:P4fileno.c
#include <stdlib.h> #include <stdio.h>  int main(void) {     printf("fileno(stdin) = %dn", fileno(stdin));     return 0; } 
  • 测试:
    该文件指针对应的文件描述符为0
    (P4-P5)文件与IO:open、close、creat、read、write

3.文件系统调用

  • open系统调用
    有几种方法可以获得允许访问文件的文件描述符。最常用的是使用open系统调用
1int open(const char *path, int flags); 参数: path:文件的名称,可以包含(绝对和相对)路径 flags:文件打开模式  返回值: 打开成功,返回文件描述符 打开失败,返回-12int open(const char *pathname, int flags, mode_t mode); 参数:文件的名称,可以包含(绝对和相对)路径 flags:文件打开模式 mode:用来规定对该文件的所有者,文件的用户组及系统中其它用户的访问权限 返回值: 打开成功,返回文件描述符; 打开失败,返回-1
  • 代码:P4open.c
//下面的头文件来自man 2 open #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>  // #define ERR_EXIT(m) (perror(m), exit(EXIT_FAILURE)) //等价于下面的一条一句,注意斜杠后面不能有空格,前面可以有空格 #define ERR_EXIT(m)      do      {          perror(m);         exit(EXIT_FAILURE);     } while(0)   int main(void) {     int fd;     //O_RDONLY只读方式打开     fd = open("test.txt", O_RDONLY);     if (fd == -1)     {         fprintf(stderr,"open error with errno=%d_%sn", errno, strerror(errno));//错误输出方法1         perror("open error");//错误输出方法2         ERR_EXIT("open error");//错误输出方法3         exit(EXIT_FAILURE);//EXIT_FAILURE=1     }      printf("open succn");     return 0; } 
  • 测试:
    (P4-P5)文件与IO:open、close、creat、read、write
  • 代码:P4open2.c
//下面的头文件来自man 2 open #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>  #define ERR_EXIT(m)      do      {          perror(m);         exit(EXIT_FAILURE);     } while(0)   int main(void) {     umask(0);     int fd;     fd = open("test.txt", O_WRDONLY| O_CREAT, 0666);     if (fd == -1)     {          ERR_EXIT("open error");         exit(EXIT_FAILURE);     }      printf("open succn");     return 0; } 
  • 测试:
    若注释掉umask(0),则与预期设置的0666不一样,这是由于umask造成的
    (P4-P5)文件与IO:open、close、creat、read、write
    666去掉022的权限(6的权限是4和2,去掉2的权限就是4),所以最终是644
    (P4-P5)文件与IO:open、close、creat、read、write
    新创建的文件的权限是0666
    (P4-P5)文件与IO:open、close、creat、read、write
  • 打开方式总结
    所有这些标志值的符号名称可以通过#include <fcntl.h>访问
    (P4-P5)文件与IO:open、close、creat、read、write
  • 代码:P4open3.c
//下面的头文件来自man 2 open #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>  #define ERR_EXIT(m)      do      {          perror(m);         exit(EXIT_FAILURE);     } while(0)   int main(void) {     umask(0);     int fd;     //O_EXCL:如果文件存在则打开失败     fd = open("test.txt", O_WRDONLY| O_CREAT| O_EXCL, 0666);     if (fd == -1)     {          ERR_EXIT("open error");         exit(EXIT_FAILURE);     }      printf("open succn");     return 0; } 
  • 测试:
    不如数字创建方便
    S_IRUSR,S_IWUSR,S_IXUSR组合在一起就是7,其它以此类推
    (P4-P5)文件与IO:open、close、creat、read、write
  • 访问权限总结:下面的访问权限在man 2 open中
    (P4-P5)文件与IO:open、close、creat、read、write
  • 代码:P4open4.c
//下面的头文件来自man 2 open #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>  #define ERR_EXIT(m)      do      {          perror(m);         exit(EXIT_FAILURE);     } while(0)   int main(void) {     umask(0);     int fd;     //创建600权限     fd = open("test2.txt", O_WRDONLY| O_CREAT| O_EXCL, S_IRUSR|S_IWUSR);     if (fd == -1)     {          ERR_EXIT("open error");         exit(EXIT_FAILURE);     }      printf("open succn");     close(fd);     return 0; } 
  • 测试:
    (P4-P5)文件与IO:open、close、creat、read、write
  • open返回的错误码说明
若open返回的错误码是EMFILE 进程能够打开的文件数量 [root@host131 ~]# ulimit -n 1024  若open 返回的错误码是ENFILE 系统能够打开的文件数量,与内存有关 [root@host131 ~]# cat /proc/sys/fs/file-max 179974 
  • close系统调用
    为了重新利用文件描述符,用close()系统调用释放打开的文件描述符
int close(int fd); 参数: fd:要关闭的文件的文件描述符  返回值: 如果出现错误,返回-1; 调用成功返回0 
  • creat系统调用
    为了维持与早期的UNIX系统的向后兼容性
int creat(const char *path, mode_t mode);  参数: path:文件的名称,可以包含(绝对和相对)路径 mode:用来规定对该文件的所有者,文件的用户组及系统中其它用户的访问权限  返回值: 打开成功,返回文件描述符 打开失败,返回-1  fd=creat(file,mode); 完全等价于近代的open()调用 fd=open(file, O_WRONLY|O_CREAT|O_TRUNC, mode); 
  • read系统调用
    一旦有了与一个打开文件描述符相连的文件描述符,只要该文件是用O_RDONLY或O_RDWR标志打开的,就可以用read()系统调用从该文件中读取字节
    man 2 read
ssize_t read(int fd, void *buf, size_t count);  参数: fd:想要读的文件的文件描述符 buf:指向内存块的指针,从文件中读取来的字节放到这个内存块中 count:从该文件复制到buf中的字节个数  返回值: 如果出现错误,返回-1; 读文件结束,返回0; 否则返回从该文件复制到规定的缓冲区中的字节数 
  • write系统调用:用write()系统调用将数据写到一个文件中
size_t是无符号整数,可以看成unsigned int ssize_t是有符号整数,可以看成是int  ssize_t write(int fildes, const void *buf, size_t nbyte); 函数参数: fd:要写入额文件的文件描述符 buf:指向内存块的指针,从这个内存块中读取数据写入到文件中 count:要写入文件的字节个数  返回值: 如果出现错误,返回-1 如果写入成功,则返回写入到文件中的字节个数 
  • read与write的区别?
    read操作表示将数据从磁盘读到缓冲区了
    write操作并不意味着缓冲区的数据写入到磁盘中,除非指定O_SYNC选项来打开(O_SYNC:写操作将被阻塞,直到数据被写入到物理硬件上面,才会返回),或者调用fsync()函数来进行同步

  • 代码:P5copy.c

//下面的头文件来自man 2 open #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>  #define ERR_EXIT(m)      do      {          perror(m);         exit(EXIT_FAILURE);     } while(0)  //实现拷贝文件的功能 int main(int argc, char* argv[]) {     int infd;     int outfd;     if (argc !=3 )     {         fprintf(stderr, "Usage %s src dts", argv[0]);         ERR_EXIT(EXIT_FAILURE);     }      infd = open(argv[1], O_RDONLY);     if (infd == -1)         ERR_EXIT("open src error");     if ((outfd = open(argv[2], O_WDONLY|O_CREAT|O_TRUNC, 0644)) == -1)         ERR_EXIT("open dst error");      //将infd的内容写入到outfd     char buf[1024];     int nread;     while ((nread = read(infd, buf, 1024) >0)     {         write(outfd, buf, 1024);     }     close(infd);     close(outfd);     return 0; } 
  • 测试:
    (P4-P5)文件与IO:open、close、creat、read、write

  • 文件的随机读写
    (1)到目前位置的所有文件访问都是顺序访问。这是因为所有的读和写都是从当前文件的偏移位置开始,然后文件偏移值自动地增加到刚好超出读或写结束时的位置,使它为下一次访问做好准备
    (2)有个文件偏移机制,在Linux系统中,随机访问就变得很简单,你所需做的只是将当前文件移值改变到有关的位置,它将迫使一次read()或write()发生在这一位置。(除非文件被O_APPEND打开,在这种情况下,任何write调用仍将发生在文件结束处)

  • lseek
    对应C函数库的fseek(功能一致),目的是进行随机读写;
    功能:通过指定相对于开始位置、当前位置或末尾位置的字节数来重定位curp,这取决于lseek()函数中指定的位置

off_t lseek(int fd, off_t offset, int base); 参数: fd:需设置的文件标识符 offset:偏移量 base:搜索的起始位置  返回值: 返回新的文件偏移值 
  • lseek中的搜索起始base位置
    定义在<unistd.h>
    (P4-P5)文件与IO:open、close、creat、read、write

  • 代码:P5hole.c

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>  #define ERR_EXIT(m)      do      {          perror(m);         exit(EXIT_FAILURE);     } while(0)   //空洞文件的eg int mian(void) {     int fd;     fd = open("hole.txt", O_WRONLY| O_CREAT| O_TRUNC, 0644);     if (fd == -1)         ERR_EXIT("open with error");     write(fd, "ABCDE", 5);     int ret = lseek(fd, 32, SEEK_CUR);     // int ret = lseek(fd, 1024*1024*1024, SEEK_CUR);//仅仅在内核操作,不会在磁盘IO进行操作     if (ret == -1)         ERR_EXIT("lseek error");     write(fd, "hello", 5);     close(fd);     return 0; } 
  • 测试:
    当前偏移量为5
    (P4-P5)文件与IO:open、close、creat、read、write
    od -c XXX.txt查看空洞文件
    磁盘最小块单元是4k,查看方法du -h XXXX.txt
    (P4-P5)文件与IO:open、close、creat、read、write

  • 代码:P5hole.c

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>  #define ERR_EXIT(m)      do      {          perror(m);         exit(EXIT_FAILURE);     } while(0)   //空洞文件的eg int mian(void) {     int fd;     fd = open("hole.txt", O_WRONLY| O_CREAT| O_TRUNC, 0644);     if (fd == -1)         ERR_EXIT("open with error");     write(fd, "ABCDE", 5);     // int ret = lseek(fd, 32, SEEK_CUR);     int ret = lseek(fd, 1024*1024*1024, SEEK_CUR);//仅仅在内核操作,不会在磁盘IO进行操作     if (ret == -1)         ERR_EXIT("lseek error");     write(fd, "hello", 5);     close(fd);     return 0; } 
  • 测试:
    使用ls -lh查看的文件大小为1G多,实际在磁盘上的数据并没有1G多。实际上1G多的空洞文件并不存放在磁盘上,只保存必要信息
    一个文件的大小并不等于其在磁盘上所占用的空间
    (P4-P5)文件与IO:open、close、creat、read、write

  • opendir
    功能:打开一个目录

原型: DIR *opendir(const char *name);  参数: pathname:文件的路径名,相对路径和绝对路径都行  返回值: 打开成功,返回一个目录指针 打开失败,则返回0 
  • readdir
    功能:访问指定目录中下一个连接的细节
struct dirent* readdir(DIR *dirptr);  参数: dirptr:目录指针  返回值: 返回一个指向dirent结构的指针,它包含指定目录中下一个连接的细节; 没有更多连接时,返回0
  • closedir
    功能:关闭一个已经打开的目录
int closedir(DIr *dirptr)  参数: dirptr:目录指针 返回值:调用成功返回0,失败返回-1 
  • 目录信息结构体
 struct old_linux_dirent  {     long  d_ino;              /* inode number */文件在目录中的偏移位置     off_t d_off;              /* offset to this old_linux_dirent */     unsigned short d_reclen;  /* length of this d_name */文件名称的长度     char  d_name[NAME_MAX+1]; /* filename (null-terminated) */ name最重要 }  
  • 代码:P5ls.c
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>  #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>  #include <dirent.h>  #define ERR_EXIT(m)      do      {          perror(m);         exit(EXIT_FAILURE);     } while(0)   //编写简单的ls命令 int main(void) {     DIR *dir = opendir(".");//打开当前目录     struct dirent *de;     while ((de = readdir(dir)) != NULL)//man readdir     {         if (strncmp(de->d_name,"."1) == 0)             continue;         printf("%sn", de->d_name);     }          close(dir);     exit(EXIT_SUCCESS);//等价于exit(0); } 
  • 测试:
    (P4-P5)文件与IO:open、close、creat、read、write

  • 其它:
    mkdir系统调用:用来创建一个称为pathname的新目录,它的权限位设置为mode
    (P4-P5)文件与IO:open、close、creat、read、write
    rmdir系统调用:删除一个空目录
    (P4-P5)文件与IO:open、close、creat、read、write
    chmod和fchmod系统调用:用来改变给定路径名pathname的文件的权限位
    文件的路径名:绝对路径,or相对路径
    (P4-P5)文件与IO:open、close、creat、read、write
    chown和fchown系统调用:用来改变文件所有者的识别号(owner id)或者它的用户组识别号(group ID)
    (P4-P5)文件与IO:open、close、creat、read、write

  • Makefile

.PHONY:clean all CC=gcc CFLAGS=-Wall -g BIN=01fileno 02open 03open 04open 05open 01cp 02lseek 03hole 04ls all:$(BIN) %.o:%.c 	$(CC) $(CFLAGS) -c $< -o $@ clean: 	rm -f *.o $(BIN) 

c/c++开发分享(P4-P5)文件与IO:open、close、creat、read、write地址:https://blog.csdn.net/u011436427/article/details/107325642

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/598917.html

(0)
上一篇 2021年5月8日
下一篇 2021年5月8日

精彩推荐