今天读到了文件I/O这里。首先I/O函数就是用来打开文件,读文件,写文件等等。用到的函数就是 open,read,write,lseek以及close。这些都是不带缓存的I/O。
为了温习下Go,所以我决定用Go语言来实现一下AUPE提到的东西。
文件描述符号
对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,用o p e n或c r e a t返回的文件描述符标识该文件,将其作为参数传送给 r e a d或w r i t e。
openfile函数
openfile() 函数 第一个参数文件名字。第二个是flag参数。第三个是权限参数。
• O_RDONLY 只读打开。• O_WRONLY 只写打开。
• O_RDWR 读、写打开。
此外还有O_APPEND(每次写到文件尾),
O_CREATE(不存在就创建),
O_EXCL(可测试文件是否存在)等等。
Go语言实现版本
package main
import (
"os"
"fmt"
)
var path string = "AUPE.txt"
func main() {
f,err := os.OpenFile(path,os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil{
fmt.Println(err)
}
fmt.Printf("文件名: %s \n",f.Name())
fmt.Printf("文件描述符: %d \n",f.Fd())
f.Close()
}
输出:
文件名: AUPE.txt
文件描述符: 3
read函数
read函数需要一个buf,这个buf临时存放读出来的值。 而read函数返回的值是 读出来的字节数。如果以达到文件的尾端,则返回0。
也就是他时一点一点读。每次从文件读一个buf大小,并且放进buf中。
package main
import (
"os"
"fmt"
)
var path string = "AUPE.txt"
func check(e error){
if e != nil{
panic(e)
}
}
func main() {
f,err := os.Open(path)
check(err)
fmt.Printf("文件名: %s \n",f.Name())
fmt.Printf("文件描述符: %d \n",f.Fd())
buf := make([]byte,100)
ret,err := f.Read(buf)
check(err)
fmt.Printf("%d bytes: %s \n",ret,string(buf))
ret,err = f.Read(buf)
check(err)
fmt.Printf("%d bytes: %s \n",ret,string(buf))
f.Close()
}
输出结果:
文件名: AUPE.txt
文件描述符: 3
100 bytes: AUPE 读书笔记 -- (一)
AUPE 读书笔记 -- (二)
AUPE 读书笔记 -- (三)
AUPE 读书笔记 -
34 bytes: - (四)
AUPE 读书笔记 -- (五)
与read函数一样,write也是不带缓存的I/O函数。
文件共享
Linux 内核使用了三种数据结构。它们之间的关系决定了文件共享方面一个进程对另一个进程的影响。
这三个结构分别是进程表项,文件表,v节点表。
进程表项:
(a) 文件描述符标志。
(b) 指向一个文件表项的指针。
文件表:
(a) 文件状态标志(读、写、增写、同步、非阻塞等 )。
(b) 当前文件位移量。
(c) 指向该文件v节点表项的指针。
v节点表:
该节点包含了文件类型和对此文件进行各种操作的函数的指针信息。i节点就是inode信息。
假设现在有两个进程打开了相同的文件。A进程在文件描述符3上打开。而另一个进程则在文件描述符4上打开。打开文件的每个进程都得到一个文件表项。但是值对应一个v节点表项。
也就是多个进程操作同一个文件都能正常工作。每个进程都有他自己的文件表项,其中也有它自己的当前文件偏移量。在读文件的时候不会互相干扰。但是在同时写一个文件就有可能达不到预期的效果。为了避免该问题,就需要理解原子操作。