sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/networking/msg_zerocopymodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/zh_TW/networking/msg_zerocopymodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/it_IT/networking/msg_zerocopymodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/ja_JP/networking/msg_zerocopymodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/ko_KR/networking/msg_zerocopymodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget+/translations/sp_SP/networking/msg_zerocopymodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhX/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/networking/msg_zerocopy.rsthKubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh field_body)}(h*Documentation/networking/msg_zerocopy.rst h]h)}(h)Documentation/networking/msg_zerocopy.rsth]h)Documentation/networking/msg_zerocopy.rst}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h翻译h]h翻译}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(h-王亚鑫 Wang Yaxin h]h)}(h,王亚鑫 Wang Yaxin h](h王亚鑫 Wang Yaxin <}(hj hhhNhNubh reference)}(hwang.yaxin@zte.com.cnh]hwang.yaxin@zte.com.cn}(hj*hhhNhNubah}(h]h ]h"]h$]h&]refurimailto:wang.yaxin@zte.com.cnuh1j(hj ubh>}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjubah}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h校译h]h校译}(hjShhhNhNubah}(h]h ]h"]h$]h&]uh1hhjPhhhKubh)}(hS- 徐鑫 xu xin - 何配林 He Peilin h]h bullet_list)}(hhh](h list_item)}(h#徐鑫 xu xin h]h)}(hjnh](h徐鑫 xu xin <}(hjphhhNhNubj))}(hxu.xin16@zte.com.cnh]hxu.xin16@zte.com.cn}(hjwhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:xu.xin16@zte.com.cnuh1j(hjpubh>}(hjphhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hjlubah}(h]h ]h"]h$]h&]uh1jjhjgubjk)}(h+何配林 He Peilin h]h)}(h*何配林 He Peilin h](h何配林 He Peilin <}(hjhhhNhNubj))}(hhe.peilin@zte.com.cnh]hhe.peilin@zte.com.cn}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurimailto:he.peilin@zte.com.cnuh1j(hjubh>}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jjhjgubeh}(h]h ]h"]h$]h&]bullet-uh1jehhhK hjaubah}(h]h ]h"]h$]h&]uh1hhjPubeh}(h]h ]h"]h$]h&]uh1hhhhK hhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(h MSG_ZEROCOPYh]h MSG_ZEROCOPY}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h简介h]h简介}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(hMSG_ZEROCOPY 标志用于启用套接字发送调用的免拷贝功能。该功能目前适用于 TCP、UDP 和 VSOCK (使用 virtio 传输)套接字。h]hMSG_ZEROCOPY 标志用于启用套接字发送调用的免拷贝功能。该功能目前适用于 TCP、UDP 和 VSOCK (使用 virtio 传输)套接字。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hhh](j)}(h机遇与注意事项h]h机遇与注意事项}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(h在用户进程与内核之间拷贝大型缓冲区可能会消耗大量资源。Linux 支持多种免拷贝的接口,如sendfile 和 splice。MSG_ZEROCOPY 标志将底层的拷贝避免机制扩展到了常见的套接字发送调用中。h]h在用户进程与内核之间拷贝大型缓冲区可能会消耗大量资源。Linux 支持多种免拷贝的接口,如sendfile 和 splice。MSG_ZEROCOPY 标志将底层的拷贝避免机制扩展到了常见的套接字发送调用中。}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX免拷贝并非毫无代价。在实现上,它通过页面固定(page pinning)将按字节拷贝的成本替换为页面统计 (page accounting)和完成通知的开销。因此,MSG_ZEROCOPY 通常仅在写入量超过大约 10 KB 时 才有效。h]hX免拷贝并非毫无代价。在实现上,它通过页面固定(page pinning)将按字节拷贝的成本替换为页面统计 (page accounting)和完成通知的开销。因此,MSG_ZEROCOPY 通常仅在写入量超过大约 10 KB 时 才有效。}(hj0hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK hjhhubh)}(hXL页面固定还会改变系统调用的语义。它会暂时在进程和网络堆栈之间共享缓冲区。与拷贝不同,进程在系统 调用返回后不能立即覆盖缓冲区,否则可能会修改正在传输中的数据。内核的完整性不会受到影响,但有缺 陷的程序可能会破坏自己的数据流。h]hXL页面固定还会改变系统调用的语义。它会暂时在进程和网络堆栈之间共享缓冲区。与拷贝不同,进程在系统 调用返回后不能立即覆盖缓冲区,否则可能会修改正在传输中的数据。内核的完整性不会受到影响,但有缺 陷的程序可能会破坏自己的数据流。}(hj>hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK$hjhhubh)}(h当内核返回数据可以安全修改的通知时,进程才可以修改数据。因此,将现有应用程序转换为使用 MSG_ZEROCOPY 并非总是像简单地传递该标志那样容易。h]h当内核返回数据可以安全修改的通知时,进程才可以修改数据。因此,将现有应用程序转换为使用 MSG_ZEROCOPY 并非总是像简单地传递该标志那样容易。}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK(hjhhubeh}(h]id2ah ]h"]机遇与注意事项ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h 更多信息h]h 更多信息}(hjehhhNhNubah}(h]h ]h"]h$]h&]uh1jhjbhhhhhK,ubh)}(h本文档的大部分内容是来自于 netdev 2.1 上发表的一篇长篇论文。如需更深入的信息,请参阅该论文和 演讲,或者浏览 LWN.net 上的精彩报道,也可以直接阅读源码。h]h本文档的大部分内容是来自于 netdev 2.1 上发表的一篇长篇论文。如需更深入的信息,请参阅该论文和 演讲,或者浏览 LWN.net 上的精彩报道,也可以直接阅读源码。}(hjshhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK.hjbhhubh block_quote)}(hX 论文、幻灯片、视频: https://netdevconf.org/2.1/session.html?debruijn LWN 文章: https://lwn.net/Articles/726917/ 补丁集: [PATCH net-next v4 0/9] socket sendmsg MSG_ZEROCOPY https://lore.kernel.org/netdev/20170803202945.70750-1-willemdebruijn.kernel@gmail.com h]hdefinition_list)}(hhh](hdefinition_list_item)}(hP论文、幻灯片、视频: https://netdevconf.org/2.1/session.html?debruijn h](hterm)}(h论文、幻灯片、视频:h]h论文、幻灯片、视频:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhK2hjubh definition)}(hhh]h)}(h0https://netdevconf.org/2.1/session.html?debruijnh]j))}(hjh]h0https://netdevconf.org/2.1/session.html?debruijn}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurijuh1j(hjubah}(h]h ]h"]h$]h&]uh1hhhhK2hjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhK2hjubj)}(h/LWN 文章: https://lwn.net/Articles/726917/ h](j)}(h LWN 文章:h]h LWN 文章:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhK5hjubj)}(hhh]h)}(h https://lwn.net/Articles/726917/h]j))}(hjh]h https://lwn.net/Articles/726917/}(hjhhhNhNubah}(h]h ]h"]h$]h&]refurijuh1j(hjubah}(h]h ]h"]h$]h&]uh1hhhhK5hjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhK5hjubj)}(h补丁集: [PATCH net-next v4 0/9] socket sendmsg MSG_ZEROCOPY https://lore.kernel.org/netdev/20170803202945.70750-1-willemdebruijn.kernel@gmail.com h](j)}(h 补丁集:h]h 补丁集:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhK9hjubj)}(hhh]h)}(h[PATCH net-next v4 0/9] socket sendmsg MSG_ZEROCOPY https://lore.kernel.org/netdev/20170803202945.70750-1-willemdebruijn.kernel@gmail.comh](h4[PATCH net-next v4 0/9] socket sendmsg MSG_ZEROCOPY }(hjhhhNhNubj))}(hUhttps://lore.kernel.org/netdev/20170803202945.70750-1-willemdebruijn.kernel@gmail.comh]hUhttps://lore.kernel.org/netdev/20170803202945.70750-1-willemdebruijn.kernel@gmail.com}(hj!hhhNhNubah}(h]h ]h"]h$]h&]refurij#uh1j(hjubeh}(h]h ]h"]h$]h&]uh1hhhhK8hjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhK9hjubeh}(h]h ]h"]h$]h&]uh1jhjubah}(h]h ]h"]h$]h&]uh1jhhhK1hjbhhubeh}(h]id3ah ]h"] 更多信息ah$]h&]uh1jhjhhhhhK,ubeh}(h]id1ah ]h"]简介ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h接口h]h接口}(hjahhhNhNubah}(h]h ]h"]h$]h&]uh1jhj^hhhhhKhj^hhubj)}(hhh](j)}(h套接字设置h]h套接字设置}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj}hhhhhKAubh)}(hX(当应用程序向 send 系统调用传递未定义的标志时,内核通常会宽容对待。默认情况下,它会简单地忽略 这些标志。为了避免为那些偶然传递此标志的遗留进程启用免拷贝模式,进程必须首先通过设置套接字选项 来表明意图:h]hX(当应用程序向 send 系统调用传递未定义的标志时,内核通常会宽容对待。默认情况下,它会简单地忽略 这些标志。为了避免为那些偶然传递此标志的遗留进程启用免拷贝模式,进程必须首先通过设置套接字选项 来表明意图:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKChj}hhubh literal_block)}(hoif (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one))) error(1, errno, "setsockopt zerocopy");h]hoif (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one))) error(1, errno, "setsockopt zerocopy");}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKIhj}hhubeh}(h]id5ah ]h"]套接字设置ah$]h&]uh1jhj^hhhhhKAubj)}(hhh](j)}(h传输h]h传输}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKMubh)}(hl对 send(或 sendto、sendmsg、sendmmsg)本身的改动非常简单。只需传递新的标志即可。h]hl对 send(或 sendto、sendmsg、sendmmsg)本身的改动非常简单。只需传递新的标志即可。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKOhjhhubj)}(h/ret = send(fd, buf, sizeof(buf), MSG_ZEROCOPY);h]h/ret = send(fd, buf, sizeof(buf), MSG_ZEROCOPY);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKShjhhubh)}(h如果零拷贝操作失败,将返回 -1,并设置 errno 为 ENOBUFS。这种情况可能发生在套接字超出其 optmem 限制,或者用户超出其锁定页面的 ulimit 时。h]h如果零拷贝操作失败,将返回 -1,并设置 errno 为 ENOBUFS。这种情况可能发生在套接字超出其 optmem 限制,或者用户超出其锁定页面的 ulimit 时。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKUhjhhubj)}(hhh](j)}(h混合使用免拷贝和拷贝h]h混合使用免拷贝和拷贝}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKYubh)}(h许多工作负载同时包含大型和小型缓冲区。由于对于小数据包来说,免拷贝的成本高于拷贝,因此该 功能是通过标志实现的。带有标志的调用和没有标志的调用可以安全地混合使用。h]h许多工作负载同时包含大型和小型缓冲区。由于对于小数据包来说,免拷贝的成本高于拷贝,因此该 功能是通过标志实现的。带有标志的调用和没有标志的调用可以安全地混合使用。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK[hjhhubeh}(h]id7ah ]h"]混合使用免拷贝和拷贝ah$]h&]uh1jhjhhhhhKYubeh}(h]id6ah ]h"]传输ah$]h&]uh1jhj^hhhhhKMubj)}(hhh](j)}(h通知h]h通知}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhK_ubh)}(h当内核认为可以安全地重用之前传递的缓冲区时,它必须通知进程。完成通知在套接字的错误队列上 排队,类似于传输时间戳接口。h]h当内核认为可以安全地重用之前传递的缓冲区时,它必须通知进程。完成通知在套接字的错误队列上 排队,类似于传输时间戳接口。}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKahjhhubh)}(hX通知本身是一个简单的标量值。每个套接字都维护一个内部的无符号 32 位计数器。每次带有 MSG_ZEROCOPY 标志的 send 调用成功发送数据时,计数器都会增加。如果调用失败或长度为零, 则计数器不会增加。该计数器统计系统调用的调用次数,而不是字节数。在 UINT_MAX 次调用后, 计数器会循环。h]hX通知本身是一个简单的标量值。每个套接字都维护一个内部的无符号 32 位计数器。每次带有 MSG_ZEROCOPY 标志的 send 调用成功发送数据时,计数器都会增加。如果调用失败或长度为零, 则计数器不会增加。该计数器统计系统调用的调用次数,而不是字节数。在 UINT_MAX 次调用后, 计数器会循环。}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKdhjhhubj)}(hhh](j)}(h 通知接收h]h 通知接收}(hjNhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjKhhhhhKjubh)}(h下面的代码片段展示了 API 的使用。在最简单的情况下,每次 send 系统调用后,都会对错误队列 进行轮询和 recvmsg 调用。h]h下面的代码片段展示了 API 的使用。在最简单的情况下,每次 send 系统调用后,都会对错误队列 进行轮询和 recvmsg 调用。}(hj\hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKlhjKhhubh)}(h从错误队列读取始终是一个非阻塞操作。poll 调用用于阻塞,直到出现错误。它会在其输出标志中 设置 POLLERR。该标志不需要在 events 字段中设置。错误会无条件地发出信号。h]h从错误队列读取始终是一个非阻塞操作。poll 调用用于阻塞,直到出现错误。它会在其输出标志中 设置 POLLERR。该标志不需要在 events 字段中设置。错误会无条件地发出信号。}(hjjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKohjKhhubj)}(hpfd.fd = fd; pfd.events = 0; if (poll(&pfd, 1, -1) != 1 || pfd.revents & POLLERR == 0) error(1, errno, "poll"); ret = recvmsg(fd, &msg, MSG_ERRQUEUE); if (ret == -1) error(1, errno, "recvmsg");h]hpfd.fd = fd; pfd.events = 0; if (poll(&pfd, 1, -1) != 1 || pfd.revents & POLLERR == 0) error(1, errno, "poll"); ret = recvmsg(fd, &msg, MSG_ERRQUEUE); if (ret == -1) error(1, errno, "recvmsg");}hjxsbah}(h]h ]h"]h$]h&]hhuh1jhhhKthjKhhubh)}(hread_notification(msg);h]hread_notification(msg);}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK}hjKhhubh)}(h这个示例仅用于演示目的。在实际应用中,不等待通知,而是每隔几次 send 调用就进行一次非阻塞 读取会更高效。h]h这个示例仅用于演示目的。在实际应用中,不等待通知,而是每隔几次 send 调用就进行一次非阻塞 读取会更高效。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjKhhubh)}(h零拷贝通知可以与其他套接字操作乱序处理。通常,拥有错误队列套接字会阻塞其他操作,直到错误 被读取。然而,零拷贝通知具有零错误代码,因此不会阻塞 send 和 recv 调用。h]h零拷贝通知可以与其他套接字操作乱序处理。通常,拥有错误队列套接字会阻塞其他操作,直到错误 被读取。然而,零拷贝通知具有零错误代码,因此不会阻塞 send 和 recv 调用。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjKhhubeh}(h]id9ah ]h"] 通知接收ah$]h&]uh1jhjhhhhhKjubj)}(hhh](j)}(h通知批处理h]h通知批处理}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjhhhhhKubh)}(hX可以使用 recvmmsg 调用来一次性读取多个未决的数据包。这通常不是必需的。在每条消息中,内核 返回的不是一个单一的值,而是一个范围。当错误队列上有一个通知正在等待接收时,它会将连续的通 知合并起来。h]hX可以使用 recvmmsg 调用来一次性读取多个未决的数据包。这通常不是必需的。在每条消息中,内核 返回的不是一个单一的值,而是一个范围。当错误队列上有一个通知正在等待接收时,它会将连续的通 知合并起来。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h当一个新的通知即将被排队时,它会检查队列尾部的通知的范围是否可以扩展以包含新的值。如果是这 样,它会丢弃新的通知数据包,并增大未处理通知的范围上限值。h]h当一个新的通知即将被排队时,它会检查队列尾部的通知的范围是否可以扩展以包含新的值。如果是这 样,它会丢弃新的通知数据包,并增大未处理通知的范围上限值。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h对于按顺序确认数据的协议(如 TCP),每个通知都可以合并到前一个通知中,因此在任何时候在等待 的通知都不会超过一个。h]h对于按顺序确认数据的协议(如 TCP),每个通知都可以合并到前一个通知中,因此在任何时候在等待 的通知都不会超过一个。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hr有序交付是常见的情况,但不能保证。在重传和套接字拆除时,通知可能会乱序到达。h]hr有序交付是常见的情况,但不能保证。在重传和套接字拆除时,通知可能会乱序到达。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id10ah ]h"]通知批处理ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h 通知解析h]h 通知解析}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj hhhhhKubh)}(h下面的代码片段演示了如何解析控制消息:前面代码片段中的 read_notification() 调用。通知 以标准错误格式 sock_extended_err 编码。h]h下面的代码片段演示了如何解析控制消息:前面代码片段中的 read_notification() 调用。通知 以标准错误格式 sock_extended_err 编码。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(h控制数据中的级别和类型字段是协议族特定的,对于 TCP 或 UDP 套接字,分别为 IP_RECVERR 或 IPV6_RECVERR。对于 VSOCK 套接字,cmsg_level 为 SOL_VSOCK,cmsg_type 为 VSOCK_RECVERR。h]h控制数据中的级别和类型字段是协议族特定的,对于 TCP 或 UDP 套接字,分别为 IP_RECVERR 或 IPV6_RECVERR。对于 VSOCK 套接字,cmsg_level 为 SOL_VSOCK,cmsg_type 为 VSOCK_RECVERR。}(hj(hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(h错误来源是新的类型 SO_EE_ORIGIN_ZEROCOPY。如前所述,ee_errno 为零,以避免在套接字上 阻塞地读取和写入系统调用。h]h错误来源是新的类型 SO_EE_ORIGIN_ZEROCOPY。如前所述,ee_errno 为零,以避免在套接字上 阻塞地读取和写入系统调用。}(hj6hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(h32 位通知范围编码为 [ee_info, ee_data]。这个范围是包含边界值的。除了下面讨论的 ee_code 字段外,结构中的其他字段应被视为未定义的。h]h32 位通知范围编码为 [ee_info, ee_data]。这个范围是包含边界值的。除了下面讨论的 ee_code 字段外,结构中的其他字段应被视为未定义的。}(hjDhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubj)}(hX9struct sock_extended_err *serr; struct cmsghdr *cm; cm = CMSG_FIRSTHDR(msg); if (cm->cmsg_level != SOL_IP && cm->cmsg_type != IP_RECVERR) error(1, 0, "cmsg"); serr = (void *) CMSG_DATA(cm); if (serr->ee_errno != 0 || serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY) error(1, 0, "serr");h]hX9struct sock_extended_err *serr; struct cmsghdr *cm; cm = CMSG_FIRSTHDR(msg); if (cm->cmsg_level != SOL_IP && cm->cmsg_type != IP_RECVERR) error(1, 0, "cmsg"); serr = (void *) CMSG_DATA(cm); if (serr->ee_errno != 0 || serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY) error(1, 0, "serr");}hjRsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhj hhubh)}(hee_info, serr->ee_data);h]h@printf(“completed: %u..%un”, serr->ee_info, serr->ee_data);}(hj`hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj hhubeh}(h]id11ah ]h"] 通知解析ah$]h&]uh1jhjhhhhhKubj)}(hhh](j)}(h 延迟拷贝h]h 延迟拷贝}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjvhhhhhKubh)}(h传递标志 MSG_ZEROCOPY 是向内核发出的一个提示,让内核采用免拷贝的策略,同时也是一种约 定,即内核会对完成通知进行排队处理。但这并不保证拷贝操作一定会被省略。h]h传递标志 MSG_ZEROCOPY 是向内核发出的一个提示,让内核采用免拷贝的策略,同时也是一种约 定,即内核会对完成通知进行排队处理。但这并不保证拷贝操作一定会被省略。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjvhhubh)}(hX拷贝避免不总是适用的。不支持分散/聚集 I/O 的设备无法发送由内核生成的协议头加上零拷贝用户 数据组成的数据包。数据包可能需要在协议栈底层转换为一份私有数据副本,例如用于计算校验和。h]hX拷贝避免不总是适用的。不支持分散/聚集 I/O 的设备无法发送由内核生成的协议头加上零拷贝用户 数据组成的数据包。数据包可能需要在协议栈底层转换为一份私有数据副本,例如用于计算校验和。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjvhhubh)}(h在所有这些情况下,当内核释放对共享页面的持有权时,它会返回一个完成通知。该通知可能在(已 拷贝)数据完全传输之前到达。因此。零拷贝完成通知并不是传输完成通知。h]h在所有这些情况下,当内核释放对共享页面的持有权时,它会返回一个完成通知。该通知可能在(已 拷贝)数据完全传输之前到达。因此。零拷贝完成通知并不是传输完成通知。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjvhhubh)}(hX如果数据不在缓存中,延迟拷贝可能会比立即在系统调用中拷贝开销更大。进程还会因通知处理而产 生成本,但却没有带来任何好处。因此,内核会在返回时通过在 ee_code 字段中设置标志 SO_EE_CODE_ZEROCOPY_COPIED 来指示数据是否以拷贝的方式完成。进程可以利用这个信号,在 同一套接字上后续的请求中停止传递 MSG_ZEROCOPY 标志。h]hX如果数据不在缓存中,延迟拷贝可能会比立即在系统调用中拷贝开销更大。进程还会因通知处理而产 生成本,但却没有带来任何好处。因此,内核会在返回时通过在 ee_code 字段中设置标志 SO_EE_CODE_ZEROCOPY_COPIED 来指示数据是否以拷贝的方式完成。进程可以利用这个信号,在 同一套接字上后续的请求中停止传递 MSG_ZEROCOPY 标志。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjvhhubeh}(h]id12ah ]h"] 延迟拷贝ah$]h&]uh1jhjhhhhhKubeh}(h]id8ah ]h"]通知ah$]h&]uh1jhj^hhhhhK_ubeh}(h]id4ah ]h"]接口ah$]h&]uh1jhjhhhhhK