---
id: 43974d116e135fd9d3478f658dcf71c6
author: Lemon Mint
title: 'HQQ: Izolácia C knižníc od Go cez Shared Memory s Cache-Line'
description: Runtime pre IPC bez cgo, kompilátor IDL (hqq-bindgen) a overený procesový supervízor, ktoré spúšťajú natívne knižnice C11 v izolovaných pracovných procesoch prostredníctvom zdieľaných pamäťových kruhových bufferov.
language: sk
date: 2026-10-18T13:56:46.094643498Z
path: /hqq
go_package: gosuda.org/hqq
go_repourl: https://github.com/gosuda/HQQ.git
category: lowlevel
---

`gosuda.org/hqq` je runtime pre medziprocesovú komunikáciu bez zero-cgo a toolchain na generovanie kódu pre Go a C11. Izoluje natívne C knižnice vo vnútri samostatných procesov OS workerov a prepája ich s volajúcimi v Go prostredníctvom zdieľaných kruhových bufferov (rings) v pamäti mapovanej do pamäte, zarovnaných na hranice cache riadkov. Projekt kombinuje dve úrovne transportu (`StandardLink` pre dedikované jednosmerné SPSC relácie a `Advanced` pre viacworkerové MPMC rozosielanie), kompilátor IDL `hqq-bindgen` a supervízor procesov s overovaním integrity (`secureexec` a `launch`).

```bash
go get gosuda.org/hqq
```

## Cena zdieľania adresného priestoru s C

Každý inžinier systémov v Go skôr či neskôr narazí na rovnakú rázcestie. Budujete službu v Go, ktorá sa skompiluje počas niekoľkých sekúnd s `CGO_ENABLED=0`, distribuuje sa ako jeden statický binárny súbor, krížovo sa kompiluje z notebooku pre cieľové systémy Linux alebo Windows bez sysroot a beží v pamäťovo bezpečnej heape spravovanej garbage collectorom. Potom potrebujete zrelú C knižnicu: SQLite pre vnorené relačné úložisko, Zstandard (`libzstd`) pre kompresiu so slovníkom alebo hardvérovo akcelerovanú knižnicu pre médiá alebo kryptografiu.

Povolenie `cgo` na prepojenie tejto C knižnice do procesu Go síce okamžite vyrieši problém väzby, no zavádza tri štrukturálne nevýhody:

1. **Previazanie toolchainu a krížovej kompilácie**: `CGO_ENABLED=1` vyžaduje C kompilátor, hlavičkové súbory cieľovej libc a platformový linker pre každý cieľový operačný systém a architektúru. Zostavenie artefaktov `linux/arm64` alebo `windows/arm64` z pracovnej stanice `darwin/arm64` mení jediný príkaz `go build` na maticu krížových kompilátorov a sysroot.
2. **Réžia plánovania behu**: Každé volanie `cgo` prechádza hranicou runtime jazyka Go, prepína sa z rastúceho zásobníka goroutine na systémový zásobník a viaže vlákno OS (`M`). Pri jemnozrnných volaniach sa daň za prechod (približne 50 až 150 ns na volanie) a konkurencia plánovača rýchlo nahromadia.
3. **Kolaps domény porúch**: Kód C beží vo rovnakom virtuálnom adresnom priestore ako runtime Go. Jediný zápis mimo hraníc, použitie po uvoľnení, pretečenie zásobníka alebo `abort()` vnútri funkcie C poškodí metadáta heapy Go alebo okamžite ukončí celý serverový proces, obíde `recover()` a so sebou strhne každú prebiehajúcu požiadavku HTTP alebo RPC.

Keď sa tímy snažia vyhnúť `cgo`, bežné alternatívy vynucujú iný kompromis. Mechanické preklady kódovej základne C do čistého Go zachovávajú `CGO_ENABLED=0`, no často zaostávajú za upstream vydaniami C, nesú emulovanú vrstvu virtuálnej pamäte a pri záťaži intenzívne alokujú. Presun knižnice C do pomocného procesu cez loopback TCP alebo Unix doménové sokety obnoví izoláciu porúch, no každé volanie platí dvoma prepnutiami kontextu jadra, kopírovaním vyrovnávacích pamätí soketov a serializáciou na drôte.

