14-Advanced IO

本文详细介绍了高级IO,包括非阻塞I/O的实现方式、记录锁定的规则与作用,以及内存映射的使用场景和注意事项。通过实例展示了如何在标准输入输出中应用非阻塞I/O,并讨论了记录锁定可能导致的死锁问题。同时,讨论了内存映射文件在多进程中的行为,以及如何利用msync同步文件内容。最后,比较了内存映射I/O与传统read/write操作在文件复制效率上的差异。
摘要由CSDN通过智能技术生成

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1

14.1 Introduction

14.2 Nonblocking I/O

  • Section 10.5: System calls are divided into two categories: the slow ones and all the others. The slow system calls are those that can block forever. They include
    • Reads that can block the caller forever if data isn’t present with certain file types(pipes, terminal devices, and network devices)
    • Writes that can block the caller forever if the data can’t be accepted immediately by these same file types(e.g., no room in the pipe, network flow control)
    • Opens that block until some condition occurs on certain file types(such as an open of a FIFO for writing only, when no other process has the FIFO open for reading)
    • Reads and writes of files that have mandatory record locking enabled
    • Certain ioctl operations
    • Some of the interprocess communication functions(Chapter 15)
  • System calls related to disk I/O are not considered slow, even though the read or write of a disk file can block the caller temporarily.
  • Nonblocking I/O lets us issue an I/O operation and not have it block forever. If the operation cannot be completed, the call returns immediately with an error noting that the operation would have blocked.
  • Two ways to specify nonblocking I/O for a given descriptor.
    1. Call open to get the descriptor specifying the O_NONBLOCK flag(Section 3.3).
    2. For a descriptor that is already open, call fcntl to turn on the O_NONBLOCK file status flag(Section 3.14). Figure 3.12 shows a function that we can call to turn on any of the file status flags for a descriptor.
  • The program in Figure 14.1 reads up to 500000 bytes from the standard input and attempts to write it to the standard output. The standard output is first set to be nonblocking. The output is in a loop, with the results of each write being printed on the standard error.

  • If the standard output is a regular file, we expect the write to be executed once:
$ ls -l /etc/services                                  #print file size
-rw-r--r-- 1 root 677959 Jun 23 2009 /etc/services
$ ./a.out < /etc/services > temp.file                      #try a regular file first
read 500000 bytes
nwrite = 500000, errno = 0                          #a single write
$ ls -l temp.file                                      #verify size of output file
-rw-rw-r-- 1 sar 500000 Apr 1 13:03 temp.file
  • But if the standard output is a terminal, we expect the write to return a partial count sometimes and an error at other times.
$ ./a.out < /etc/services 2>stderr.out         #output to terminal
 #lots of output to terminal ...
$ cat stderr.out
read 500000 bytes
nwrite = 999, errno = 0
nwrite = -1, errno = 35
nwrite = -1, errno = 35
nwrite = -1, errno = 35
nwrite = -1, errno = 35
nwrite = 1001, errno = 0
nwrite = -1, errno = 35
nwrite = 1002, errno = 0
nwrite = 1004, errno = 0
nwrite = 1003, errno = 0
nwrite = 1003, errno = 0
nwrite = 1005, errno = 0
nwrite = -1, errno = 35
...                                         #61 of these errors
nwrite = 1006, errno = 0
nwrite = 1004, errno = 0
nwrite = 1005, errno = 0
nwrite = 1006, errno = 0
nwrite = -1, errno = 35
...                                         #108 of these errors
nwrite = 1006, errno = 0
nwrite = 1005, errno = 0
nwrite = 1005, errno = 0
nwrite = -1, errno = 35
...                                         #681 of these errors and so on ...
nwrite = 347, errno = 0
  • The errno of 35 is EAGAIN(Resource temporarily unavailable). The amount of data accepted by the terminal driver varies from system to system. The results also vary depending on how you are logged in to the system: on the system console, on a hard-wired terminal, on a network connection using a pseudo terminal. If you are running a windowing system on your terminal, you are also going through a pseudo terminal device.
  • In this example, the program issues more than 9000 write calls, even though only 500 are needed to output the data. The rest just return an error. This type of loop, called polling, is a waste of CPU time on a multiuser system.
  • We can avoid using nonblocking I/O by using multiple threads(Chapter 11). We can allow individual threads to block in I/O calls if we can continue to make progress in other threads.

