Introduction: Advanced I/O includes non-blocking I/O, record locks, System V-flow mechanisms, I/O multiplexing (SELECT and poll functions), READV and Writev functions, and storage-mapped I/O.
(i) Non-blocking I/O
A class of system calls that may cause the process to block forever are:
1. If data for some file types does not exist, the read operation may cause the caller to block forever.
2, if the data is not immediately accepted by the same type of file, then the write operation will also cause the caller to block forever.
3. Opening certain types of files will block until a certain condition occurs.
4. Read and write files that have been added to the mandatory record lock.
5, some IOCTL operation.
6, some inter-process communication functions.
There are two ways to specify non-blocking I/O for a given descriptor:
1. If you call open to get a descriptor, you can specify the O_NONBLOCK flag.
2. For a descriptor that has already been opened, you can call fcntl, and the function opens the O_nonblock file status flag.
/* *file name:nonblock.c *author:libing *mail: [email protected] */#include <stdio.h> #include < stdlib.h> #include <fcntl.h> #include <unistd.h>char buf[500000];void set_fl (int fd, int flags)/*flags is file status Falgs to turn On*/{int val;if ((val = fcntl (fd, F_GETFL, 0)) < 0) printf ("Fcntl F_GETFL error"); Val |= fl ags;/* Open the FLAGS flag */if (Fcntl (FD, F_SETFL, Val) < 0) printf ("Fcntl F_SETFL Error");} void Clr_fl (int fd, int flags)/*flags is file status Falgs to turn On*/{int val;if (val = fcntl (fd, F_GETFL, 0)) < 0) printf ("Fcntl F_GETFL error"); Val &= ~flags;/* Close the FLAGS flag */if (Fcntl (FD, F_SETFL, Val) < 0) printf ("Fcntl F_SETFL err or ");} Intmain (void) {int Errno;char *ptr;int ntowrite, nwrite;ntowrite = Read (Stdin_fileno, buf, sizeof (BUF)); fprintf (stderr, "Read%d bytes\n", ntowrite); Set_fl (Stdout_fileno, O_nonblock); /* Set non-blocking flag o_nonblock*/ptr = Buf;while (Ntowrite > 0) {errno = 0;nwrite = Write (Stdout_fileno, PTR, ntowrite); fprintf ( StdErr "Nwrite =%d, errno =%d\n", Nwrite, errno); if (Nwrite > 0) {ptr + = nwrite;ntowrite-= Nwrite;}} CLR_FL (Stdout_fileno, o_nonblock);/* Set non-blocking flag o_nonblock*/exit (0);}Cons: For non-blocking operations, the general form is polling, which wastes CPU time on multi-user systems. You can use Select and poll to solve the problem of CPU waste, which is described separately later.
(ii) record lock
The function of record locks is that when a process is reading or modifying a portion of a file, it can prevent other processes from modifying the same file area.
#include <fcntl.h>
int fcntl (int fd, int cmd, .../*struct flock *flockptr*/);//return value: If the success is dependent on CMD, if the error returns-1.
Flockptr is a pointer to the flock structure (in the Bits/fcntl.h file).
struct flock {short int l_type;/* type of lock:f_rdlck, F_wrlck, or f_unlck.*/short int l_whence;/* Where ' L_start ' is relative to (like ' Lseek '). */#ifndef __use_file_offset64 __off_t l_start;/* Offset where the lock begins. */ __off_t l_len;/* Size of the locked area; zero means until EOF. */#else __off64_t l_start;/* Offset where the lock begins. */ __off64_t l_len;/* Size of the locked area; zero means until EOF. */#endif __pid_t l_pid;/* Process holding the lock. */ };
The flock structure is described as follows:
1. The desired lock type: F_RDLCK (Shared read lock), F_WRLCK (exclusive write lock), F_unlck (unlocks an area).
2. The starting byte offset to lock or unlock the area, which is determined by both L_start and l_whence.
3, the byte length of the region, represented by L_len.
4, with the lock that can block the current process, the ID of its holding process is stored in l_pid.
Note about areas that lock and unlock are also important:
1, L_start is the relative offset (bytes), l_whence determines the relative offset of the starting point. This is similar to the last two parameters in the Lseek function. Indeed, the values that l_whence can choose are Seek_set, Seek_cur, and Seek_end.
2. The area can start at the end of the current file or at the end of its tail, but not before the file start position.
3, if the L_len is 0, the area of the lock will start at its starting point (determined by L_start and l_whence) until the maximum possible offset, that is, regardless of how much data is added to the file, they are within the scope of the lock.
4, in order to lock the entire file, set L_start and l_whence, so that the beginning of the lock at the beginning of the file, and the length of the description (L_len) is 0.
There are two types of proceeds: shared read locks (l_type to F_rdlck) and exclusive write locks (f_wrlck). Basic rule: Multiple processes can have a shared read lock on a given byte, but there can be only one write lock for a single process on a given byte. If there is already one or more read locks on a given byte, no more write locks can be added to the byte, and if an exclusive write lock is already on a byte, no more read locks can be added to it.
The rules described above apply to lock requests made by different processes and do not apply to multiple lock requests made by a single process. If a process already has a lock on a file interval, and then the process attempts to add a lock to the same file interval, the new lock replaces the old lock.
When a lock is read, the descriptor must be read-open, and the descriptor must be write-open when a write lock is added.
Depending on the cmd, the three commands for the FCNTL function are as follows:
1. F_SETLK set the lock described by Flockptr. If you attempt to establish a read lock (L_type is set to F_rdlck) or a write lock (L_type is set to F_wrlck), if it fails, fcntl immediately returns with an error, and errno is set to eaccess or Eagain at this time. This command is also used to clear the lock described by Flockptr (L_type is F_unlck).
Example code:
#define READ_LOCK (FD, offset, whecne, Len) Lock_reg (FD), F_setlk, F_rdlck, (offset), (whence), (len)) #define Write_lock ( FD, offset, whecne, Len) Lock_reg (FD), F_setlk, F_wrlck, (offset), (whence), (len)) #define UN_LOCK (FD, offset, whecne, le n) Lock_reg (FD), F_setlk, F_unlck, (offset), (whence), (len)) int Lock_reg (int fd, int cmd, int type, off_t offset, int wh ence, off_t len) {struct flock lock;lock.l_type = type; /*f_rdlck, f_wrlck, f_unlck*/lock.l_start = offset; /*byte offset, relative to l_whence*/lock.l_whence = whence; /*seek_set, seek_cur, Seek_end*/lock.l_len = Len;return (Fcntl (fd, CMD, &lock)); }
2.F_SETLKW This is a blocked version of F_setlk. If the lock requested by FLOCKPTR cannot be created because the other process already has a lock in one part of the requested interval, the calling process sleeps. If the lock created by the request is available, or if hibernation is interrupted by a signal, the process is awakened.
3,f_getlk to determine whether the lock described by FLOCKPTR will be rejected by another lock (blocking). If there is a lock that prevents the creation of a lock described by flockptr, the information for that existing lock is written to the structure that the flockptr points to. If this is not the case, other information in the structure pointed to by Flockptr remains the same, except that L_type is set to F_unlck.
#define IS_READ_LOCKABLE (FD, offset, whecne, Len) Lock_reg (FD), F_setlk, F_rdlck, (offset), (whence), (len)) #define Is_w Rite_lockable (FD, offset, whecne, Len) Lock_reg (FD), F_setlk, F_wrlck, (offset), (whence), (len)) int lock_test (int fd, in T type, off_t offset, int whence, off_t len) {struct flock lock;lock.l_type = type; /*f_rdlck, F_wrlck*/lock.l_start = offset; /*byte offset, relative to l_whence*/lock.l_whence = whence; /*seek_set, seek_cur, Seek_end*/lock.l_len = len;if (Fcntl (FD, F_GETLK, &lock) < 0) printf ("Fcntl error"); Lock.l_type = = F_unlck) return 0; /*false, region isn ' t locked by another proc*/return (LOCK.L_PID); /*true, return pid of lock owner*/}
Use F_GETLK to test whether a lock can be established and then attempt to establish a lock with f_setlk and F_SETLKW, which is not an atomic operation. There is therefore no guarantee that there will not be another process inserted between the two fcntl calls and that a related lock be created so that the original test is changed. If you do not want long-term blocking that may occur when a lock is established, you should use F_SETLK and test the returned results to determine whether the required locks were successfully established.
Deadlock: If two processes are waiting for each other to hold and lock resources, the two processes are in a deadlock state.
Example of deadlock on Apue:
#include <fcntl.h>static voidlockabyte (const char *name, int fd, off_t offset) {if (Write_lock (FD, offset, seek_set, 1) < 0) Err_sys ("%s:writew_lock error", name);p rintf ("%s:got the Lock,byte%ld\n", name, offset);} Intmain (void) {int fd;pid_t pid;/* *create a file and write both bytes to it. */if (fd = creat ("Templock", File_mode) < 0) Err_sys ("creat errror"); if (Write (FD, "AB", 2)! = 2) Err_sys ("Write error"); Tell_wait (); if ((PID = fork ()) < 0) {Err_sys ("fork Error");} else if (PID = = 0) {lockabyte ("child", FD, 0); Tell_parent (Getppid ()); Wait_parent (); Lockabyte ("Child", FD, 1);} Else{lockabyte ("Parent", FD, 1); Tell_parent (PID); Wait_parent (); Lockabyte ("PARENT", FD, 0);} Exit (0);}
inheritance and release rules for Locks:
1, the lock is related to the process and the file. When a process terminates, the locks it creates are all released, and whenever a descriptor is closed, any lock on the file that the process can reference by this descriptor is freed.
2, the child process generated by the fork does not inherit the lock set by the parent process.
3. After exec executes, the new program can inherit the lock of the original executing program. However, if the CLOS_ON_EXEC flag is set on a file descriptor, all locks on the corresponding file are freed when the file descriptor is closed as part of exec.