Tuesday, 18 August 2026

OS core, OS thread, process, go process, go routine

 

 Binary vs process

A Go binary sitting on disk is just a file:

certgen-service

When you run it:

./certgen-service

the OS creates a process.

Go binary
↓ run
Go process

A process is mainly a container for things like memory, files, sockets, and threads.


2. Every running process needs at least one OS thread

A generic process can be as simple as:

Process
└── OS Thread
Logical CPU
Physical CPU Core

The thread is the actual execution unit that the OS schedules.

The process itself is not what directly runs on a core.


3. Go adds another layer: goroutines

A Go process has the Go runtime inside it.

Go Process
├── Go Runtime
├── main goroutine
├── Goroutine A
├── Goroutine B
├── Goroutine C
├── runtime goroutines
└── OS Threads

Important point:

main() itself runs as the main goroutine.

Even if you never write:

go someFunction()

Go still has internal goroutines for runtime work such as GC and scheduling.


4. Goroutine is not an OS thread

A goroutine is a lightweight user-space execution unit.

You can casually think:

goroutine ≈ lightweight thread

But technically:

Goroutine != OS Thread

Many goroutines are scheduled onto a smaller number of OS threads:

Go Process

Goroutine main ─┐
Goroutine A ────┤
Goroutine B ────┤
Goroutine C ────┤
Goroutine D ────┤
Goroutine E ────┘
Go Scheduler
┌───────────────┐
│ OS Thread 1 │
│ OS Thread 2 │
│ OS Thread 3 │
└───────────────┘

Creating:

go foo()

does not mean:

new goroutine
=
new OS thread

Usually it just creates another goroutine for the Go scheduler to manage.


5. One OS thread executes one goroutine at a time

Suppose several goroutines can use Thread 1:

OS Thread 1
├── main goroutine
├── Goroutine A
├── Goroutine B
└── Goroutine C

That does not mean all four run simultaneously on Thread 1.

It means over time:

Thread 1
main runs
A runs
B runs
main runs again
C runs

Go switches which goroutine is running.

And goroutines are generally not permanently attached to a specific OS thread.

For example:

Time 1:

Thread 1 → Goroutine A
Thread 2 → Goroutine B


Time 2:

Thread 1 → Goroutine C
Thread 2 → Goroutine A

Goroutine A moved.


6. OS threads are tied to CPU execution

The OS schedules threads onto logical CPUs.

Goroutine
↓ Go scheduler
OS Thread
↓ OS scheduler
Logical CPU
Physical Core

At one instant:

Logical CPU 1 → Thread A
Logical CPU 2 → Thread B
Logical CPU 3 → Thread C
Logical CPU 4 → Thread D

So if you have 4 logical CPUs, roughly 4 OS threads can actually execute instructions simultaneously.


7. Physical core vs logical CPU

Without SMT / Hyper-Threading:

Physical Core 1
└── Logical CPU 1
one OS thread executing

With SMT / Hyper-Threading:

Physical Core 1
├── Logical CPU 1 → Thread A
└── Logical CPU 2 → Thread B

So a machine might say:

8 physical cores
16 logical CPUs

The OS generally schedules against those 16 logical CPUs.


Full Go picture

Putting everything together:

MACHINE
┌───────────┴───────────┐
│ │
Physical Core 1 Physical Core 2
├─ Logical CPU 1 ├─ Logical CPU 3
└─ Logical CPU 2 └─ Logical CPU 4
↑ ↑
│ OS Scheduler │
│ │
OS Thread 1 OS Thread 2
↑ ↑
│ Go Scheduler │
│ │
┌───────┴───────┐ ┌──────┴───────┐
│ │ │ │
main A B C
goroutine goroutine goroutine goroutine

all inside one Go process

The two schedulers are important:

Goroutines
Go scheduler
OS Threads
OS scheduler
Logical CPUs / cores

8. What happens when a goroutine launches OpenSSL

This is different because OpenSSL becomes a separate process.

Suppose you write:

go func() {
exec.Command("openssl", ...).Run()
}()

Conceptually:

Go Process
├── main goroutine
├── Goroutine A
│ │
│ └── exec.Command("openssl")
│ │
│ │ asks OS to launch
│ ↓
└── Go OS Threads


NEW PROCESS