HQQ sme vytvorili po položení priamej otázky: čo ak by knižnica C žila v samostatnom procese OS s vlastným virtuálnym adresným priestorom, zatiaľ čo Go a C by si rámce vymieňali priamo prostredníctvom zdieľaných riadkov cache procesora bez vstupu do jadra na rýchlej ceste?

| Model integrácie                   | `CGO_ENABLED=0` | Izolácia porúch    | Systémové volania na rýchlej ceste | Alokácie na heape na volanie | Verná kópia upstream C11 |
| :--------------------------------- | :-------------- | :----------------- | ---------------------------------: | ---------------------------: | :----------------------- |
| **`cgo` lineárne prepojenie v procese** | Nie        | Žiadna (zdieľaná heap) |                                  0 |                      0 až 1+ | Presné upstream C        |
| **RPC podprocesu cez sokety / rúry**    | Áno        | Hranica procesu        |                                2 až 4 |       Viacnásobné (rámcovanie + I/O) | Presné upstream C        |
| **Mechanický preklad C do Go**          | Áno        | Go v rámci procesu     |                                  0 |                 Vysoké pri zložitých volaniach | Emulovaný runtime C      |
| **HQQ IPC so zdieľanou pamäťou**        | Áno        | Hranica procesu        |                 0 (adaptívne spinovanie) |           0 (`StandardLink`) | Presné upstream C        |

## Architektonický prehľad: štyri vrstvy naprieč hranicami procesov

HQQ rozdeľuje izoláciu procesov do štyroch skladateľných vrstiev, aby mechanika kruhových bufferov v zdieľanej pamäti, typovo bezpečné generovanie kódu RPC, overovanie binárnej integrity a správa podriadených procesov zostali nezávislé.

```text
+-------------------------------------------------------------------+
| Aplikačný proces Go (CGO_ENABLED=0)                               |
|                                                                   |
|  +-----------------------+     +-------------------------------+  |
|  | Generovaný klient Go  |     | Supervízor procesov           |  |
|  | (gosuda.org/hqq/      |     | (gosuda.org/hqq/launch +      |  |
|  |  bindgen/rt)          |     |  gosuda.org/hqq/secureexec)   |  |
|  +-----------+-----------+     +---------------+---------------+  |
|              |                                 |                  |
|  +-----------v-----------+                     | Anonymná rúra    |
|  | Transportná úroveň    |                     | Lifeline         |
|  | (StandardLink /       |                     | (--parent-stdin) |
|  |  Advanced MPMC)       |                     |                  |
+--------------+---------------------------------+------------------+
               |                                 |
===============|========= Zdieľaná pamäť =========|==================
               |     (mmap / CreateFileMapping)  |
+--------------v---------------------------------v------------------+
| Worker proces C11 (izolovaný adresný priestor)                    |
|                                                                   |
|  +-----------------------+     +-------------------------------+  |
|  | Generovaný server C11 |---->| Tabuľka handle s kontrolou    |  |
|  | a dávkové shimy       |     | generácie (hqq_rpc.c)         |  |
|  +-----------+-----------+     +---------------+---------------+  |
|              |                                 |                  |
|              +----------------+----------------+                  |
|                               |                                   |
|                     +---------v---------+                         |
|                     | Natívna knižnica C|                         |
|                     | (SQLite, Zstd...) |                         |
|                     +-------------------+                         |
+-------------------------------------------------------------------+
```

### 1. Transport so zdieľanou pamäťou (`gosuda.org/hqq` a `c/`)

Základný balík `gosuda.org/hqq` a jeho náprotivok v C11 v adresári `c/` implementujú dva režimy transportu so zdieľanou pamäťou nad POSIX `mmap` (Linux a macOS) a Win32 `CreateFileMappingW` (Windows). Každý zdieľaný segment začína overeným superblokom `HQQ_STL3`, ktorý pevne určuje verziu protokolu, geometriu bufferov a PID koncových bodov skôr, než sa prečíta akýkoľvek index kruhu.

Všetky horúce riadiace slová a hlavičky slotov sú zarovnané na hranice cache riadkov po 64 bajtoch, aby jadrá producenta a konzumenta nikdy nesúperili o rovnaký cache riadok vplyvom falošného zdieľania.

