在了解基础I/O基础函数之前,我们很有必要先了解一下文件描述符有关知识,其中的不懂之处可以留作疑问再来看这篇文章,参考博客:https://blog.csdn.net/Sun_Life_/article/details/90049499
打开或创建一个文件并返回文件描述符
Q:为什么有了open还有openat,两者有什么区别?
A:openat是POSIX.1新增的函数,为了解决:
// --------------process of setuid program-------
//
if (access("filePathName", W_OK))
{
exit(EXIT_FAILURE);
}
// ----------------------------------------------
// --------------process of attacer--------------
//
// After the access check
unlink("filePathName");
symlink("/etc/passwd", "filePathName");
// Before the open, "file" points to the password database
// ----------------------------------------------
// --------------process of setuid program-------
//
fd = open("filePathName", O_WRONLY);
// Actually writing over /etc/passwd
write(fd, buffer, sizeof(buffer));
// ----------------------------------------------
在这个原本的setuid程序中,先用access()检查当前程序/进程的真实用户是否对制定的文件具有写权限,如果有,则对其写入相应的内容,否则异常退出,假如我们在access和open之间的时间片中将setuid程序的写入点改变为了/etc/passwd,而open的检查可以顺利通过(euid为0),从而向敏感文件写入数据,最终达到提权等目的。区别就是openat的fd参数,共有3种可能:
fd参数会被忽略,openat就相当于open函数
fd参数指出了相对路径名在文件系统中的开始地址(fd参数通过打开相对路径名所在的目录获取)
路径名在当前目录中获取,openat在操作上与open类似
由open和openat函数返回的文件描述符一定是最小的未使用文件描述符数值
一个典型的例子:平常我们打开一个文件,基本原理就是先关闭掉文件描述符1(标准输出),然后执行打开操作,文件就会在文件描述符1上被打开,具体怎么保证一定会在特定的文件描述符上打开呢?我们稍后会了解到dup2函数。
path:要打开或创建文件的名字
oflag:以下列一个或多个选项做“或”运算构成
五选一:
O_RDONLY:只读打开
O_WRONLY:只写打开
O_RDWR:读写打开
O_EXEC:只执行打开
O_SEARCH:只搜索打开
其余可选项(为大家列出来,不做过多说明):
创建新文件,等效于open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
缺点是以只写方式打开创建的文件,必须先creat写再close,在open(先写再读)
关闭一个打开的文件,同时释放加在该文件上的所有记录锁。
记录锁(record locking):当一个进程正在读或修改文件的某个部分时,记录锁阻止其他进程修改同一文件区。因为UNIX内核没有文件记录的概念,而它锁定的只是文件中的一个区域或整个文件,所以更合适的应该是字节范围锁(byte-range locking)。
显式地为一个打开文件设置文件偏移量(文件描述符指向管道、FIFO或套接字,返回-1,并将errno设置为ESPIPE)
当前文件偏移量(current file offset):每一个打开的文件都有一个非负整数的当前文件偏移量,计算从文件开始计算的字节数,读、写都是从当前偏移量开始并增加所读写的字节数,系统默认偏移量为0(除非指定O_APPEND)
whence:
从fd打开的文件中读取nbytes个字节放入buf中,read成功,返回读到的字节数,如已到达文件末尾返回0。
可以使实际读到的字节数少于要求读的字节数的情况:
到达文件尾端之前还有30个字节,要求读取50个字节,read返回30,再次调用read返回0(文件尾端)
非规范模式输入处理,输入字符不配装成行
从buf中向fd打开的文件写入nbytes个字符,返回值通常与nbytes的值相同,否则表示逻辑上出错,出错通常因为磁盘已满或超过了文件的长度限制
若设定O_APPEND则每次写操作前将文件偏移量设置在文件结尾处,写成功后文件偏移量增加实际写的字节数
复制一个现有的文件描述符
dup返回当前可用文件描述符的最小数值,dup2返回fd2参数指定的新描述符值,如果fd2已经打开先将其关闭
如果fd等于fd2,dup2返回fd2,而不关闭它,否则fd2的FD_CLOEXEC文件描述符被清除,fd2在进程调用exec是打开状态
dup(fd);等效于fcntl(fd, F_DUPFD, 0);
dup2(fd, fd2);等效于close(fd2); fcntl(fd, F_DUPFD, fd2);
1.dup2是原子操作,close、fcntl可能在close和fcntl之间调用信号捕捉函数,可能修改文件描述符,线程也同样不安全
2.dup2和fcntl有不同的errno
UNIX系统内核中设有高速缓存或页高速缓存,大多数I/O都通过缓冲区进行。写入数据时,内核先将数据复制到缓冲区中,然后排入队列,之后再写入磁盘,也称作延迟写
假如内核需要通过缓冲区来存放其他磁盘数据,它会把所有延迟写数据块写入磁盘。而这三个函数则是操作系统用来保证磁盘上实际文件系统与缓冲区中内容的一致性
sync:只将所有修改过的块缓冲区排入写队列后返回,并不等待实际写磁盘操作结束(update守护进程冲洗块缓冲区)
fsync:只对文件描述符fd指定的文件起作用,等待磁盘操作结束后才返回
fdatasync:类似于fsync,但只影响文件的数据部分
改变已经打开文件的属性
通常用于终端I/O操作