登录
注册
开源
企业版
高校版
搜索
帮助中心
使用条款
关于我们
开源
企业版
高校版
私有云
模力方舟
AI 队友
登录
注册
轻量养虾,开箱即用!低 Token + 稳定算力,Gitee & 模力方舟联合出品的 PocketClaw 正式开售!点击了解详情~
代码拉取完成,页面将自动刷新
捐赠
捐赠前请先登录
取消
前往登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
Watch
不关注
关注所有动态
仅关注版本发行动态
关注但不提醒动态
3
Star
47
Fork
22
DreamCoders
/
CoderGuide
代码
Issues
1169
Pull Requests
0
Wiki
统计
流水线
服务
JavaDoc
PHPDoc
质量分析
Jenkins for Gitee
腾讯云托管
腾讯云 Serverless
悬镜安全
阿里云 SAE
Codeblitz
SBOM
开发画像分析
我知道了,不再自动展开
更新失败,请稍后重试!
移除标识
内容风险标识
本任务被
标识为内容中包含有代码安全 Bug 、隐私泄露等敏感信息,仓库外成员不可访问
请你谈谈关于IO同步、异步、阻塞、非阻塞的区别
待办的
#IAJKSC
陌生人
拥有者
创建于
2024-08-13 10:05
<p style="text-align: start;">同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?</p><p style="text-align: start;">这个问题其实不同的人给出的答案都可能不同,比如wiki,就认为asynchronous IO和non-blocking IO是一个东西。这其实是因为不同的人的知识背景不同,并且在讨论这个问题的时候上下文(context)也不相同。所以,为了更好的回答这个问题,我先限定一下本文的上下文。</p><p style="text-align: start;">本文讨论的背景是Linux环境下的network IO。</p><p style="text-align: start;">本文最重要的参考文献是Richard Stevens的“UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking ”,6.2节“I/O Models ”,Stevens在这节中详细说明了各种IO的特点和区别,如果英文够好的话,推荐直接阅读。</p><p style="text-align: start;">Stevens的文风是有名的深入浅出,所以不用担心看不懂。本文中的流程图也是截取自参考文献。</p><p style="text-align: start;">Stevens在文章中一共比较了五种IO Model:</p><ul><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>blocking IO</strong></span></li><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>nonblocking IO</strong></span></li><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>IO multiplexing</strong></span></li><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>signal driven IO</strong></span></li><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>asynchronous IO</strong></span></li></ul><p style="text-align: start;">由于signal driven IO在实际中并不常用,所以我这只提及剩下的四种IO Model。</p><p style="text-align: start;"><span style="color: inherit;"><strong>再说一下IO发生时涉及的对象和步骤。</strong></span></p><p style="text-align: start;">对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个阶段:</p><blockquote style="text-align: start;">等待数据准备 (Waiting for the data to be ready)<br>将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)</blockquote><p style="text-align: start;">记住这两点很重要,因为这些IO Model的区别就是在两个阶段上各有不同的情况。</p><h2 style="text-align: start;"><span style="color: inherit;">blocking IO</span></h2><p style="text-align: start;">在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710392098167.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710392098167.png" data-href="" style=""></p><p style="text-align: start;">当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。</p><p style="text-align: start;">对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。</p><p style="text-align: start;">当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。</p><p style="text-align: start;">所以,blocking IO的特点就是在IO执行的两个阶段都被block了。</p><h2 style="text-align: start;"><span style="color: inherit;">non-blocking IO</span></h2><p style="text-align: start;">linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710392961314.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710392961314.png" data-href="" style=""></p><p style="text-align: start;">从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。</p><p style="text-align: start;">用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。</p><p style="text-align: start;">所以,用户进程其实是需要不断的主动询问kernel数据好了没有。</p><h2 style="text-align: start;"><span style="color: inherit;">IO multiplexing</span></h2><p style="text-align: start;">IO multiplexing这个词可能有点陌生,但是如果我说select,epoll,大概就都能明白了。有些地方也称这种IO方式为event driven IO。我们都知道,select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。</p><p style="text-align: start;">它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。它的流程如图:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710393861880.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710393861880.png" data-href="" style=""></p><p style="text-align: start;">当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。</p><p style="text-align: start;">这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。</p><blockquote style="text-align: start;">多说一句。所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。</blockquote><p style="text-align: start;">在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。</p><h2 style="text-align: start;"><span style="color: inherit;">Asynchronous I/O</span></h2><p style="text-align: start;">linux下的asynchronous IO其实用得很少。先看一下它的流程:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710394791838.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710394791838.png" data-href="" style=""></p><p style="text-align: start;">用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。</p><p style="text-align: start;">然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。</p><p style="text-align: start;">到目前为止,已经将四个IO Model都介绍完了。现在回过头来回答最初的那几个问题:<span style="color: inherit;"><strong>blocking和non-blocking的区别在哪,synchronous IO和asynchronous IO的区别在哪。</strong></span></p><p style="text-align: start;">先回答最简单的这个:blocking vs non-blocking。前面的介绍中其实已经很明确的说明了这两者的区别。调用blocking IO会一直block住对应的进程直到操作完成,而non-blocking IO在kernel还准备数据的情况下会立刻返回。</p><p style="text-align: start;">在说明synchronous IO和asynchronous IO的区别之前,需要先给出两者的定义。Stevens给出的定义(其实是POSIX的定义)是这样子的:</p><blockquote style="text-align: start;">A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;<br>An asynchronous I/O operation does not cause the requesting process to be blocked;</blockquote><p style="text-align: start;">两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义,之前所述的blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO。</p><p style="text-align: start;">有人可能会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO操作,就是例子中的recvfrom这个system call。non-blocking IO在执行recvfrom这个system call的时候,如果kernel的数据没有准备好,这时候不会block进程。</p><p style="text-align: start;">但是,当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。而asynchronous IO则不一样,当进程发起IO 操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。</p><p style="text-align: start;">各个IO Model的比较如图所示:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710395680392.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710395680392.png" data-href="" style=""></p><p style="text-align: start;">经过上面的介绍,会发现non-blocking IO和asynchronous IO的区别还是很明显的。在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。</p><p style="text-align: start;">而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。</p><p style="text-align: start;"><span style="color: inherit;"><strong>最后,再举几个不是很恰当的例子来说明这四个IO Model:</strong></span></p><p style="text-align: start;">有A,B,C,D四个人在钓鱼:</p><ul><li style="text-align: start;">A用的是最老式的鱼竿,所以呢,得一直守着,等到鱼上钩了再拉杆;</li><li style="text-align: start;">B的鱼竿有个功能,能够显示是否有鱼上钩,所以呢,B就和旁边的MM聊天,隔会再看看有没有鱼上钩,有的话就迅速拉杆;</li><li style="text-align: start;">C用的鱼竿和B差不多,但他想了一个好办法,就是同时放好几根鱼竿,然后守在旁边,一旦有显示说鱼上钩了,它就将对应的鱼竿拉起来;</li><li style="text-align: start;">D是个有钱人,干脆雇了一个人帮他钓鱼,一旦那个人把鱼钓上来了,就给D发个短信。</li></ul>
<p style="text-align: start;">同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?</p><p style="text-align: start;">这个问题其实不同的人给出的答案都可能不同,比如wiki,就认为asynchronous IO和non-blocking IO是一个东西。这其实是因为不同的人的知识背景不同,并且在讨论这个问题的时候上下文(context)也不相同。所以,为了更好的回答这个问题,我先限定一下本文的上下文。</p><p style="text-align: start;">本文讨论的背景是Linux环境下的network IO。</p><p style="text-align: start;">本文最重要的参考文献是Richard Stevens的“UNIX® Network Programming Volume 1, Third Edition: The Sockets Networking ”,6.2节“I/O Models ”,Stevens在这节中详细说明了各种IO的特点和区别,如果英文够好的话,推荐直接阅读。</p><p style="text-align: start;">Stevens的文风是有名的深入浅出,所以不用担心看不懂。本文中的流程图也是截取自参考文献。</p><p style="text-align: start;">Stevens在文章中一共比较了五种IO Model:</p><ul><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>blocking IO</strong></span></li><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>nonblocking IO</strong></span></li><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>IO multiplexing</strong></span></li><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>signal driven IO</strong></span></li><li style="text-align: start;"><span style="color: rgb(225, 60, 57);"><strong>asynchronous IO</strong></span></li></ul><p style="text-align: start;">由于signal driven IO在实际中并不常用,所以我这只提及剩下的四种IO Model。</p><p style="text-align: start;"><span style="color: inherit;"><strong>再说一下IO发生时涉及的对象和步骤。</strong></span></p><p style="text-align: start;">对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的process (or thread),另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个阶段:</p><blockquote style="text-align: start;">等待数据准备 (Waiting for the data to be ready)<br>将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)</blockquote><p style="text-align: start;">记住这两点很重要,因为这些IO Model的区别就是在两个阶段上各有不同的情况。</p><h2 style="text-align: start;"><span style="color: inherit;">blocking IO</span></h2><p style="text-align: start;">在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710392098167.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710392098167.png" data-href="" style=""></p><p style="text-align: start;">当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。</p><p style="text-align: start;">对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。</p><p style="text-align: start;">当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。</p><p style="text-align: start;">所以,blocking IO的特点就是在IO执行的两个阶段都被block了。</p><h2 style="text-align: start;"><span style="color: inherit;">non-blocking IO</span></h2><p style="text-align: start;">linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710392961314.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710392961314.png" data-href="" style=""></p><p style="text-align: start;">从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。</p><p style="text-align: start;">用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。</p><p style="text-align: start;">所以,用户进程其实是需要不断的主动询问kernel数据好了没有。</p><h2 style="text-align: start;"><span style="color: inherit;">IO multiplexing</span></h2><p style="text-align: start;">IO multiplexing这个词可能有点陌生,但是如果我说select,epoll,大概就都能明白了。有些地方也称这种IO方式为event driven IO。我们都知道,select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。</p><p style="text-align: start;">它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。它的流程如图:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710393861880.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710393861880.png" data-href="" style=""></p><p style="text-align: start;">当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。</p><p style="text-align: start;">这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。</p><blockquote style="text-align: start;">多说一句。所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。</blockquote><p style="text-align: start;">在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。</p><h2 style="text-align: start;"><span style="color: inherit;">Asynchronous I/O</span></h2><p style="text-align: start;">linux下的asynchronous IO其实用得很少。先看一下它的流程:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710394791838.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710394791838.png" data-href="" style=""></p><p style="text-align: start;">用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。</p><p style="text-align: start;">然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。</p><p style="text-align: start;">到目前为止,已经将四个IO Model都介绍完了。现在回过头来回答最初的那几个问题:<span style="color: inherit;"><strong>blocking和non-blocking的区别在哪,synchronous IO和asynchronous IO的区别在哪。</strong></span></p><p style="text-align: start;">先回答最简单的这个:blocking vs non-blocking。前面的介绍中其实已经很明确的说明了这两者的区别。调用blocking IO会一直block住对应的进程直到操作完成,而non-blocking IO在kernel还准备数据的情况下会立刻返回。</p><p style="text-align: start;">在说明synchronous IO和asynchronous IO的区别之前,需要先给出两者的定义。Stevens给出的定义(其实是POSIX的定义)是这样子的:</p><blockquote style="text-align: start;">A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;<br>An asynchronous I/O operation does not cause the requesting process to be blocked;</blockquote><p style="text-align: start;">两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义,之前所述的blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO。</p><p style="text-align: start;">有人可能会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO操作,就是例子中的recvfrom这个system call。non-blocking IO在执行recvfrom这个system call的时候,如果kernel的数据没有准备好,这时候不会block进程。</p><p style="text-align: start;">但是,当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。而asynchronous IO则不一样,当进程发起IO 操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。</p><p style="text-align: start;">各个IO Model的比较如图所示:</p><p style="text-align: center;"><img src="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710395680392.png" alt="https://jsd.onmicrosoft.cn/gh/iGaoWei/codercdn@master/question/20240627/2024062710395680392.png" data-href="" style=""></p><p style="text-align: start;">经过上面的介绍,会发现non-blocking IO和asynchronous IO的区别还是很明显的。在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。</p><p style="text-align: start;">而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。</p><p style="text-align: start;"><span style="color: inherit;"><strong>最后,再举几个不是很恰当的例子来说明这四个IO Model:</strong></span></p><p style="text-align: start;">有A,B,C,D四个人在钓鱼:</p><ul><li style="text-align: start;">A用的是最老式的鱼竿,所以呢,得一直守着,等到鱼上钩了再拉杆;</li><li style="text-align: start;">B的鱼竿有个功能,能够显示是否有鱼上钩,所以呢,B就和旁边的MM聊天,隔会再看看有没有鱼上钩,有的话就迅速拉杆;</li><li style="text-align: start;">C用的鱼竿和B差不多,但他想了一个好办法,就是同时放好几根鱼竿,然后守在旁边,一旦有显示说鱼上钩了,它就将对应的鱼竿拉起来;</li><li style="text-align: start;">D是个有钱人,干脆雇了一个人帮他钓鱼,一旦那个人把鱼钓上来了,就给D发个短信。</li></ul>
评论 (
0
)
登录
后才可以发表评论
状态
待办的
待办的
进行中
已完成
已关闭
负责人
未设置
标签
Java
未设置
标签管理
里程碑
未关联里程碑
未关联里程碑
Pull Requests
未关联
未关联
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
未关联
分支 (
-
)
标签 (
-
)
开始日期   -   截止日期
-
置顶选项
不置顶
置顶等级:高
置顶等级:中
置顶等级:低
优先级
不指定
严重
主要
次要
不重要
参与者(1)
1
https://gitee.com/DreamCoders/CoderGuide.git
git@gitee.com:DreamCoders/CoderGuide.git
DreamCoders
CoderGuide
CoderGuide
点此查找更多帮助
搜索帮助
Git 命令在线学习
如何在 Gitee 导入 GitHub 仓库
Git 仓库基础操作
企业版和社区版功能对比
SSH 公钥设置
如何处理代码冲突
仓库体积过大,如何减小?
如何找回被删除的仓库数据
Gitee 产品配额说明
GitHub仓库快速导入Gitee及同步更新
什么是 Release(发行版)
将 PHP 项目自动发布到 packagist.org
评论
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册