Linux read的核心函数generic_file_buffered_read

2023-11-11

内核:5.9.0

流程图

 generic_file_buffered_read一种调用路径(cat某个文件触发):

#0  ondemand_readahead (mapping=0xffff888005c61340, ra=0xffff888005927598, filp=0xffff888005927500, hit_readahead_marker=false, index=0, req_size=16) at mm/readahead.c:445
#1  0xffffffff812eeea1 in page_cache_sync_readahead (req_count=<optimized out>, index=<optimized out>, filp=<optimized out>, ra=<optimized out>, mapping=<optimized out>) at mm/readahead.c:585
#2  page_cache_sync_readahead (mapping=<optimized out>, ra=0xffff888005927598, filp=0xffff888005927500, index=<optimized out>, req_count=16) at mm/readahead.c:567
#3  0xffffffff812dcae7 in generic_file_buffered_read (iocb=0xffffc90000033cc0, iter=<optimized out>, written=0) at mm/filemap.c:2199
#4  0xffffffff812dd8ed in generic_file_read_iter (iocb=0xffffc90000033cc0, iter=0xffffc90000033c98) at mm/filemap.c:2507
#5  0xffffffff814c7fc9 in ext4_file_read_iter (to=<optimized out>, iocb=<optimized out>) at fs/ext4/file.c:131
#6  ext4_file_read_iter (iocb=0xffffc90000033cc0, to=0xffffc90000033c98) at fs/ext4/file.c:114
#7  0xffffffff81405c0f in call_read_iter (file=<optimized out>, iter=<optimized out>, kio=<optimized out>) at ./include/linux/fs.h:1876
#8  generic_file_splice_read (in=0xffff888005927500, ppos=0xffffc90000033da8, pipe=<optimized out>, len=<optimized out>, flags=<optimized out>) at fs/splice.c:312
#9  0xffffffff81407b51 in do_splice_to (in=0xffff888005927500, ppos=0xffffc90000033da8, pipe=0xffff8880058fb6c0, len=65536, flags=<optimized out>) at fs/splice.c:890
#10 0xffffffff81407cab in splice_direct_to_actor (in=<optimized out>, sd=0xffffc90000033e00, actor=<optimized out>) at fs/splice.c:970
#11 0xffffffff81408012 in do_splice_direct (in=<optimized out>, ppos=0xffffc90000033ea8, out=0xffff888005927400, opos=0xffffc90000033eb0, len=16777216, flags=<optimized out>) at fs/splice.c:1079
#12 0xffffffff813ae9b1 in do_sendfile (out_fd=<optimized out>, in_fd=<optimized out>, ppos=0x0 <fixed_percpu_data>, count=<optimized out>, max=<optimized out>) at fs/read_write.c:1548
#13 0xffffffff813af30b in __do_sys_sendfile64 (count=<optimized out>, offset=<optimized out>, in_fd=<optimized out>, out_fd=<optimized out>) at fs/read_write.c:1609
#14 __se_sys_sendfile64 (count=<optimized out>, offset=<optimized out>, in_fd=<optimized out>, out_fd=<optimized out>) at fs/read_write.c:1595

generic_file_buffered_read核心逻辑:

  • 尝试从page cache(address_space数据结构)中查找,如果命中返回。
  • 如果未在page cache中,readpage从磁盘读取数据到page cache中。
  • 按需(不是所有场景下才会预读)预读(readahead),提升IO性能。

/**
 * generic_file_buffered_read - generic file read routine
 * @iocb:	the iocb to read
 * @iter:	data destination
 * @written:	already copied
 *
 * This is a generic file read routine, and uses the
 * mapping->a_ops->readpage() function for the actual low-level stuff.
 *
 * 本段代码自己注释说逻辑实现的真的“丑陋”....
 * This is really ugly. But the goto's actually try to clarify some
 * of the logic when it comes to error handling etc.
 *
 * Return:
 * * total number of bytes copied, including those the were already @written
 * * negative error code if nothing was copied
 */

ssize_t generic_file_buffered_read(struct kiocb *iocb,
		struct iov_iter *iter, ssize_t written)
{
	struct file *filp = iocb->ki_filp;
	struct address_space *mapping = filp->f_mapping;
	struct inode *inode = mapping->host;
    //预读的数据结构,预读后面会有专门的文章分析
	struct file_ra_state *ra = &filp->f_ra;

