Unix中的系统调用I/O和C标准库中的I/O对比


C中的I/O和系统调用的异同及对比

区别点

  1. 在Unix系统中的系统调用I/O函数一般都是围绕文件描述符,在C标准库函数中的I/O函数一般是针对流而言的(C标准库操作文件需要依赖文件指针FILE*类型,linux系统API则依赖文件描述符int类型。可以通过fdopen函数将fd转为FILE*指针)
  2. 使用系统调用的read/write没有用户态的缓存(内核态可能有缓存)。C的标准库函数中有实现缓冲区,所以用C标准I/O库函数要注意I/O缓冲区和实际文件有可能不一致,在必要时需调用fflush()
  3. C标准库是C标准的一部分,在支持C语言的平台上可移植性高,而Unix的系统调用只有在Unix的环境上才可以使用。
  4. C标准库I/O函数的头文件声明在stdio.h中,而Unix的I/O函数在头文件unistd.h
  5. 在linux环境中,C库I/O只能操作普通文件;linux的I/O可以操作所有的文件(普通文件,管道文件,套接字文件等)
  6. C库I/O既可以文本方式格式化的写入读取文件,也可以采用二进制的方式写入读取文件。Linux的I/O只能以二进制方式写入读取文件。
  7. linux文件I/O更偏向于底层,是系统内核实现的,C标准库I/O是基于系统I/O的基础上,通常一些方法进行封装而成,即C库I/O在实现时调用了系统I/O
  8. 在读写设备时通常是不希望有缓冲的,例如向代表网络设备的文件写数据就是希望数据通过网络设备发送出去,而不希望只写到缓冲区里就算完事儿了,当网络设备接收到数据时应用程序也希望第一时间被通知到,所以网络编程通常直接调用Unbuffered I/O函数。
  9. 系统调用的效率应该高一些。

相同点

  1. 无论是使用哪一种I/O操作文件,其过程都是一样,打开文件,操作文件,关闭文件。

注意

  1. 二进制IO的基本问题是,它只用于读同一系统上写数据。原因是不同系统或者不同编译程序中成员偏移量可能不同,而且某些编译程序可能准确对齐,或者紧密排列,所以程序结果在两个平台上可能因编译选项不同而有差异;另外就是存储多字节证书和浮点值的二进制格式在不同的操作系统上不同
  2. C标准库中的缓冲:一般标准输入和标准输出连到终端的时候,默认是行缓冲,缓冲区长度1024字节,但是不代表一行长度只能是1024字节,只是超过了后要多次系统调用。如果连向了文件的话,默认是全缓冲,缓冲区大小一般是当前文件系统的块大小(可能是4096KB);另外标准错误是无缓冲的,这样才能尽快的显示错误信息。
  3. 把标准I/O流作为参数用于临时文件的函数来说,会有很大的性能提升。

相似函数对比

  1. 文件打开函数
    open和fopen
  2. 读写函数
    read、write与fread、fwrite
  3. 缓冲刷新函数
    1. 系统调用:
      sync(不等待实际写操作完成,只将缓冲区内容放入写队列后就返回)
      fsync(等待写操作完成,同时更新文件属性)
      fdatasync(与fsync只更新文件数据部分)
    2. C标准库:
      fflush

与C++的标准输入输出的对比

  1. C++中cin、cout,cerr和c的stdin、stdout、stderr都是同步的,即iostream对象和cstdio流是同步的
  2. 同步即表明我们可以在程序中混合用cout和printf或其他对应的流对。可以用std::ios_base::sync_with_stdio(false)来取消这种同步,取消后,程序中cout和printf就不是按照预期的顺序输出
  3. 通过函数setbuf和setvbuf自己设置输入输出流的缓冲区,需要注意的是这两个函数设置的是c的输入输出缓冲区,因为c++和c的缓冲区是同步的,所有该函数会对c++有影响

一个小问题

如果多进程/多线程在Unix系统下同时写文件尾会发生什么情况?

答:

看write函数的man手册:

linux下write操作,使用O_APPEND选项给文件追加数据。如果write大小不超过PIPE_BUF的话保证是原子操作,大小超过PIPE_BUF不保证原子性。

原文如下:

Write requests of {PIPE_BUF} bytes or less shall not be interleaved with data from other processes doing writes on the same pipe.

Writes of greater than {PIPE_BUF} bytes may have data interleaved, on arbitrary boundaries, with writes by other processes, whether or not the O_NONBLOCK flag of the file status flags is set.

If the O_APPEND flag of the file status flags is set, the file offset shall be set to the end of the file prior to each write and no intervening file modification operation shall occur between changing the file offset and the write operation