copy_to_user与mmap的工作原理

本文探讨了mmap和copy_to_user两种数据传输方式的性能差异。mmap通过一次性建立页表映射,避免了频繁的地址合法性和数据拷贝检查,减少了CACHE失效的情况,从而提升了效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  copy_to_user在每次拷贝时需要检测指针的合法性,也就是用户空间的指针所指向的地址的确是一段该进程本身的地址,而不是指向了不属于它的地方,而且每次都会拷贝一次数据,频繁访问内存,由于虚拟地址连续,物理地址不一定会连续,从而造成CPU的CACHE频繁失效,从而使速度降低   
  mmap仅在第一次使用时为进程建立页表,也就是将一段物理地址映射到一段虚拟地址上,以后操作时不再检测其地址的合法性(合法性交由CPU页保护异常来做),另一方面是内核下直接操作mmap地址,可以不用频繁拷贝,也就是说在内核下直接可用指针向该地址操作,而不再在内核中专门开一个缓冲区,然后将缓冲区中的数据拷贝一次进来,mmap一般是将一段连续的物理地址映射成一段虚拟地址,当然,也可以将每段连续,但各段不连续的物理地址映射成一段连续的虚拟地址,无论如何,其物理地址在每段之中是连续的,这样一来,就不会造成CPU的CACHE频繁失效,从而大大节约时间。

if (udata) { struct virtio_rdma_create_qp_uresp uresp = {}; struct virtio_rdma_ucontext *uctx = rdma_udata_to_drv_context(udata, struct virtio_rdma_ucontext, ibucontext); uint32_t per_size; per_size = sizeof(struct virtio_rdma_cmd_post_send) + sizeof(struct virtio_rdma_sge) * attr->cap.max_send_sge; vqp->usq_buf_size = PAGE_ALIGN(per_size * attr->cap.max_send_wr); vqp->usq_buf = virtio_rdma_init_mmap_entry(vdev, vqp->sq->vq, &vqp->sq_entry, vqp->usq_buf_size, uctx, &uresp.sq_size, &uresp.svq_used_off, &uresp.svq_size, &vqp->usq_dma_addr); if (!vqp->usq_buf) goto out_err; per_size = sizeof(struct virtio_rdma_cmd_post_recv) + sizeof(struct virtio_rdma_sge) * attr->cap.max_recv_sge; vqp->urq_buf_size = PAGE_ALIGN(per_size * attr->cap.max_recv_wr); vqp->urq_buf = virtio_rdma_init_mmap_entry(vdev, vqp->rq->vq, &vqp->rq_entry, vqp->urq_buf_size, uctx, &uresp.rq_size, &uresp.rvq_used_off, &uresp.rvq_size, &vqp->urq_dma_addr); if (!vqp->urq_buf) { // TODO: pop sq entry dma_free_coherent(vdev->vdev->dev.parent, vqp->usq_buf_size, vqp->usq_buf, vqp->usq_dma_addr); goto out_err; } uresp.sq_offset = rdma_user_mmap_get_offset(&vqp->sq_entry->rdma_entry); uresp.sq_phys_addr = vqp->usq_dma_addr; uresp.num_sqe = attr->cap.max_send_wr; uresp.num_svqe = virtqueue_get_vring_size(vqp->sq->vq); uresp.sq_idx = vqp->sq->vq->index; uresp.rq_offset = rdma_user_mmap_get_offset(&vqp->rq_entry->rdma_entry); uresp.rq_phys_addr = vqp->urq_dma_addr; uresp.num_rqe = attr->cap.max_recv_wr; uresp.num_rvqe = virtqueue_get_vring_size(vqp->rq->vq); uresp.rq_idx = vqp->rq->vq->index; uresp.notifier_size = vdev->fast_doorbell ? PAGE_SIZE : 0; uresp.qpn = vqp->qp_handle; if (udata->outlen < sizeof(uresp)) { rc = -EINVAL; goto out_err_u; } rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp)); if (rc) goto out_err_u; } pr_info("%s: qpn 0x%x wq %d rq %d\n", __func__, rsp->qpn, vqp->sq->vq->index, vqp->rq->vq->index); // ret = &vqp->ibqp; ret = 0; goto out; out_err_u: dma_free_coherent(vdev->vdev->dev.parent, vqp->usq_buf_size, vqp->usq_buf, vqp->usq_dma_addr); dma_free_coherent(vdev->vdev->dev.parent, vqp->urq_buf_size, vqp->urq_buf, vqp->urq_dma_addr); rdma_user_mmap_entry_remove(&vqp->sq_entry->rdma_entry); rdma_user_mmap_entry_remove(&vqp->rq_entry->rdma_entry); out_err: kfree(vqp); out: kfree(rsp); kfree(cmd); return ret; }
03-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

do2jiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值