linux基础11 | IO函数
IO 函数
文件基本操作
- 打开
- 读取
- 写入
- 关闭
需要引入如下头文件
1 |
open
1 | int open(const char *pathname, int flags); |
需要注意的是上面的函数实际上是通过多参数va_list实现的,C 库函数函数中没有函数重载。
- const char * pathname
- 可以填写相对路径和绝对路径
- int flags(常用
- O_CREAT 当文件不存在创建。
- O_RDONLY, 顾名思义,只读
- O_WRONLY 只写
- O_RDWR. 读写都有
- O_APPEND 在文件末尾write添加
- O_TRUNC 当文件不存在创建并清零。(常用在管道
- O_CLOEXEC 是原子操作 ,当执行exec 自动关闭该文件描述符,防止EXEC后的子进程调用该文件描述符。
- mode_t mode
- 当文件不存在创建时,需要有mode值,也就是文件的权限
- S_IRWXU | S_IRWXG | S_IRWXO 分别对应
00700|00070|00007(也可以知直接填数字,这样更快 - S_I 固定开头 ,中间有三个W(写) R(读) X(执行) 有三个固定结尾 USR(用户) GRP(组) OTH(其他人)
- return int
- 返回 成功 0 失败-1 并且perror()打印错误 ;
read
1 | ssize_t read(int fd, void *buf, size_t count); |
- int fd
- 文件描述符,不止是普通文件,也是socket or fifo ,甚至可以是 stderr,stdout ,stdin
- void * buf
- 要读的数据存入哪
- 这玩意可以进行偏移,单位是字节(多进程拷贝啥的。
- 可以是结构体,实现方式其实是用ELF格式解析。
- 网络数据包之类的 比如socket 传递的包
- size_t count
- 要读的数据大小。
return ssize_t
实际读了多少字节
读完读到文件尾返回0 ,没读到文件结尾但是成功读取返回成功读取的字节数(一般文件)
当读取的是socket的文件描述符时。需要注意的是,返回0有很多个意义。
对端或者本端关闭socket文件描述符或者读到FIN(没差)。
对端客户端关闭后导致的服务端的read即使返回值为0,服务端也可以继续进行写。
当读取小于0可以检查信号位,检测是否是信号中断影响
ELF
在计算机科学中,是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件的文件格式。
是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的,也是Linux的主要可执行文件格式。
1999年,被86open项目选为x86架构上的类Unix操作系统的二进制文件标准格式,用来取代COFF。因其可扩展性与灵活性,也可应用在其它处理器、计算机系统架构的操作系统上。
ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。实际上,一个文件中不一定包含全部内容,而且它们的位置也未必如同所示这样安排,只有ELF头的位置是固定的,其余各部分的位置、大小等信息由ELF头中的各项值来决定。
结构体信息通常都放在ELF header中
write
1 | ssize_t write(int fd, const void *buf, size_t count); |
- int fd
- 文件描述符,不止是普通文件,也是socket or fifo ,甚至可以是 stderr,stdout ,stdin
- void * buf
- 要读的数据存入哪
- 这玩意可以进行偏移,单位是字节(多进程拷贝啥的。
- 可以是结构体。
- 网络数据包之类的 比如socket 传递的包
- size_t count
- 要读的数据大小。
- return ssize_t
- 实际读了多少字节
close
1 | int close(int fd); |
关闭文件描述符,释放进程的PCB中资源
需要注意的是,close不是单纯的关闭文件描述符,在设计多个进程共用一套PCB时,close关闭的文件描述符的操作是将,该当前进程下的文件描述符的引用计数(类似Windows下HANDLE句柄 )减1,当彻底为零时,才是彻底释放进程中的PCB的资源和所占用的内存资源。
文件描述符的操作函数
dup
复制文件描述符
1 | int dup(int oldfd); |
实际上就是输出重定向常用函数
返回新的文件描述符
dup2可以指定重定向指定的文件描述符
fsync
文件数据同步
1 | int fsync(int fd); |
强制将缓存中的数据写入磁盘
要求写入立刻生效,谨慎调用
调用的需要:
硬盘和内存之间的交互有两个缓存区,交互这两个缓存区是同步的,在从缓存区传到硬盘或内存中。
当需要立刻将数据写入文件操作执行完成,可以调用该函数。
但是调用这个是会占用是时间的,根据你的缓存调度速度,硬件配置等因素。
文件读写位置指针
钟硕肘子, 文件呢,读取的时候,又有提供一个指针的,指向文件内数据的位置。
我们可以通过lseek函数将指针进行位置偏移。
1 | off_t lseek(int fd, off_t offset, int whence); |
int fd
- 要操作的文件的描述符
off_t offset
- 偏移量 or 读写位置
int whence
- 可以理解为操作设置
- SEEK_SET
- 设置当前读写位置为offset
- SEEK_CUR
- 将当前的读写位置偏移offset字节
- 有读权限的话,即使偏移的位置没有值也会补0补到偏移的指针位置,否则就会失败
- SEEK_END
- 指向末尾后,在偏移offset字节
return off_t
Upon successful completion, lseek() returns the resulting offset location as measured in bytes from the beginning of the file. On error, the value (off_t) -1 is returned and errno is set to indicate the error.
翻译过来就是成功返回处理后的从0开始的读写位置
失败返回-1并 返回error
创建临时文件
1 |
|
man文档描述:
The mkstemp() function generates a unique temporary filename from tem‐ plate, creates and opens the file, and returns an open file descriptor for the file. The last six characters of template must be "XXXXXX" and these are replaced with a string that makes the filename unique. Since it will be modified, template must not be a string constant, but should be declared as a character array. The file is created with permissions 0600, that is, read plus write for owner only. The returned file descriptor provides both read and write access to the file. The file is opened with the open(2) O_EXCL flag, guaranteeing that the caller is the process that creates the file.
翻译总结下来得出:
- 生成一个临时的temp 文件,并且放回fd该文件的文件描述符
- 该文件的生命周期,通常是close()结束,不保证数据长期有效
- char * temporary
- 有固定的内容,尾部必须是“XXXXXX”前面倒是无所谓。
文件锁
1 |
|
给文件上锁,放置文件在进程同步中出现访问问题。(防止篡改
int operation
- LOCK_SH 建立共享锁定,可以超过多个进程使用进入访问这个锁,虽然都可以访问这个锁,但是只能读,不可以写。
- LOCK_EX 建立互斥锁定,只能有一个程序使用这个文件。
- LOCK_UN 解锁文件访问状态。实际上文件close或异常关闭的时候也是可以自动解锁的。(有时差,并不是立刻解锁(比如网络套接字有协议方面的等待。
- LOCK_NB 非阻塞锁,如果无法建立锁定,不会等待阻塞进程他会立刻返回进程。
return int
- 成功返回0 失败返回-1并输出error
在消息分发与事件处理起很大的作用
文件锁特性测试
1 | int main() { |
执行这个程序,开两个进程,可以发现当第一个进程结束后,第二个进程才开始对这个文件进行写的操作。
但是当其他不同的进程并没有给这文件加锁,而是直接进行打开写入也是可以成功的,无视锁的限制。
1 | const char* fileName = "test.txt"; |
说明文件锁这玩意,只有对其进行认知,设置的进程有效,是一个建议锁,其他进程因为不知道锁,就相当于没有,可以直接写入。
那么问题来了,如果,我将 写入与上锁语句顺序修改又会如何呢?
1 | flock(fd, LOCK_EX); |
总结:一样, 其他没有认知的进程一样可以直接进行写入访问。所以文件锁不过是建议锁罢了
FCNTL
1 | int fcntl(int fd, int cmd, ... /* arg */ ); |
文件控制函数
可变参数函数。
- int fd
- 要操作的文件描述符
- int cmd
- F_DUPFD ,dup fd 作用与dup类似,将旧的文件描述符复制到新的位置,这个新的位置根据后方传入的arg ,大于或等于,并且未使用的文件描述符的位置,不是重定向,虽然共享同一块进程控制块PCB(文件表项),但是新描述符又自己的的一套文件描述符标志,其中FD_CLOEXEC (close on exec, not on-fork)文件描述符标志清除(执行exec后的新进程不会自动释放fd,也能使用这个fd(管道))。返回值成功返回新的文件描述符
- F_GETFD 取FD 文件标志 ,一般是用来判断FD_CLOEXEC,返回值就是标志;
- F_SETFD 设置FD 的文件标志 。
- F_GETFL 取得文件描述符的状态标志,也就是读写权限的标志 O_APPEND,之列的
- F_SETFL 设置FD 文件状态标志:不过只能设置三个位 O_APPEND,O_NONBLOCK (非阻塞访问),O_ASYNC:(异步访问)
- F_GETLK 得到锁状态
- F_SETLK 设置文件锁状态。此时传入的flock 结构的I_type 值必须是F_RDLC,(读锁)F_WRLCK (写锁),F_UNLCK 解锁状态。
- F_SETLKW(wait) 同上,区别不同的是,当无法设置锁的时候,他会一直等待锁的权限阻塞进程,直到成功return 0。或者,直到信号影响为止,返回-1 错误码为EINTR。