1.linux文件I/O

来源:互联网 发布:电商团队美工工资提成 编辑:程序博客网 时间:2024/06/03 17:06



1.C标准文件io函数工作流程 图左边是C标准文件IO函数
    如果要对文件进行操作,必须先fopen这个文件,用fopen打开hello.txt文件的时候,如果成功,则会返回一个struct FILE结构体的地址,这个结构体有三个核心要素,第一个是文件描述符,第二个是文件读写指针位置,第三个是文件缓冲区,I/O缓冲区。
    文件描述符是一个整型的数,例如1、2、3、4,这个文件描述符与linux内核有关,可以用它来直接索引到真正的磁盘文件(姑且先这么理解)   
    文件读写指针位置:记录文件读写到什么位置,当使用fread,fwrite等函数对文件进行读写操作是,他就会记录文件指针位置。
    文件缓冲区:打开一个文件会自动打开一个文件缓冲区,默认大小8192Bytes,在内存中建立的;作用就是缓存往文件中写的数据,当满足条件时,刷新到磁盘中;
                刷新条件:调用fflush函数
                                  数据大到超过缓冲区大小
                                  正常关闭文件,fclose,return(main),exit(main)
    C标准库在当你运行一个程序的时候,会默认打开三个FILE类型的指针:stdin(标准输入)、stdout(标准输入)、stderr(标准出错),也都有自己对应得文件描述符,文件读写指针位置,I/O缓冲区


    以printf函数来时,它是不能直接调用电脑的显示屏的,它是先调用操作系统的API函数,再由操作系统的API函数调用硬件层的显示屏,其中操作系统又分为应用层和内核层。
    以linux系统为例,printf函数调用了应用层的write函数,格式write(fd, "hello", 5),fd是文件描述符(file dis),“hello”,5是5个字节;printf也会默认打开stdin(标准输入)、stdout(标准输入)、stderr(标准出错)三个函数,printf用的是stdout,把stdout的fd传给write,但是要想输入到显示屏上要内核层的设备驱动函数sys_write();通过sys_write函数,就可以把hello输入到屏幕上了。
    write这个函数是没有缓冲区的,但是如果write一个“hello”,在内核层是有缓冲区的,这个缓冲区是有设备驱动函数来维护的。
    c标准库缓冲区与内核缓冲区的区别
    fwrite一个“hello”,首先存到c标准库缓冲区,这个时候如果进行fread是读不到这个hello的,因为这个时候数据还在C标准缓冲区存着,并没有通过write写到内核缓冲区和磁盘上,所以是读不到的;
    write一个“hello”,通过函数的层层调用写到内核缓冲区,write就可以返回了,这个时候内核缓冲区是就有一个叫守护进程/线程,通过守护进程的定时刷新机制,把数据写到设备文件(磁盘、显示屏、网络)上去,
调用fopen会生成一个文件缓冲区,这个文件缓冲区是有C标准提供的,与操作系统无关。


条件:上图是以linux32位操作系统为例
在linux上的每个运行的程序(进程),系统都会给他分配0~4G的地址空间(虚拟地址空间)
    3G~4G:成为内核空间(kernel):运行的linux内核;内存管理,进程管理,设备驱动管理,VFS虚拟文件系统等(受保护,用户不可访问)
如果用户在linux上运行一个./a.out的时候,它是运行在0~3G的
    4k~3G的地址划分:TEXT(二进制机器指令);DATA(已初始化的全局变量);BBS(未初始化的全局变量);堆空间(malloc等申请的空间);共享库/dll动态库(C标准库,系统库);栈空间(局部变量、数组等);命令行参数(main(int argc, char *argv[])中argv字符串数组的存储位置);环境变量(env)
    0~4k:受保护地址 #define NULL (void*)0
    当程序内存访问越界到内核空间是会报段错误
  1 #include <stdio.h>
  2
  3 int main(void)
  4 {
  5         *((unsigned int*)0xcf000000) = 0x12345678;
  6         return 0;
  7 }

段错误(核心已转移)
linxu可执行文件的格式:ELF