```text
Rozloženie slotu StandardLink (zarovnané na cache riadok 64 bajtov)
Posun v bajtoch:  0x00              0x08              0x10                          0x40
                  +-------------------+-------------------+-----------------------------+
Riadok hlavičky:  | seq (u64 atomic)  | len (u32) | flags | rezerva (doplnená nulami)   |
                  +-------------------+-------------------+-----------------------------+
Oblasť payloadu:  | bajty payloadu [0 .. BufferSize - 1] (kódované / dekódované priamo)  |
                  +---------------------------------------------------------------------+
```

| Pole      | Posun  |         Veľkosť | Význam                                                                                                                       |
| :-------- | -----: | --------------: | :--------------------------------------------------------------------------------------------------------------------------- |
| `seq`     | `0x00` |            8 B  | Monotónny sekvenčný čítač. Nepárne hodnoty označujú zápis v procese; párne hodnoty publikujú potvrdený rámec cez store-release. |
| `len`     | `0x08` |            4 B  | Dĺžka payloadu v bajtoch zapísaného do slotu (`0 <= len <= BufferSize`).                                                     |
| `flags`   | `0x0C` |            4 B  | Riadiace bity rámca (pokračovanie, stav chyby alebo oznámenie o ukončení).                                                   |
| `payload` | `0x40` | `BufferSize`    | Súvislá oblasť payloadu, čítaná a zapisovaná na mieste bez medziľahlých kópií.                                               |

Volajúci si vyberajú medzi dvoma úrovňami transportu podľa štruktúry súbežnosti:

- **`StandardLink` (SPSC relácia 1:1)**: Spája klientsku a serverovú reláciu cez dedikované požiadavkové a odpoveďové sloty chránené sekvenčným zámkom (`SeqLock`). Na rýchlej ceste zapisovateľ serializuje údaje priamo do mapovaného slotu a potvrdí ich atomickým store-release; čitateľ krátko spinuje v používateľskom priestore a až potom sa uchýli k primitívam prebudenia OS (`futex` na Linuxe, `__ulock_wait` na macOS, `WaitOnAddress` na Windows). Jeden rámec v rámci kompletného cyklu sa vykoná približne za 200 ns s `0 allocs/op`.
- **`Advanced` (MPMC rozosielací kruh)**: Multiplexuje `M` súbežných volajúcich v Go cez `N` vlákien workerov C pomocou bezzámkového viacproducentského viackonzumentského odoslacieho kruhu, zdieľaného fondu slotov a indexovanej tabuľky dokončení. Volajúci si rezervujú slot, publikujú jeho index do kruhu a čakajú na svoj záznam o dokončení, zatiaľ čo vlákna workerov vyberajú požiadavky a dokončujú ich v ľubovoľnom poradí.

Obe úrovne priebežne sledujú dostupnosť partnera. Každý koncový bod aktualizuje atomickú monotónnu časovú pečiatku cez `Heartbeat()` a počas čakania kontroluje `PeerAlive(timeout)`. Ak proces workera uprostred volania zlyhá, partner okamžite zistí neaktuálny heartbeat alebo mŕtvy PID, preruší blokované čakania s `ErrPeerGone` a podporuje atomické prevzatie relácie (`Takeover`) bez toho, aby zostali zaseknuté súbory zdieľanej pamäte.

### 2. RPC bez alokácií a handle s kontrolou generácie (`gosuda.org/hqq/bindgen`)

Ručné písanie rámcovania zdieľanej pamäte pre C knižnicu s desiatkami funkcií je únavné a náchylné na chyby. `hqq-bindgen` je kompilátor schém, ktorý načíta súbor Interface Definition Language (`.idl`) (alebo z hlavičky C `.h` priamo vytvorí počiatočný súbor príkazom `hqq-bindgen init`) a vygeneruje balík klienta Go bez zero-cgo spolu so skeletonom servera C11.

Najnebezpečnejšou chybou v akejkoľvek väzbe na C je vystavenie surových ukazovateľov C (`sqlite3*`, `sqlite3_stmt*`, `ZSTD_CCtx*`) cez hranicu jazyka. Ak volajúci odovzdá ukazovateľ po jeho uvoľnení, odovzdá ukazovateľ patriaci inej relácii alebo poškodí celočíselnú adresu, knižnica C dereferencuje neplatnú pamäť.

`hqq-bindgen` zakazuje surové ukazovatele na drôte. Namiesto toho IDL modeluje stavové prostriedky C ako typované deklarácie `handle`:

```text
service sqlite3 {
    handle Db;
    handle Stmt @owned_by(Db);

    fn open_v2(path: string, flags: i32) -> result<Db, i32>;
    fn prepare_one(db: Db, sql: string) -> result<Stmt, i32>;
    fn finalize(stmt: @consume Stmt) -> result<void, i32>;
    fn close(db: @consume Db) -> result<void, i32>;
}
```

Na drôte je každý handle 64-bitové celé číslo, ktoré obsahuje 32-bitový index tabuľky a 32-bitový nenulový čítač generácie: `(uint64_t)generation << 32 | index`. Runtime C (`c/src/hqq_rpc.c`) udržiava tabuľku handle pre každú reláciu s tromi invariantmi:

1. **Overenie generácie**: Každé vyhľadanie overí, že slot je obsadený, jeho uložená generácia sa zhoduje s hornými 32 bitmi handle a jeho typová značka handle zodpovedá typu parametra (`Db` verzus `Stmt`). Opätovné použitie uzavretého handle zlyhá okamžite s `HQQRPC_E_BAD_HANDLE` ešte pred volaním knižnice C.
2. **Hierarchické vlastníctvo (`@owned_by`)**: Keďže `Stmt` je anotovaný `@owned_by(Db)`, uzavretie handle `Db` automaticky prejde všetky živé podriadené handle `Stmt` patriace tomuto `Db` a spustí ich deštruktory v opačnom poradí alokácie (`sqlite3_finalize` pred `sqlite3_close_v2`).
3. **Čistenie pri ukončení relácie**: Ak sa klient Go odpojí alebo zlyhá bez uzavretia svojich handle, deštruktor relácie na serveri C prejde celú tabuľku handle relácie v opačnom poradí a zaručí nulový únik alokácií C pri reštartoch klienta.

Skôr než relácia spracuje prvé volanie RPC, klient a server si vymenia 32-bajtový digest rozhrania SHA-256 vypočítaný nad kanonizovaným IDL (predpona `HQQ-BINDGEN-IDL-V1\n`). Ak boli klient Go a worker C skompilované z nekompatibilných schém IDL, handshake pripojenie už pri inicializácii odmietne.

### 3. Binárne spúšťanie odolné voči manipulácii (`gosuda.org/hqq/secureexec`)

Presun kódu C do binárneho súboru workera vyvoláva otázku dodávateľského reťazca: ako proces Go zistí, že binárny súbor workera na disku nebol medzi overením a spustením zmenený alebo nahradený?

Výpočet `crypto/sha256` nad cestou k súboru, za ktorým nasleduje `os/exec.Command(path).Start()`, ponecháva okno Time-of-Check to Time-of-Use (TOCTOU), v ktorom môže iný proces súbor prepísať alebo vymeniť skôr, než ho jadro spustí. `gosuda.org/hqq/secureexec` toto okno uzatvára:

- Na Linuxe `secureexec` otvorí binárny súbor, preloží jeho obsah do anonymného deskriptora pamäťového súboru vytvoreného cez `memfd_create(MFD_CLOEXEC | MFD_ALLOW_SEALING)` a zároveň vypočíta jeho hash SHA-256, overí hash voči pevne určenému 32-bajtovému digestu alebo manifestu vydania podpísanému Ed25519 (`cmd/hqq-manifest`), aplikuje `fcntl(F_ADD_SEALS, F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)`, aby žiadny proces v systéme nemohol stránky modifikovať, a potomka spustí z zapečateného deskriptora.
- Na macOS a Windows `secureexec` materializuje overené binárne súbory do adresárov vyrovnávacej pamäte s uzamknutými oprávneniami, ktorých názvy sú odvodené od digestu SHA-256, a pred spustením overuje integritu veľkosti súboru a digestu.

### 4. Supervízia procesu s väzbou na rodiča (`gosuda.org/hqq/launch`)

Keď aplikácia Go spustí proces workera C na pozadí, worker nesmie nikdy prežiť svojho rodiča ako osirelý proces, ani keď je rodičovský proces Go ukončený signálom `SIGKILL` (`kill -9`) a nemôže spustiť obslužné rutiny `defer` na upratovanie.

