Heim >php教程 >PHP开发 >Implementierung des GIT-Übertragungsprotokolls

Implementierung des GIT-Übertragungsprotokolls

高洛峰
高洛峰Original
2016-11-23 14:18:111378Durchsuche

Implementierung des GIT-Übertragungsprotokolls

Unter den drei gängigen Übertragungsprotokollen von GIT, HTTP SSH GIT, ist das GIT-Protokoll das am wenigsten verwendete Protokoll (d. h. das Protokoll, dessen URL mit git:// beginnt). Dies liegt daran, dass das Git-Protokoll fast keine Berechtigungskontrolle hat. Entweder können alle gelesen, alle geschrieben oder alle gelesen und geschrieben werden. Für Code-Hosting-Plattformen besteht der Zweck des Git-Protokolls also lediglich darin, den schreibgeschützten Zugriff auf öffentliche Projekte zu unterstützen.

Unter den verschiedenen Übertragungsprotokollen von Git ist das Git-Protokoll zweifellos das effizienteste. HTTP wird durch die Eigenschaften von HTTP eingeschränkt, und der Übertragungsprozess erfordert die Erstellung von HTTP-Anfragen und -Antworten. Wenn es sich um HTTPS handelt, ist auch eine Ver- und Entschlüsselung erforderlich. Darüber hinaus wirken sich HTTP-Timeout-Einstellungen und Paketgrößenbeschränkungen auf die Benutzererfahrung aus.

Die Leistungsprobleme des SSH-Protokolls konzentrieren sich hauptsächlich auf die Verschlüsselung und Entschlüsselung. Natürlich sind diese Kosten im Vergleich zur Informationssicherheit des Benutzers akzeptabel.

Das Git-Protokoll entspricht tatsächlich SSH ohne Verschlüsselung und Überprüfung, daher gibt es keine Möglichkeit, über Berechtigungskontrolle zu sprechen. Tatsächlich werden jedoch einige Synchronisierungsdienste innerhalb der Code-Hosting-Plattform erheblich verbessert, wenn sie mit implementiert werden das Git-Protokoll. Leistungsverbesserungen.

Übertragungsprotokollspezifikationen

Die technische Dokumentation des Git-Protokolls finden Sie unter Documentation/technical im Git-Quellcodeverzeichnis, d. h. Packfile-Übertragungsprotokolle Nach dem Erstellen einer TCP-Verbindung wird der Git Der Kunde übernimmt die Führung beim Senden des Anforderungstexts und des Anforderungsformats. Die auf BNF basierende Beschreibung lautet wie folgt:

git-proto-request = request-command SP pathname NUL [ host-parameter NUL ]
request-command   = "git-upload-pack" / "git-receive-pack" / "git-upload-archive"   ; case sensitive
pathname          = *( %x01-ff ) ; exclude NUL
host-parameter    = "host=" hostname [ ":" port ]

Ein Beispiel lautet wie folgt:

0033git-upload-pack /project. Idiot

在 C 语言中,有 popen 函数,可以创建一个进程,并将进程的标准输出或标准输入创建成一个文件指针,即 FILE*其他可以使用 C 函数的语言很多也提供了类似的实现,比如 Ruby,基于 Ruby 的 git HTTP 服务器 grack 正是使用 的 popen,相比与其他语言改造的 popen,C 语言中 popen 存在了一些缺陷,比如无法同时读写,如果要输出标准 错误,需要在命令参数中额外的将标准错误重定向到标准输出。

在 musl libc 的中,popen 的实现如下:

FILE *popen(const char *cmd, const char *mode)
{
    int p[2], op, e;
    pid_t pid;
    FILE *f;
    posix_spawn_file_actions_t fa;

    if (*mode == 'r') {
        op = 0;
    } else if (*mode == 'w') {
        op = 1;
    } else {
        errno = EINVAL;
        return 0;
    }

    if (pipe2(p, O_CLOEXEC)) return NULL;
    f = fdopen(p[op], mode);
    if (!f) {
        __syscall(SYS_close, p[0]);
        __syscall(SYS_close, p[1]);
        return NULL;
    }
    FLOCK(f);

    /* If the child's end of the pipe happens to already be on the final
     * fd number to which it will be assigned (either 0 or 1), it must
     * be moved to a different fd. Otherwise, there is no safe way to
     * remove the close-on-exec flag in the child without also creating
     * a file descriptor leak race condition in the parent. */
    if (p[1-op] == 1-op) {
        int tmp = fcntl(1-op, F_DUPFD_CLOEXEC, 0);
        if (tmp < 0) {
            e = errno;
            goto fail;
        }
        __syscall(SYS_close, p[1-op]);
        p[1-op] = tmp;
    }

    e = ENOMEM;
    if (!posix_spawn_file_actions_init(&fa)) {
        if (!posix_spawn_file_actions_adddup2(&fa, p[1-op], 1-op)) {
            if (!(e = posix_spawn(&pid, "/bin/sh", &fa, 0,
                (char *[]){ "sh", "-c", (char *)cmd, 0 }, __environ))) {
                posix_spawn_file_actions_destroy(&fa);
                f->pipe_pid = pid;
                if (!strchr(mode, &#39;e&#39;))
                    fcntl(p[op], F_SETFD, 0);
                __syscall(SYS_close, p[1-op]);
                FUNLOCK(f);
                return f;
            }
        }
        posix_spawn_file_actions_destroy(&fa);
    }
fail:
    fclose(f);
    __syscall(SYS_close, p[1-op]);

    errno = e;
    return 0;
}

在 Windows Visual C++ 中,popen 源码在 C:\Program Files (x86)\Windows Kits\10\Source\${SDKVersion}\ucrt\conio\popen.cpp , 按照 MSDN 文档说明,Windows 32 GUI 程序,即 subsystem 是 Windows 的程序,使用 popen 可能导致程序无限失去响应。

所以在笔者实现 git-daemon 及其他 git 服务器时,都不会使用 popen 这个函数。

为了支持跨平台和简化编程,笔者在实现 svn 代理服务器时就使用了 Boost Asio 库,后来也用 Asio 实现过一个 git 远程命令服务, 每一个客户端与服务器连接后,服务器启动程序,需要创建 3 条管道,分别是 子进程的标准输入 输出 错误,即 stdout stdin stderr, 然后注册读写异步事件,将子进程的输出与错误写入到 socket 发送出去,读取 socket 写入到子进程的标准输入中。

在 POSIX 系统中,boost 有一个文件描述符类 boost::asio::posix::stream_descriptor 这个类不能是常规文件,以前用 go 做 HTTP 前端 没注意就 coredump 掉。

在 Windows 系统中,boost 有文件句柄类 boost::asio::windows::stream_handle 此处的文件应当支持随机读取,比如命名管道(当然 在 Windows 系统的,匿名管道实际上也是命名管道的一种特例实现)。

以上两种类都支持 async_read async_write ,所以可以很方便的实现异步的读取。

上面的做法,唯一的缺陷是性能并不是非常高,代码逻辑也比较复杂,当然好处是,错误异常可控一些。

在 Linux 网络通信中,类似与 git 协议这样读取子进程输入输出的服务程序的传统做法是,将 子进程的 IO 重定向到 socket, 值得注意的是 boost 中 socket 是异步非阻塞的,然而,git 命令的标准输入标准错误标准输出都是同步的,所以在 fork 子进程之 前,需要将 socket 设置为同步阻塞,当 fork 失败时,要设置回来。

socket_.native_non_blocking(false);

另外,为了记录子进程是否异常退出,需要注册信号 SIGCHLD 并且使用 waitpid 函数去等待,boost 就有 boost::asio::signal_set::async_wait 当然,如果你开发这样一个服务,会发现,频繁的启动子进程,响应信号,管理连接,这些操作才是性能的短板。

一般而言,Windows 平台的 IO 并不能重定向到 socket,实际上,你如果使用 IOCP 也可以达到相应的效率。还有,Windows 的 socket API WSASocket WSADuplicateSocket 复制句柄 DuplicateHandle ,这些可以好好利用。

其他

对于非代码托管平台的从业者来说,上面的相关内容可能显得无足轻重,不过,网络编程都是殊途同归,最后核心理念都是类似的。关于 git-daemon 如果笔者有时间会实现一个跨平台的简易版并开源。


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Betten Sie Python in C/C++ einNächster Artikel:Betten Sie Python in C/C++ ein