文件描述符
    PCB:进程控制块
    每个进程度维护了自己的一个文件描述符表
    PCB里面有个files_struct的一个指针,这个指针指向一个文件描述符表(类似一个文件描述符表),
stdin的文件描述符是0(读),stdout是1(写),stderr是2;0,1,2都是指向当前终端/dev/tty,每个终端都是相对独立的,都有单独的0,1,2.
    printf调用的是write,write的文件描述符是stdout(1);
    文件描述符表存储在内核空间;准则:新分配的文件描述符是文件描述符表里面未使用的最小的那个,默认情况下,一个进程最多打开1024个文件(0~1023),shell命令:ulimit -a查看,-n修改;
    linux应用层打开文件的函数int fd = open("hello.txt", O_RWRD);返回一个整型的值,这个值就是文件描述符里的值,因为这个时候进程已经自己打开了0,1,2三个文件描述符,所以hello.txt的open函数返回的整型是3;3会和hello.txt磁盘文件产生关联.
    当再打开一个“world.txt”,这时候他的文件描述符是4,再close(fd);把“hello.txt”关闭,再打开一个“aaa.txt”,根据文件描述符的准则,这个时候“aaa.txt”的文件描述符是3。


系统API函数
open/close
open


SYNOPSIS
       #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);
       int creat(const char *pathname, mode_t mode);

RETURN VALUE
       open() and creat() return the new file descriptor, or -1 if an
       error occurred (in which  case, errno is set appropriately).

    flags 一个32位的进制数
必选项:以下三个常数中必须指定一个,且仅允许指定一个。
* O_RDONLY 只读打开
* O_WRONLY 只写打开
* O_RDWR 可读可写打开
以下可选项可以同时指定0个或多个,和必选项按位或起来作为flags参数。
* O_APPEND    表示追加。如果文件已有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容。
* O_CREAT    若此文件不存在则创建它。使用此选项是需要提供第三个参数mode,表示该文件的访问权限。
* O_EXCL    如果同时指定了O_CREAT,并且文件已存在,则出错返回。
* O_TRUNC    如果文件已存在,并且以O_WRONLY或O_RDWR方式打开,则将其长度截断为0字节。
* O_NONBLOCK    对于设备文件,以O_NONBLOCK方式打开,可以做非阻塞I/O
在/usr/src/linux-headers-3.2.0-20/include/fcntl.h中宏定义

19 #define O_RDONLY        00000000
 20 #define O_WRONLY        00000001
 21 #define O_RDWR          00000002
 22 #ifndef O_CREAT
 23 #define O_CREAT         00000100        /* not fcntl */
 24 #endif
 25 #ifndef O_EXCL
 26 #define O_EXCL          00000200        /* not fcntl */
 27 #endif
 28 #ifndef O_NOCTTY
 29 #define O_NOCTTY        00000400        /* not fcntl */
 30 #endif
 31 #ifndef O_TRUNC
 32 #define O_TRUNC         00001000        /* not fcntl */
 33 #endif
 34 #ifndef O_APPEND
 35 #define O_APPEND        00002000
 36 #endif
 37 #ifndef O_NONBLOCK
 38 #define O_NONBLOCK      00004000
 39 #endif