`gosuda.org/hqq/launch` vynucuje previazanie životnosti cez anonymnú rúru OS odovzdanú na štandardný vstup workera s príznakom `--parent-stdin`. Rodičovský proces Go drží zapisovací koniec rúry a nikdy doňho nezapisuje; worker C sleduje čítací koniec na vyhradenom vlákne lifeline. Ak proces Go skončí normálne, spadne panikou alebo je ukončený jadrom OS (`SIGKILL` alebo OOM killer), jadro okamžite uzavrie zapisovací deskriptor. Worker C dostane `EOF` na rúre v rámci mikrosekúnd, preruší všetky aktívne volania, spustí deštruktory svojich handle relácie, odstráni prepojenia súborov zdieľanej pamäte a čisto skončí.

## Rýchly štart: volanie generovanej služby

S `hqq-bindgen` a `launch` sa volanie izolovaného workera C z Go správa ako bežný typovaný klient Go, pričom zostáva zachované `CGO_ENABLED=0`:

```go
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"gosuda.org/hqq/bindgen/rt"
	"gosuda.org/hqq/launch"
	"gosuda.org/hqq/secureexec"
)

func main() {
	// Vytvorenie kontextu s časovým limitom 5 sekúnd
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	// Spracovanie pevne určeného digestu SHA-256 overovaného workera
	hash, err := secureexec.ParseHash("7649b1b45dacd934cde8149f35f4fcc3937f6486fbac23ce976466719b3752bb")
	if err != nil {
		log.Fatal(err)
	}

	// Vytvorenie názvu listenera pre zdieľanú pamäť
	listener, err := launch.NewListenerName("demo")
	if err != nil {
		log.Fatal(err)
	}

	// Spustenie overeného workera s pripojením na listener
	srv, err := launch.Start(ctx, launch.Config{
		Binary:   "/path/to/verified-worker",
		SHA256:   hash,
		Listener: listener,
		Args:     []string{listener},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer srv.Close()

	fmt.Printf("worker beží na zdieľanom listeneri %s (opts=%+v)\n", listener, rt.SessionOptions{})
}
```

## Štruktúra repozitára

Každý subsystém v `gosuda.org/hqq` je organizovaný do zameraných balíkov s jasne vymedzenými hranicami:

```text
gosuda.org/hqq
├── standard.go / ring.go      # StandardLink (SPSC) a nízkoúrovňové primitíva kruhu zdieľanej pamäte
├── advanced.go / slotpool.go  # Rozosielač Advanced (MPMC), fond slotov a tabuľka dokončení
├── shm_*.go / wait_*.go       # Špecifické pre OS: mmap/CreateFileMapping a futex/ulock/WaitOnAddress
├── c/                         # Transportná knižnica C11 (hqq.c) a runtime servera RPC (hqq_rpc.c)
├── bindgen/                   # Parser IDL hqq-bindgen, verifikátor (V1-V41) a generátor kódu Go/C11
│   └── rt/                    # Runtime klienta Go bez alokácií pre generované služby
├── secureexec/                # Overenie manifestu SHA-256 / Ed25519 a spúšťanie zapečateného cez memfd
├── launch/                    # Supervízor workerov s riadením lifeline cez --parent-stdin
├── cmd/hqq-manifest/          # CLI nástroj na generovanie a podpisovanie manifestov binárnych vydaní
└── spec/                      # Spustiteľné špecifikácie drôtu a referenčné testovacie vektory (BINDGEN.md)
```

Ak chcete vidieť, ako HQQ poháňa priamu produkčnú náhradu za dve z najpoužívanejších knižníc `cgo` v ekosystéme Go, pozrite si doplnkové balíky postavené na tomto zásobníku:

- **[`gosuda.org/sqlite3`](https://gosuda.org/sqlite3)**: Priamy ovládač `database/sql` kompatibilný s `github.com/mattn/go-sqlite3`, ktorý beží nad natívnym C SQLite `3.54.1` cez zdieľanú pamäť s pipelinovým predbežným načítavaním dávok.
- **[`gosuda.org/zstd`](https://gosuda.org/zstd)**: Priama náhrada za `github.com/DataDog/zstd`, ktorá beží nad natívnym C `libzstd 1.5.7` v izolovanom workeri so zdieľanou pamäťou, s kontextami bez alokácií a hromadným dávkovaním.
- **Repozitár na GitHube**: [https://github.com/gosuda/HQQ](https://github.com/gosuda/HQQ)