14.3 Record Locking

  • Record locking is the term used to describe the ability of a process to prevent other processes from modifying a region of a file while the first process is reading or modifying that portion of the file.
  • Figure 14.2 shows the forms of record locking provided by various systems.

fcntl Record Locking

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* struct flock *flockptr */ );
Returns: depends on cmd if OK(see following), -1 on error
  • For record locking, cmd is F_GETLK, F_SETLK, or F_SETLKW.
  • flockptr is a pointer to an flock structure.
struct flock
{
    short   l_type;     /* F_RDLCK, F_WRLCK, or F_UNLCK */
    short   l_whence;   /* SEEK_SET, SEEK_CUR, or SEEK_END */
    off_t   l_start;        /* offset in bytes, relative to l_whence */
    off_t   l_len;      /* length, in bytes; 0 means lock to EOF */
    pid_t   l_pid;      /* returned with F_GETLK */
};
  • This structure describes
    • The type of lock desired: F_RDLCK(a shared read lock), F_WRLCK(an exclusive write lock), or F_UNLCK(unlocking a region)
    • The starting byte offset of the region being locked or unlocked(l_start and l_whence)
    • The size of the region in bytes(l_len)
    • The ID(l_pid) of the process holding the lock that can block the current process(returned with F_GETLK only)
  • Numerous rules apply to the specification of the region to be locked or unlocked.
    • The l_whence member is specified as SEEK_SET, SEEK_CUR, or SEEK_END.
    • Locks can start and extend beyond the current end of file, but cannot start or extend before the beginning of the file.
    • l_len = 0: the lock extends to the largest possible offset of the file(until EOF). This allows us to lock a region starting anywhere in the file, up through and including any data that is appended to the file.
    • To lock the entire file, we set l_start as 0 and l_whence as SEEK_SET to point to the beginning of the file and specify a length(l_len) of 0.

  • Figure 14.3: The compatibility rule applies to lock requests made from different processes, not multiple lock requests made by a single process. If a process has an existing lock on a range of a file, a subsequent attempt to place a lock on the same range by the same process will replace the existing lock with the new one.
  • To obtain a read lock, the descriptor must be open for reading; to obtain a write lock, the descriptor must be open for writing. Three commands for the fcntl function:
    1. F_GETLK: Determine whether the lock described by flockptr is blocked by some other lock.
      -1- If a lock exists that would prevent our lock from being created, the information on that existing lock overwrites the information pointed to by flockptr.
      -2- If no lock exists that would prevent our lock from being created, the structure pointed to by flockptr is left unchanged except for the l_type member(set to F_UNLCK).
    2. F_SETLK: Set the lock described by flockptr.
      If we try to obtain a read lock(l_type = F_RDLCK) or a write lock(l_type = F_WRLCK) and the compatibility rule prevents the system from giving us the lock, fcntl returns with errno = EAGAIN. This command is also used to clear the lock described by flockptr(l_type = F_UNLCK).
    3. F_SETLKW: This command is a blocking version of F_SETLK.
      If the requested read lock or write lock cannot be granted because another process currently has some part of the requested region locked, the calling process is put to sleep. The process wakes up either when the lock becomes available or when interrupted by a signal.
  • Testing a lock with F_GETLK and then trying to obtain that lock with F_SETLK or F_SETLKW is not an atomic operation. Between the two fcntl calls, some other process may come in and obtain the same lock. If we don’t want to block while waiting for a lock to become available to us, we must handle the error returns from F_SETLK.
  • POSIX.1 doesn’t specify what happens when one process read locks a range of a file, a second process blocks while trying to get a write lock on the same range, and a third processes then attempts to get another read lock on the range. If the third process is allowed to place a read lock on the range, then the it can starve processes with pending write locks.
  • When setting or releasing a lock on a file, the system combines or splits adjacent areas as required. E.g., if we lock bytes [100, 199] and then unlock byte 150, the kernel maintains the locks on bytes [100, 149] and [151, 199].

  • If we then lock byte 150, the system would coalesce the adjacent locked regions into a single region [100, 199].

  • We normally use one of the following five macros:
#define read_lock(fd, offset, whence, len) \
                    lock_reg((fd), F_SETLK, F_RDLCK,(offset),(whence),(len))
#define readw_lock(fd, offset, whence, len) \
          
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值