在写入的数据至少存储在内核的缓冲区缓存中之后,synchronize write入操作才会返回。
在读取数据存储在应用程序提供的用户空间缓冲器中之前,synchronize read操作不会返回。
另一方面, asynchronous write操作可以在数据甚至离开用户空间之前返回; asynchronous read操作可以在读取数据可用之前返回。也就是说,操作可能不会在请求时实际发生,而只是在往后排队。当然,在这种情况下,必须存在某种机制来确定操作何时实际完成。 以及成功的程度。
与单纯的synchronize操作相比,synchronized操作更具限制性和安全性。synchronized write操作将数据刷新到磁盘,确保磁盘上的数据始终是同步的。 相对于相应的内核缓冲区。synchronized read操作总是从磁盘返回最新的数据副本。
读取操作总是同步的,因为读取陈旧的数据没有什么意义。
读操作的同步性 。
通过O_SYNC标志使write synchronized,,通过fsync()确保所有I/O在是synchronized。
Asynchronous I/O
#include <aio.h>
/* asynchronous I/O control block */
struct aiocb {
int aio_fildes; /* file descriptor */
int aio_lio_opcode; /* operation to perform */
int aio_reqprio; /* request priority offset */
volatile void *aio_buf; /* pointer to buffer */
size_t aio_nbytes; /* length of operation */
struct sigevent aio_sigevent; /* signal number and value */
/* internal, private members follow... */
};
int aio_read (struct aiocb *aiocbp);
int aio_write (struct aiocb *aiocbp);
int aio_error (const struct aiocb *aiocbp);
int aio_return (struct aiocb *aiocbp);
int aio_cancel (int fd, struct aiocb *aiocbp);
int aio_fsync (int op, struct aiocb *aiocbp);
int aio_suspend (const struct aiocb * const cblist[],
int n,
const struct timespec *timeout);
I/O Scheduler and I/O Performance
在现代系统中,磁盘与系统其余部分之间的相对性能差距相当大,而且还在不断扩大。
磁盘性能的最糟糕组件是将读/写头从磁盘的一部分移动到另一个部分的过程,这是一种称为seek的操作。
鉴于磁盘驱动器与系统其他部分在性能上的差异,按照发出I/O请求的顺序向磁盘发送I/O请求将是极其粗糙和低效的。 因此,现代操作系统内核实现I/O调度器,它通过操作I/O请求的服务顺序和时间来最小化磁盘的数量和大小。I/O调度程序努力减少与磁盘访问相关的性能损失。
The Life of an I/O Scheduler
I/O调度程序执行两个基本操作:合并和排序。
合并·:
考虑两个请求,一个从磁盘块5读取,另一个从磁盘块6到7读取,这些请求可以合并为一个从磁盘块5到7读取的请求。 I/O的其他数量可能相同,但I/O操作的数量减少了一半。
排序:
例如,给定对块52、109和7的I/O操作,I/O调度程序将这些请求排序为顺序7、52和109。如果向第81项发出请求,则将插入 在对块52和109的请求之间。然后,I/O计划程序将按它们在队列中的顺序将请求分派到磁盘:7、然后52、81和109。
Helping Out Reads
每个读取请求必须返回最新的数据。因此,如果请求的数据不在页缓存中,则读取过程必须阻塞,直到数据可以从磁盘读取-这可能是一种冗长的操作。 我们称这种性能影响为read latency。
Optimzing I/O Performance
Sechedling I/O in user space
sort based on:
- The full path
- The inode number
- The physical disk block of the file
Sorting by path
通过路径名进行排序是最简单、最有效的逼近块排序的方式。
文件系统越支离破碎,按路径排序的用处就越小。
Sorting by inode
每个文件都有一个与其相关联的inode,并且为inode分配了唯一的编号。
//怎么获取inode
int get_inode(int fd)
{
struct stat buf;
int ret;
ret = fstat(fd, &buf);
if(ret < 0){
perror("fstat");
return -1;
}
return buf.st_ino;
}
int Print_inode_Number(int argc, char* argv[]) {
int fd, inode;
if(argc<2){
fprintf(stderr, "usage: %s <file>\n", argv[0]);
return 1;
}
fd = open(argv[1], O_RDONLY);
if(fd<0){
perror("open");
return 1;
}
inode = get_inode(fd);
printf("%s - inode: %d\n", argv[1], inode);
if(close(fd)<0){
perror("close");
}
return 0;
}
int main(int argc, char*argv[])
{
Print_inode_Number(argc, argv);
return 0;
}
测试结果
这是在用户空间中调度I/O请求的最常用方法。
Sorting by physical block
当然,设计您自己的电梯算法的最佳方法是按物理磁盘块进行排序。
内核提供了从文件的逻辑块号获取物理磁盘块的方法。
ret = ioctl(fd, FIBMAP, &block);
if(ret < 0)
perror("ioctl");
在这里,fd是所讨论文件的文件描述符,block是我们想要确定的物理块的逻辑块。
在成功返回时,block将被替换为物理块编号。
传入的逻辑块是零索引和文件有关的。也就是说,如果一个文件由八个逻辑块组成,那么有效值是0到7。
因此,寻找逻辑到物理块映射是一个两步的过程。
首先,我们必须确定给定文件中的块数。这是通过stat()系统调用完成的。
第二,对于每个逻辑块,我们必须发出一个ioctl()请求,以查找对应的物理块。
int get_block(int fd, int logical_block)
{
int ret;
ret = ioctl(fd, FIBMAP, &logical_block);
if(ret < 0) {
perror("ioctl");
return -1;
}
return logical_block;
}
int get_nr_blocks(int fd)
{
struct stat buf;
int ret;
ret = fstat(fd, &buf);
if(ret < 0){
perror("fstat");
return -1;
}
return buf.st_blocks;
}
void print_blocks(int fd)
{
int nr_blocks, i;
nr_blocks = get_nr_blocks(fd);
if(nr_blocks < 0){
fprintf(stderr, "get_nr_blocks failed!\n");
return;
}
if(nr_blocks == 0){
printf("no allocared blocks\n");
return;
}
else if(nr_blocks == 1){
printf("1 block\n\n");
}
else{
printf("%d blocks\n\n", nr_blocks);
}
for(i = 0; i < nr_blocks; ++i){
int phys_block = get_block(fd, i);
if(phys_block < 0){
fprintf(stderr, "get_block failed!\n");
return;
}
if(!phys_block){
continue;
}
printf("(%u, %u)", i, phys_block);
}
putchar('\n');
}
int logical_to_physical(int argc, char **argv) {
int fd;
if(argc < 2){
fprintf(stderr, "usage: %s <file>\n", argv[0]);
return 1;
}
fd = open(argv[1], O_RDONLY);
if(fd < 0){
perror("open");
return 1;
}
print_blocks(fd);
if(close(fd) < 0){
perror("close");
}
return 0;
}
int main(int argc, char*argv[])
{
logical_to_physical(argc, argv);
return 0;
}
网友评论