Chapter 4. File I/O: The Universal I/O Model
Changing the File Offset: lseek()
off_t lseek(int fd, off_t offset, int whence);
- SEEK_SET
- SEEK_CUR
- SEEK_END
如果文件系统产生hole
,这段内容读出来是0
Chapter 5. File I/O: Further Details
Relationship Between File Descriptors and Open Files
file offset记录在open file table中
Duplicating File Descriptors
Chapter 13. File I/O Buffering
Kernel Buffering of File I/O: The Buffer Cache
- read()、write()系统调用并不直接写到磁盘,而是和cache打交道。
- 虽然read()、write()很快,但最好以
BUF_SIZE
传输效率最高,大小4K。系统调用负担也很大。
Controlling Kernel Buffering of File I/O
int fsync(int fd); //针对某个文件
void sync(void);//针对整个系统
打开文件用O_SYNC
参数,每次write后自动flush
fd = open(pathname, O_WRONLY | O_SYNC);
Summary of I/O Buffering
fflush()保证调用了write()系统调用,fsync(),sync()保证写到了磁盘上。
Bypassing the Buffer Cache: Direct I/O
用O_DIRECT
参数打开文件,不走kernel cache,影响性能。
Chapter 21. Signals: Signal Handlers
Interruption and Restarting of System Calls
- We establish a handler for some signal.
- We make a blocking system call, for example, a read() from a terminal device, which blocks until input is supplied.
- While the system call is blocked, the signal for which we established a handler is delivered, and its signal handler is invoked.
默认情况下,read()会返回EINTR。在设置flag为SA_RESTART
时,system call会自动重启。
Chapter 23. Timers and Sleeping
Setting Timeouts on Blocking Operations
参见书中例子
Chapter 24. Process Creation
Overview
- fork()
- exit()
- wait()
- execve() //argv + envp, 系统调用,其他函数(exec)调用这个函数
File Sharing Between Parent and Child
When a fork() is performed, the child receives duplicates of all of the parent’s file descriptors. These duplicates are made in the manner of dup().
下面的例子证明了,父子进程共享文件,子进程修改描述符的offset,父进程也同样结果。
Memory Semantics of fork()
为了避免拷贝内存:
- 代码段设置为只读,父子进程的代码段指向同一段物理内存。
- 对于 data, heap, and stack,Linux内核采用
copy-on-write.
The vfork() System Call
和fork的区别:
- 父子进程共享内存
- 父进程不需要调用wait(),就可以等待子进程退出。
Chapter 27. Program Execution
Executing a New Program: execve()
exec家族函数是调用execve()实现的
/proc/PID/exe
是对运行文件的符号链接。
Interpreter Scripts
#! interpreter-path [optional-arg]
File Descriptors and exec()
$ ls /tmp > dir.txt
以上的shell脚本做了几件事
- A fork() is performed to create a child process that is also running a copy of the shell
- The child shell opens dir.txt for output using file descriptor 1
- The child shell closes descriptor 1 (STDOUT_FILENO) and then opens the file dir.txt.
- The shell opens dir.txt, obtaining a new file descriptor.
- The child shell execs the ls program.
The close-on-exec flag (FD_CLOEXEC)
在执行exec函数前,关掉FD。此功能可以用fcntl()查看,并且修改。
Chapter 55. File Locking
Overview
- flock():整个文件上锁
- fcntl():文件的某个范围上锁
为了使用lock,而不被stdio的缓存机制破坏,调用setbuf() 关闭stdio缓存。
File Locking with flock()
Semantics of Lock Inheritance and Release
释放锁可以通过:
-
LOCK_UN
操作 - 所有关联的FD都关闭
锁的信息存在Open file table
中,而不是存在FD中。flock操作的是内核中的file object,而不是FD。下图通过dup句柄,然后对新句柄操作,就可以释放锁(因为这两个FD都指向同一个file object)。
下图说明两个不同open()会产生两个不同file。第二次lock会被锁住。
fork()后得到的子进程的FD,其实dup父进程得到的,所以子进程可以解锁lock.
flock()的限制
- Only whole files can be locked.
- We can place only advisory locks with flock().
- Many NFS implementations don’t recognize locks granted by flock().
Record Locking with fcntl()
lock split
Lock Limits and Performance
Semantics of Lock Inheritance and Release
下图的例子,说明close(fd2)就可以关闭lock,即使lock发生在fd1上。
Mandatory Locking
使用Mandatory Locking的先决条件
# mount -o mand /dev/sda10 /testfs
The /proc/locks File
其中第6列的含义是major dev:minor dev:inode number
确定ID 3:7 是 /dev/sda7
箭头代表被block的lock
Running Just One Instance of a Program
The /var/run
directory is the usual location for such lock files. For example, syslogd creates the file /var/run/syslogd.pid
.