    //文件读取位置,即在文件内偏移量,字节为单位
	loff_t *ppos = &iocb->ki_pos;
	pgoff_t index;
	pgoff_t last_index;
	pgoff_t prev_index;
	unsigned long offset;      /* offset into pagecache page */
	unsigned int prev_offset;
	int error = 0;

	if (unlikely(*ppos >= inode->i_sb->s_maxbytes))
		return 0;
	iov_iter_truncate(iter, inode->i_sb->s_maxbytes);

    //把文档按4K分成数据页,index代表是数据页索引
    //|----index 0----|----index 1----|----index 2----|
    //       4K             4K              4K
	index = *ppos >> PAGE_SHIFT;

    //上次预读的位置
	prev_index = ra->prev_pos >> PAGE_SHIFT;
	prev_offset = ra->prev_pos & (PAGE_SIZE-1);

    //iter->count 要读取得字节数,last_index是要读取得最后一个index
	last_index = (*ppos + iter->count + PAGE_SIZE-1) >> PAGE_SHIFT;
	offset = *ppos & ~PAGE_MASK;

    //循环读取iter->count bytes数据(即应用程序要求读取的数据)
	for (;;) {
		struct page *page;
		pgoff_t end_index;
		loff_t isize;
		unsigned long nr, ret;

		cond_resched();
find_page:
		if (fatal_signal_pending(current)) {
			error = -EINTR;
			goto out;
		}
        //mapping指向的address_space缓存中,根据index查找是否已经有缓存数据
		page = find_get_page(mapping, index);
		if (!page) {
            //如果执行了不进行时机的磁盘io操作(必须在cache中)返回错误
			if (iocb->ki_flags & IOCB_NOIO)
				goto would_block;
            //"同步预读",注意,这里同步并非真的同步等待,本质上也是向block layer
            //提交一个io请求就返回了
			page_cache_sync_readahead(mapping,
					ra, filp,
					index, last_index - index);
			page = find_get_page(mapping, index);

            //如果没有成功(因为sync_readahead给page申请内存时,不会进入慢路径,所以
            //有可能失败。失败后goto no_cache_page:分配page页面,并readpage进行磁盘io操作
			if (unlikely(page == NULL))
				goto no_cached_page;
		}

        //走到这里,代表readahead成功,也就是当前想读取的页面,已经被预读了
        //理解这句话要看readahead机制和数据结构,PageReadahead page是怎么设置的
		if (PageReadahead(page)) {
			if (iocb->ki_flags & IOCB_NOIO) {
				put_page(page);
				goto out;
			}
            //触发一次异步read_ahead,因为异步读成功命中,重新取预读更多的页面,也会
            //更新ra数据结构
			page_cache_async_readahead(mapping,
					ra, filp, page,
					index, last_index - index);
		}

        //如果页面数据无效,比如系统I/O非常繁忙,上面异步读没有完成,这里就需要等待了
		if (!PageUptodate(page)) {
			/*
			 * See comment in do_read_cache_page on why
			 * wait_on_page_locked is used to avoid unnecessarily
			 * serialisations and why it's safe.
			 */
            //IOCB_WAITQ代表异步io,比如io_uring
			if (iocb->ki_flags & IOCB_WAITQ) {
				if (written) {
					put_page(page);
					goto out;
				}
				error = wait_on_page_locked_async(page,
								iocb->ki_waitq);
			} else {
                //指定不等待,就返回错误
				if (iocb->ki_flags & IOCB_NOWAIT) {
					put_page(page);
					goto would_block;
				}
                //block等待IO完成,systrace上显示进程block i/o就是等在此处。
				error = wait_on_page_locked_killable(page);
			}
			if (unlikely(error))
				goto readpage_error;

            //上面的等待成功了
			if (PageUptodate(page))
				goto page_ok;

			if (inode->i_blkbits == PAGE_SHIFT ||
					!mapping->a_ops->is_partially_uptodate)
				goto page_not_up_to_date;
			/* pipes can't handle partially uptodate pages */
			if (unlikely(iov_iter_is_pipe(iter)))
				goto page_not_up_to_date;
			if (!trylock_page(page))
				goto page_not_up_to_date;
			/* Did it get truncated before we got the lock? */
			if (!page->mapping)
				goto page_not_up_to_date_locked;
			if (!mapping->a_ops->is_partially_uptodate(page,
							offset, iter->count))
				goto page_not_up_to_date_locked;
			unlock_page(page);
		}
page_ok:
		/*
		 * i_size must be checked after we know the page is Uptodate.
		 *
		 * Checking i_size after the check allows us to calculate
		 * the correct value for "nr", which means the zero-filled
		 * part of the page is not copied back to userspace (unless
		 * another truncate extends the file - this is desired though).
		 */

		isize = i_size_read(inode);
		end_index = (isize - 1) >> PAGE_SHIFT;
		if (unlikely(!isize || index > end_index)) {
			put_page(page);
			goto out;
		}

		/* nr is the maximum number of bytes to copy from this page */
		nr = PAGE_SIZE;
		if (index == end_index) {
			nr = ((isize - 1) & ~PAGE_MASK) + 1;
			if (nr <= offset) {
				put_page(page);
				goto out;
			}
		}
		nr = nr - offset;

		/* If users can be writing to this page using arbitrary
		 * virtual addresses, take care about potential aliasing
		 * before reading the page on the kernel side.
		 */
		if (mapping_writably_mapped(mapping))
			flush_dcache_page(page);

		/*
		 * When a sequential read accesses a page several times,
		 * only mark it as accessed the first time.
		 */
		if (prev_index != index || offset != prev_offset)
			mark_page_accessed(page);
		prev_index = index;

		/*
		 * Ok, we have the page, and it's up-to-date, so
		 * now we can copy it to user space...
		 */

        //数据copy给用户空间,iter->count会减去copy的数据大小
		ret = copy_page_to_iter(page, offset, nr, iter);
		offset += ret;
		index += offset >> PAGE_SHIFT;
		offset &= ~PAGE_MASK;
		prev_offset = offset;

		put_page(page);
		written += ret;
        //如果iter->count = 0,即已经完成数据读取goto out跳出循环。
		if (!iov_iter_count(iter))
			goto out;
		if (ret < nr) {
			error = -EFAULT;
			goto out;
		}
		continue;

page_not_up_to_date:
		/* Get exclusive access to the page ... */
		if (iocb->ki_flags & IOCB_WAITQ)
			error = lock_page_async(page, iocb->ki_waitq);
		else
			error = lock_page_killable(page);
		if (unlikely(error))
			goto readpage_error;

page_not_up_to_date_locked:
		/* Did it get truncated before we got the lock? */
		if (!page->mapping) {
			unlock_page(page);
			put_page(page);
			continue;
		}

		/* Did somebody else fill it already? */
		if (PageUptodate(page)) {
			unlock_page(page);
			goto page_ok;
		}

readpage:
        //low-level page read,即调用readpage触发磁盘i/o
		if (iocb->ki_flags & (IOCB_NOIO | IOCB_NOWAIT)) {
			unlock_page(page);
			put_page(page);
			goto would_block;
		}
		/*
		 * A previous I/O error may have been due to temporary
		 * failures, eg. multipath errors.
		 * PG_error will be set again if readpage fails.
		 */
		ClearPageError(page);
		/* Start the actual read. The read will unlock the page. */
		error = mapping->a_ops->readpage(filp, page);

		if (unlikely(error)) {
			if (error == AOP_TRUNCATED_PAGE) {
				put_page(page);
				error = 0;
				goto find_page;
			}
			goto readpage_error;
		}

		if (!PageUptodate(page)) {
			if (iocb->ki_flags & IOCB_WAITQ)
				error = lock_page_async(page, iocb->ki_waitq);
			else
				error = lock_page_killable(page);

			if (unlikely(error))
				goto readpage_error;
			if (!PageUptodate(page)) {
				if (page->mapping == NULL) {
					/*
					 * invalidate_mapping_pages got it
					 */
					unlock_page(page);
					put_page(page);
					goto find_page;
				}
				unlock_page(page);
				shrink_readahead_size_eio(ra);
				error = -EIO;
				goto readpage_error;
			}
			unlock_page(page);
		}

		goto page_ok;

readpage_error:
		/* UHHUH! A synchronous read error occurred. Report it */
		put_page(page);
		goto out;

no_cached_page:
        //address space没有缓存页面,alloc page,然后插入cache中。
		/*
		 * Ok, it wasn't cached, so we need to create a new
		 * page..
		 */
		page = page_cache_alloc(mapping);
		if (!page) {
			error = -ENOMEM;
			goto out;
		}
		error = add_to_page_cache_lru(page, mapping, index,
				mapping_gfp_constraint(mapping, GFP_KERNEL));
		if (error) {
			put_page(page);
			if (error == -EEXIST) {
				error = 0;
				goto find_page;
			}
			goto out;
		}
		goto readpage;
	}

would_block:
	error = -EAGAIN;
out:
	ra->prev_pos = prev_index;
	ra->prev_pos <<= PAGE_SHIFT;
	ra->prev_pos |= prev_offset;

