Skip to content
Daniel Berger edited this page Apr 21, 2023 · 25 revisions

IO-EXTRA

The io-extra gem adds both singleton and instance methods to Ruby's IO class, as well as new constants.

This library is a mix of pure Ruby code and C extension. It is supported on Unix platforms only. Support for OSX is somewhat limited. This library will not work on MS Windows. Take a look at the win32-nio gem if you are interested in scatter reads and gather writes on MS Windows.

Constants

IO::DIRECTIO_ON

Used as an argument to the IO#directio= instance method.

From the man page:

If set, the system behaves as though the application is not going to reuse the file data in the near future. In other words, the file data is not cached in the system's memory pages.

When possible, data is read or written directly between the application's memory and the device when the data is accessed with read(2) and write(2) operations. When such transfers are not possible, the system switches back to the default behavior, but just for that operation. In general, the transfer is possible when the application's buffer is aligned on a two-byte (short) boundary, the offset into the file is on a device sector boundary, and the size of the operation is a multiple of device sectors.

This advisory is ignored while the file associated with fildes is mapped (see mmap(2)).

IO::DIRECTIO_OFF

Used as an argument to the IO#directio= instance method.

From the man page:

Applications get the default system behavior when accessing file data.

When an application reads data from a file, the data is first cached in system memory and then copied into the application's buffer (see read(2)). If the system detects that the application is reading sequentially from a file, the system will asynchronously "read ahead" from the file into system memory so the data is immediately available for the next read(2) operation.

When an application writes data into a file, the data is first cached in system memory and is written to the device at a later time (see write(2)). When possible, the system increases the performance of write(2) operations by cacheing the data in memory pages. The data is copied into system memory and the write(2) operation returns immediately to the application. The data is later written asynchronously to the device. When possible, the cached data is "clustered" into large chunks and written to the device in a single write operation.

IO::IOV_MAX

The maximum number of values that an array argument to IO#writev may contain. On Linux this is probably 1024, but your mileage may vary.

Instance Methods

IO#directio=(const)

A wrapper around the directio() C function. Possible argument values are IO::DIRECTIO_ON and IO::DIRECTIO_OFF.

You can read about the semantics of setting each in the 'Constants' section above.

IO#ttyname

Returns the ttyname for the given file handle, or nil if there isn't one associated with it.

$stdout.ttyname # => "/dev/pts/3"
File.open('test.txt', 'w').ttyname # => nil

Singleton Methods

IO.closefrom(lowfd)

This is a wrapper around the closefrom() C function. From the man page:

The closefrom() system call deletes all open file descriptors greater than or equal to lowfd from the per-process object reference table. Any errors encountered while closing file descriptors are ignored.

# Close all file descriptors from 3 up.
IO.closefrom(3)

IO.fdwalk(lowfd)

This is a wrapper around the fdwalk() C function. It walks over all open file descriptors for the current process, starting at the lowfd value you pass as an argument.

# Inspect all the open handles
IO.fdwalk(0){ |handle|
  puts "=" * 40 
  p handle
  p handle.fileno
}

IO.pread(fh, length, offset)

This is similar to the IO.read method, except that it reads from a given position in the file without changing the file pointer. And unlike IO.read the arguments are all mandatory.

Update: As of version 1.4.0, this singleton method is a thin wrapper around the instance method already provided by core Ruby.

IO.pread_ptr(fileno, length, offset)

This is identical to IO.pread, except that it returns the pointer address of the string, instead of the actual buffer.

This was added because, in some cases, the IO.pread buffer might return an empty string. In such situations we are unable to get the actual pointer address with pure Ruby.

Update: As of version 1.4.0 this method no longer exists.

IO.pwrite(fh, string, offset)

This is a thin singleton wrapper around the equivalent instance method that already ships with core Ruby.

IO.writev(fh, [...])

A wrapper for the writev() C function. It writes an array of strings to fileno using a "gather write" approach. This is useful when you have a large array of values that you wish to write to a filehandle, without having to create intermediate values first, e.g. Array#join.

The size of the array should not exceed IO::IOV_MAX.

a = (0..1023).to_a.map(&:to_s)
fh = File.open('temp.txt', 'w')
IO.writev(fh.fileno, a)
fh.close

Thoughts on directio

Linus Torvalds himself has said that using directio should be avoided. Instead, you should use posix_fadvise. This has been baked into Ruby itself since Ruby 2.2 via the IO#advise method.

However, not everyone agrees with Linus Torvalds, particularly database developers.