mode:就是新创建文件的权限为,可以结合chmod命令来理解;创建的时候注意umask。
文件描述符
一个进程默认打开3个文件描述符
#define STDIN_FILENO     0
#define STDOUT_FILENO    1
#define STDERR_FILENO    2
open函数打开文件错误会返回-1,并且设置一个errno,errno的宏定义在
/usr/include/asm-generic/errno-base.h

  4 #define EPERM            1      /* Operation not permitted */
  5 #define ENOENT           2      /* No such file or directory */
  6 #define ESRCH            3      /* No such process */
  7 #define EINTR            4      /* Interrupted system call */
  8 #define EIO              5      /* I/O error */
  9 #define ENXIO            6      /* No such device or address */
 10 #define E2BIG            7      /* Argument list too long */
 11 #define ENOEXEC          8      /* Exec format error */
 12 #define EBADF            9      /* Bad file number */
 13 #define ECHILD          10      /* No child processes */
 14 #define EAGAIN          11      /* Try again */
 15 #define ENOMEM          12      /* Out of memory */
 16 #define EACCES          13      /* Permission denied */
 17 #define EFAULT          14      /* Bad address */
 18 #define ENOTBLK         15      /* Block device required */
 19 #define EBUSY           16      /* Device or resource busy */
 20 #define EEXIST          17      /* File exists */
 21 #define EXDEV           18      /* Cross-device link */
 22 #define ENODEV          19      /* No such device */
 23 #define ENOTDIR         20      /* Not a directory */
 24 #define EISDIR          21      /* Is a directory */
 25 #define EINVAL          22      /* Invalid argument */
 26 #define ENFILE          23      /* File table overflow */
 27 #define EMFILE          24      /* Too many open files */
 28 #define ENOTTY          25      /* Not a typewriter */
 29 #define ETXTBSY         26      /* Text file busy */
 30 #define EFBIG           27      /* File too large */
 31 #define ENOSPC          28      /* No space left on device */
 32 #define ESPIPE          29      /* Illegal seek */
 33 #define EROFS           30      /* Read-only file system */
 34 #define EMLINK          31      /* Too many links */
 35 #define EPIPE           32      /* Broken pipe */
 36 #define EDOM            33      /* Math argument out of domain of func */


查看这个errno需要用到perror这个函数

SYNOPSIS
       #include <stdio.h>
       #include <errno.h>
       void perror(const char *s);
close
SYNOPSIS
       #include <unistd.h>
       int close(int fd);


RETURN VALUE
       close()  returns  zero  on  success.  On error, -1 is returned, and errno is set
       appropri ately.



read/write

read

SYNOPSIS
       #include <unistd.h>
       ssize_t read(int fd, void *buf, size_t count);


RETURN VALUE
        On  success,  the  number  of bytes read is returned (zero indicates end of file ), and the  file position is advanced by this number.  It is not an error if this  number  is smaller than  the  number  of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file,  or  because  we are  reading from a pipe, or from a terminal), or because read() was interrupted by a signal.  On error, -1 is returned, and errno is set appropriately.  In this case it  is  left  unspecified whether the file position (if any) changes.


write

SYNOPSIS
      #include <unistd.h>
      ssize_t write(int fd, const void *buf, size_t count);


RETURN VALUE
      On success, the number of bytes written is returned (zero indicates nothing was  written).  On error, -1 is returned, and errno is set appropriately.
       If count is zero and fd refers to a regular file, then write() may return a failure
status . if one of the errors below is detected.  If no errors are detected,  0  will  be 
returned. without  causing  any other effect.  If count is zero and fd refers to a file
other than a regular file, the results are not specified.



阻塞与非阻塞
    阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起。只有函数在得到结果之后才会返回。
    非阻塞:非阻塞与阻塞的概念相对应,只在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回
    默认情况下打开文件时是阻塞的,要把flags的值位或(|)上一个非阻塞标志(O_NONBLOCK)就可以改变。
lseek

SYNOPSIS
      #include <sys/types.h>
      #include <unistd.h>
      off_t lseek(int fd, off_t offset, int whence);


RETURN VALUE
      Upon successful completion, lseek() returns the resulting offset location as measured in bytes from the beginning of the file.  On error, the value (off_t) -1 is
returned  and errno is set to indicate the error.
int 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.


linux的shell命令:od -tcx aaa.avi
                                以16进制和ASIIC码对应的方式查看文件

fcntl

SYNOPSIS
      #include <unistd.h>
      #include <fcntl.h>
      int fcntl(int fd, int cmd, ... /* arg */ );

RETURN VALUE
       For a successful call, the return value depends on the operation:
       F_DUPFD  The new descriptor.
       F_GETFD  Value of file descriptor flags.
       F_GETFL  Value of file status flags.
       F_GETLEASE   Type of lease held on file descriptor.
       F_GETOWN  Value of descriptor owner.
       F_GETSIG   Value of signal sent when read or write becomes possible, or zero for traditional SIGIO behavior.
       F_GETPIPE_SZ   The pipe capacity.
       All  commands
                Zero.
       On error, -1 is returned, and errno is set appropriately.


重新设置文件的flags,而且不用重新打开文件







0 0
原创粉丝点击