	*ppos = ((loff_t)index << PAGE_SHIFT) + offset;
	file_accessed(filp);
	return written ? written : error;
}

重点提示:代码中为什么先触发一次sync_readahead,再触发一次async_readahead?

  • 如果代码注释的一样,这两个函数命令很容易误解,其实这两个从应用调用的角度来看,都是”异步"的,以为他们都只是向block layer submit io,所以是异步,从代码上下文也能推测出来,比如sync_readahead调用完之后,紧接调用了find_get_page还是判断了page == NULL的情况,从这点就充分看出来sync_readahead也是异步调用。
  • 如果系统io情况正常,100%顺序读取的情况下,sync_readahead调用一次,触发了预读,后面再顺序读就不会触发sync_readhead了,而是触发async_readahead,async_readahead触发的条件是要读取的page满足PageReadahead(page),即预读页面被命中了。就像网络协议栈中的滑动窗口,既然请求已经来到预读的窗口中,那么就要重新触发新的预读,预读更多的页面。

具体上面这段话不理解的话,参考我后面readahead算法介绍的文章。

wait_on_page_locked_killable返回时机

IO完成之前会一直等待IO完成,在IO完成后,块设备通过中断通知cpu。在中断处理函数中,会进一步触发BLOCK_SOFTIRQ。在软中断处理例程中,回调最终会触发bio->bi_end_io(对ext4来说是mpage_end_io,解锁(unlock_page)之前在锁定的页面,同时调用SetPageUptodate设置update状态,调用栈如下:

#0  SetPageUptodate (page=<optimized out>) at ./include/linux/page-flags.h:542
#1  __read_end_io (bio=0xffff88800607be40) at fs/ext4/readpage.c:85
#2  0xffffffff8150ed00 in mpage_end_io (bio=0xffff88800607be40) at fs/ext4/readpage.c:183
#3  0xffffffff8168859f in bio_endio (bio=0xffff88800607be40) at block/bio.c:1449
#4  0xffffffff8168f9d7 in req_bio_endio (error=<optimized out>, nbytes=<optimized out>, bio=<optimized out>, rq=<optimized out>) at block/blk-core.c:259
#5  blk_update_request (req=0xffff8880062fe040, error=0 '\000', nr_bytes=131072) at block/blk-core.c:1577
#6  0xffffffff816a2d6a in blk_mq_end_request (rq=0xffff8880062fe040, error=<optimized out>) at ./include/linux/blkdev.h:976
#7  0xffffffff81b780c9 in virtblk_request_done (req=0xffff8880062fe040) at drivers/block/virtio_blk.c:171
#8  0xffffffff8169e6fb in blk_done_softirq (h=<optimized out>) at block/blk-mq.c:586
#9  0xffffffff826000d1 in __do_softirq () at kernel/softirq.c:298
#10 0xffffffff82400f82 in asm_call_on_stack () at arch/x86/entry/entry_64.S:708
#11 0xffffffff810ea498 in __run_on_irqstack (func=<optimized out>) at ./arch/x86/include/asm/irq_stack.h:26

 wait_on_page_locked_killable函数代码

static inline int wait_on_page_locked_killable(struct page *page)                                                                                                        
{
    if (!PageLocked(page))
        return 0;
    return wait_on_page_bit_killable(compound_head(page), PG_locked);
}

/* Convenience macros for the sake of set_current_state: */
#define TASK_KILLABLE			(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)


int wait_on_page_bit_killable(struct page *page, int bit_nr)                                                                                                             
{
    wait_queue_head_t *q = page_waitqueue(page);
    return wait_on_page_bit_common(q, page, bit_nr, TASK_KILLABLE, false);
}

static inline int wait_on_page_bit_common(wait_queue_head_t *q,
        struct page *page, int bit_nr, int state, bool lock)
{
    struct wait_page_queue wait_page;
    wait_queue_entry_t *wait = &wait_page.wait;
    int ret = 0;

    init_wait(wait);
    wait->flags = lock ? WQ_FLAG_EXCLUSIVE : 0;
    wait->func = wake_page_function;
    wait_page.page = page;
    wait_page.bit_nr = bit_nr;

    for (;;) {
        spin_lock_irq(&q->lock);

        if (likely(list_empty(&wait->entry))) {
            __add_wait_queue_entry_tail(q, wait);
            SetPageWaiters(page);
        }

        set_current_state(state);

        spin_unlock_irq(&q->lock);

        if (likely(test_bit(bit_nr, &page->flags))) {
            io_schedule();
        }

        if (lock) {
            if (!test_and_set_bit_lock(bit_nr, &page->flags))
                break;
        } else {
            if (!test_bit(bit_nr, &page->flags))
                break;
        }

        if (unlikely(signal_pending_state(state, current))) {
            ret = -EINTR;
            break;
        }
    }

    finish_wait(q, wait);

    /*
     * A signal could leave PageWaiters set. Clearing it here if
     * !waitqueue_active would be possible (by open-coding finish_wait),
     * but still fail to catch it in the case of wait hash collision. We
     * already can fail to clear wait hash collision cases, so don't
     * bother with signals either.
     */

    return ret;
}

IO没有完成前,PG_Locked一直置位,调用进入io_schedule,进程主动让出CPU,状态切入TASK_KILLABLE(同时也是一个TASK_UNINTERRUPTIBLE)。io_schedule会设置current->io_wait = 1,io等待期间,cpu idle期间检测到io_wait就会累计iowait的时间。

IO完成解锁页面,唤醒进程

static void __read_end_io(struct bio *bio)
{
    struct page *page;
    struct bio_vec *bv;
    struct bvec_iter_all iter_all;

    bio_for_each_segment_all(bv, bio, iter_all) {
        page = bv->bv_page;

        /* PG_error was set if any post_read step failed */
        if (bio->bi_status || PageError(page)) {
            ClearPageUptodate(page);
            /* will re-read again later */
            ClearPageError(page);
        } else {
            //设置Uptodate状态
            SetPageUptodate(page);
        }
        //解锁页面,会wakeup等待的线程
        unlock_page(page);                                                                                                                                               
    }
    if (bio->bi_private)
        mempool_free(bio->bi_private, bio_post_read_ctx_pool);
    bio_put(bio);
}

/**
 * unlock_page - unlock a locked page
 * @page: the page
 *
 * Unlocks the page and wakes up sleepers in ___wait_on_page_locked().
 * Also wakes sleepers in wait_on_page_writeback() because the wakeup
 * mechanism between PageLocked pages and PageWriteback pages is shared.
 * But that's OK - sleepers in wait_on_page_writeback() just go back to sleep.
 *
 * Note that this depends on PG_waiters being the sign bit in the byte
 * that contains PG_locked - thus the BUILD_BUG_ON(). That allows us to
 * clear the PG_locked bit and test PG_waiters at the same time fairly
 * portably (architectures that do LL/SC can test any bit, while x86 can
 * test the sign bit).
 */
void unlock_page(struct page *page)
{
	BUILD_BUG_ON(PG_waiters != 7);
	page = compound_head(page);
	VM_BUG_ON_PAGE(!PageLocked(page), page);
	if (clear_bit_unlock_is_negative_byte(PG_locked, &page->flags))
		wake_up_page_bit(page, PG_locked);
}
EXPORT_SYMBOL(unlock_page);

mpage_end_io 回调是哪里赋值的 - ext4读文件为例

fs/ext4/readpage.c : ext4_mpage_readpages

int ext4_mpage_readpages(struct address_space *mapping,
			 struct list_head *pages, struct page *page,
			 unsigned nr_pages)
{
    ...
	for (; nr_pages; nr_pages--) {
        ...
		bio_set_dev(bio, bdev);
		bio->bi_iter.bi_sector = blocks[0] << (blkbits - 9);
		bio->bi_end_io = mpage_end_io;
		bio->bi_private = ctx;
		ext4_set_bio_ctx(inode, bio);
		bio_set_op_attrs(bio, REQ_OP_READ, 0);
        ...
	}
	BUG_ON(pages && !list_empty(pages));
	if (bio)
		ext4_submit_bio_read(bio);
	return 0;
}

参考文章:

Linux 通用块层 bio 详解 – 字节岛技术分享

Life of an ext4 write request - Ext4

深入分析Linux内核File cache机制(上篇) - 知乎

Linux内核中跟踪文件PageCache预读_读取

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux read的核心函数generic_file_buffered_read 的相关文章

  • 如何解压缩字节数组中的 gzip 数据?

    我有一个类 它有一个接收对象作为参数的方法 该方法通过 RMI 调用 public RMIClass extends Serializable public RMIMethod MyFile file do stuff MyFile 有一个
  • Java中如何查找未关闭的I/O资源?

    Java 中的许多 I O 资源 例如 InputStream 和 OutputStream 在使用完毕后需要关闭 如前所述here http www coderanch com t 202922 Performance java Uncl
  • 一次性*level*触发的epoll():EPOLLONESHOT是否意味着EPOLLET?

    是否可以使用epoll一击level 触发模式 我在搜索时没有找到任何相关信息 看来每个人都使用边缘触发模式 当 的时候EPOLLONESHOT标记被选中并且您已经为套接字提取了一个事件 那么该套接字不会像许多人想象的那样从 epoll 中
  • 如何从标准输入读取输入并强制编码?

    目标是不断地阅读stdin并强制执行utf8在Python2和Python3中 我尝试过以下解决方案 以与 python2 和 python3 兼容的方式将字节写入标准输出 https stackoverflow com questions
  • 如何在Python中打印命令?

    我不从事编程领域 但最近对 Python 感兴趣 我正在编写一些函数 但为了调试 我需要查看正在运行哪些命令 例如 def foo for i in xrange 0 5 a 1 i 是否可以让解释器输出 gt gt gt for i in
  • 接受用户输入

    我目前正在 Malbolge 中编写一款文字冒险类型游戏 谁能告诉我如何在 Malbolge 中接受用户输入 我能够将文本输出到屏幕上 但是 我似乎不知道如何接受输入 GHJUYGHJKLKUJHM MJ 6AG9F5D8V A8 gt 7
  • C 中逐个字符读取文件

    我正在用 C 语言编写 BF 解释器 但在读取文件时遇到了问题 我以前用过scanf为了读取第一个字符串 但是你的 BF 代码中不能有空格或注释 现在这就是我所拥有的 char readFile char fileName FILE fil
  • 使用 x64 汇编代码的基本输入

    我正在编写有关汇编中基本输入和输出的教程 我使用的是 64 位 Linux 发行版 Ubuntu 在教程的第一部分中 我讨论了基本输出并创建了一个简单的程序 如下所示 global start section text start mov
  • 强制“git status”在终端上输出颜色(在脚本内)

    EDIT 我想提出一个建议 解析颜色通常是一个考虑不周的想法 我想要它的部分原因是我可以解析它并在我自己的脚本输出中传递它 这是 好吧 但使用瓷器或类似的东西并自己重新构建彩色部件可能会更明智 原始问题如下 我喜欢看到颜色 因为我的脚本足够
  • PipedInputStream - 如何避免“java.io.IOException:管道损坏”

    我有两个线程 其中一个写入 PipedOutputStream 另一个从相应的 PipedInputStream 读取 背景是一个线程正在从远程服务器下载一些数据 并通过管道流将其复用到多个其他线程 问题是有时 尤其是下载大文件时 gt 5
  • 如何判断一个文件是否可以删除?

    我怎么能够check我可以用Java删除文件吗 例如 如果一个文件test txt在另一个程序中打开我无法删除它 我必须在实际删除之前知道它 所以我不能这样做 if file delete And srcFile canWrite 也不工作
  • Java:BufferedReader 的 readLine 方法的效率和可能的替代方案

    我们正在努力减少延迟并提高用 Java 编写的进程的性能 该进程通过 readLine 方法从套接字消费数据 xml 字符串 缓冲读取器 http java sun com javase 6 docs api java io Buffere
  • 将数据写入文件:fflush()需要大量时间

    我有一个要求 其中我必须缓冲大量数据 以 GB 为单位 以供将来使用 由于没有足够的 RAM 来缓冲如此大量的数据 我决定将数据存储在文件中 现在的陷阱是 当我将数据写入文件时 其他线程可能需要 缓冲 数据 因此每次向文件流写入内容时 我都
  • 如何使用 Spring 注入键值属性文件?

    我有一个键值属性文件 其中包含错误代码及其错误消息 我想在应用程序启动时注入此文件 以便我可以在注入的属性上进行查找 而无需读取该文件 下面只是伪代码 里面有什么吗Spring可以创建这个设置吗 Value location classpa
  • 如何在 C# 中读取文本文件并将数据添加到 int 数组中?

    我正在尝试读取一个文本文件 其中包含以逗号分隔的数字 当我阅读时使用File Readline 我把它拿到string 我需要将其转换为 int 数组 但它给出了错误 文本文件的内容 146429 143689 144380 141523
  • osx 上的 aio:它是在内核中实现还是通过用户线程实现?其他选择?

    我正在开发我的小型 C 框架 并且有一个文件类 它也应该支持异步读写 除了在我发现的一些工作线程中使用同步文件 I O 之外 唯一的解决方案是 aio 无论如何 我环顾四周并在某处读到 在 Linux 中 aio 甚至不是在内核中实现的 而
  • 关闭/清理“混合”文件描述符/套接字

    当我使用accept 创建一个套接字并使用fdopen 从中创建一个文件时 我需要做什么来清理所有内容 我是否需要对 FILE 执行 fclose 对套接字执行 shutdown 和 close 还是只需要 shutdown 和 或 clo
  • 如何检查并关闭Excel文件是否已在Java中打开[重复]

    这个问题在这里已经有答案了 可能的重复 Java 检查文件是否已打开 https stackoverflow com questions 1390592 java check if file is already open 我正在制作一个
  • 在Python中解析制表符分隔的文件

    我正在尝试在 Python 中解析一个制表符分隔的文件 其中与行开头分开的 k 个制表符的数字应该放入第 k 个数组中 除了逐行读取并执行简单解决方案将执行的所有明显处理之外 是否有内置函数可以执行此操作 或者有更好的方法 您可以使用the
  • 在java中将StreamWriter转换为OutputStream?

    我正在尝试使用 System setOut 将 System out 重定向到字符串 它需要一个 PrintStream 有什么方法可以将 StringWriter 转换为 Stream 以便我可以将其传递给 setOut 吗 你不能完全这

随机推荐

  • 【Java设计模式】这么详细,你还不懂建造者模式嘛!

    我是清风 每天学习一点点 快乐成长多一点 这些都是我的日常笔记以及总结 目录 建造者 建造者模式和工厂模式区别 业务场景 UML类图 源码解析 StringBuilder 源码分析 SringBuffer 开源框架 spring中BeanD
  • ZeroTierr的moon云服务器搭建和使用

    搭建moon 本质上是在云服务器上建立一个moon服务器 也加入zerotier的Network ID 服务器记录请求路径来做类似于DNS的解析 让设备之间p2p直连 问题是ZeroTier One本身的服务器都在国外访问速度很慢 可以通过
  • 【AI实战】BERT 文本分类模型自动化部署之 dockerfile

    AI实战 BERT 文本分类模型自动化部署之 dockerfile BERT BERT 文本分类模型 基于中文预训练bert的文本分类模型 针对多分类模型的loss函数 样本不均衡时 多标签分类时 dockerfile 编写 dockerf
  • 淘宝logo设计遇到的坑

    看了抖音上一些大神设计的logo 被惊艳到了 新开了一个公众号 所以也想设计一个有创意的logo 想到就去做 开始吧 我就忘记了 付款前 应该先搜一下TB logo设计的坑 先说我的教训吧 教训 终于明白 这些店是怎么赚钱的了 只要你付款了
  • 切换摄像头操作(前置、后置)

    一 默认进入页面后直接加载并调用摄像头 调用摄像头的方法 function takePhoto if navigator mediaDevices getUserMedia navigator getUserMedia navigator
  • [转]unity作品打包

    unity作品打包 每当项目完成后最重要的就是项目打包 首先打包之前要看项目内部是否有错误 以保证项目的正确 1 在unity上部找到File 文件 选项卡 并点击进入 2 点击Flie选项卡中的Build Settings选项 3 完成上
  • 为什么文学也可以解决许多问题

    文学是一种艺术形式 通过语言和叙事手法创造出各种文学作品 如小说 诗歌 戏剧等 它具有独特的力量和功能 能够解决许多问题的原因如下 情感共鸣与同理心 文学作品可以引起人们的情感共鸣和同理心 通过创造生动的角色 复杂的情节和丰富的描绘 文学作
  • 浅谈VVC(H.266)的变换模块

    转自 https zhuanlan zhihu com p 108792210 本文将分为四个部分对下一代视频编码标准Versatile Video Coding VVC 的变化模块进行介绍 第一部分简单介绍一下视频编码的发展历程以及VVC
  • surface pro 4专业版没有64位虚拟机选项的解决办法

    前言 因为surface没办法开bios的虚拟化支持 所以博主也是打电话亲自询问了微软的客服然后得出的结论 这个因为可能微软对自己产品的封锁吧 你装vmware也好 Virualbox也好 都是只有32位系统的 然后呢 也是一样的 选择代数
  • 【QT】——信号和槽

    1 信号和槽的概念 信号和槽是 Qt 特有的信息传输机制 是 Qt 设计程序的重要基础 它可以让互不干扰的 对象建立一种联系 当信号发出时 被连接的槽函数会自动被回调 这就类似观察者模式 当发生了感兴趣的事件 某一个操作就会被自动触发 1
  • 《C++ Primer》学习笔记(十四):重载运算与类型转换

    C Primer 学习笔记 十四 重载运算与类型转换 输入和输出运算符 算术和关系运算符 赋值运算符 下标运算符 递增和递减运算符 成员访问运算符 函数调用运算符 标准库定义的函数对象 可调用对象与function 当一个重载的运算是成员函
  • 做视频剪辑必须学会的几个剪辑软件,你知道哪些?

    现在短视频非常火热 身边70 以上的人或多或少都会使用手机APP快速剪辑视频 但是如果大家想要通过视频剪辑变现 或者想要自己的视频出彩 那么掌握系统的剪辑方法 剪辑软件的使用是必不可少的 今天小编就给大家分享几款我在剪辑视频中会常常用到的软
  • android 验证码短信验证码,Android​短信验证码倒计时验证的2种常用方式

    前言 本文主要介绍的是短信验证码功能 这里总结了两种常用的方式 可以直接拿来使用 看图 计时器 说明 这里的及时从10开始 是为了演示的时间不要等太长而修改的 方法如下 1 第一种方式 Timer Description 自定义Timer
  • thinkcmf5 pc切换手机

    1 在simplewind cmf common php 里找到 获取当前主题名 添加 if cmf is mobile theme config cmf mobile default theme else theme config cmf
  • java 返回function_Java8通过Function获取字段名

    摘要 Java8通过Function获取字段名 不用再硬编码 效果类似于mybatis plus的LambdaQueryWrapper 本文总共三个步骤 1 使Function获取序列化能力 2 通过SFunction获取字段名 3 建一些
  • 【Spring配置文件】Spring定时器的使用及配置

    如何在spring中配置定时任务 spring的定时任务配置分为三个步骤 1 定义任务 2 任务执行策略配置 3 启动任务 1 定义任务
  • OpenCV部署CRNN文字识别

    一 参考链接 1 模型获取及训练 2 github解答 二 模型转化 pytorch 转 ONNX import torch import models crnn as crnn model crnn CRNN 32 1 37 256 mo
  • dotfuscator使用方法

    转载自 http hi baidu com free3people item 0fba87d34091df15d80e4400 dotfuscator如何对 net程序进行混淆保护对于程序代码的保护 网上有很多资料 有的说混淆 有的说加密
  • jsp中使用response.sendRedirect重定向页面传递中文参数

    1 要跳转的jsp页面的书写 2 跳转的jsp页面对参数的获取 使用jsp内置对象
  • Linux read的核心函数generic_file_buffered_read

    内核 5 9 0 流程图 generic file buffered read一种调用路径 cat某个文件触发 0 ondemand readahead mapping 0xffff888005c61340 ra 0xffff8880059