OpenSSL Process
└── OS Thread
Logical CPU

The OpenSSL process has its own address space and own OS thread(s).

So:

Goroutine
starts
OpenSSL Process
contains
OpenSSL OS Thread
CPU

The OpenSSL process is not an OS thread itself.


9. Your certificate service example

Say you have:

8 logical CPUs
1 Go certgen process
100 goroutines
20 OpenSSL commands running

You could conceptually have:

Machine
├── Logical CPU 1
├── Logical CPU 2
├── Logical CPU 3
├── Logical CPU 4
├── Logical CPU 5
├── Logical CPU 6
├── Logical CPU 7
└── Logical CPU 8
│ OS schedules everything here
┌────────┴───────────────────────────────┐
│ │
│ Go Process │
│ ├── main goroutine │
│ ├── goroutine 1 │
│ ├── goroutine 2 │
│ ├── ... │
│ ├── goroutine 100 │
│ │ │
│ └── several Go OS threads │
│ │
├── OpenSSL Process #1 │
│ └── OS thread │
│ │
├── OpenSSL Process #2 │
│ └── OS thread │
│ │
├── OpenSSL Process #3 │
│ └── OS thread │
│ │
└── ... │
OpenSSL Process #20 │
└── OS thread │

All of those threads compete for the same CPU resources.

That's why creating:

1000 goroutines
+
1000 OpenSSL processes

does not mean you get 1000-way CPU parallelism.

If you only have 8 logical CPUs:

thousands of runnable threads/work
OS scheduler
8 logical CPUs

Only a limited amount can actually execute at once.


The shortest version to remember

Binary
↓ run
Process
↓ contains
OS Threads
↓ OS schedules onto
Logical CPUs
↓ provided by
Physical CPU cores

And for Go, insert one extra layer:

Go Process
↓ contains
Goroutines
↓ Go schedules onto
OS Threads
↓ OS schedules onto
Logical CPUs
Physical CPU cores

And for your OpenSSL case:

Go goroutine
exec.Command()
NEW OpenSSL process
OpenSSL OS thread(s)
CPU

That entire model is the foundation for understanding why goroutine count, GOMAXPROCS, CPU cores, semaphores, and spawning many OpenSSL processes affect the performance of your certificate service.

Monday, 17 August 2026

how to add swagger to docsify

 Method 1: Using docsify-swagger

The docsify-swagger GitHub repository provides a lightweight plugin to parse and render a Swagger JSON or YAML document natively within your custom Docsify pages. [1, 2]
1. Update index.html
Add the plugin script inside your main index.html file, right below the main Docsify script: [1, 2]
html
<!-- Main Docsify script -->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>

<!-- Docsify-swagger plugin -->
<script src="//cdn.jsdelivr.net/npm/docsify-swagger/dist/docsify-swagger.min.js"></script>
Use code with caution.
2. Embed the Swagger Doc in a Markdown File [1]
Create a dedicated Markdown page (e.g., api.md). You can render your OpenAPI schema by declaring a docsify-swagger block and pointing it to your hosted Swagger definition file: [1]
markdown
# API Reference

```docsify-swagger
https://swagger.io
```
Use code with caution.

Method 2: Using docsify-swagger-ui
If you prefer the official, interactive look and feel of the standard Swagger dashboard (including the "Try it out" testing panel), you can use the docsify-swagger-ui GitHub repository plugin. [1, 2]
1. Update index.html
Include both the official Swagger UI assets and the Docsify wrapper plugin in your configuration file:
html
<!-- Swagger UI CSS -->
<link rel="stylesheet" href="//://unpkg.com" />

<!-- Docsify configuration -->
<script>
  window.$docsify = {
    // your config here
  }
</script>

<!-- Docsify and Swagger UI Scripts -->
<script src="//://unpkg.com"></script>
<script src="//://unpkg.com"></script>
<script src="//://unpkg.com"></script>
Use code with caution.
2. Embed the UI Component
In your Markdown file, use a customized utility tag to position the interactive layout:
markdown
# Interactive API Explorer

<div id="swagger-ui"></div>

```javascript
window.ui = SwaggerUIBundle({
  url: 'https://swagger.io',
  dom_id: '#swagger-ui'
});
```
Use code with caution.

---------------
method 2 seems working better