Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughAdds a new markdown blog post detailing Linux process memory layout, tooling (strace, pmap, readelf), architecture notes (lscpu, address sizes), example C programs, and references. No code or API changes; only documentation content is introduced. Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Pull Request Overview
This PR adds a new blog post titled "More about program memory layout" that provides a deeper dive into how programs are structured in computer memory, building upon a previous post. The content focuses on practical examples and tools for understanding memory management on Linux systems.
Key changes:
- Adds comprehensive documentation about process address space, including kernel and user space divisions
- Introduces practical Linux tools (strace, lscpu, pmap, readelf) for memory analysis
- Provides code examples and command-line demonstrations for hands-on learning
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
|
|
||
| The output of using the *strace* tool will be a big chunk of text, showing all the syscalls performed by the process. And a bunch of those are related to setting the program to execute properly. | ||
|
|
||
| \*Something to keep in mind is that executing syscalls is not a cheap operation, so in order to improve a program's performance we can try to minimize the quantity of those. |
There was a problem hiding this comment.
The escaped asterisk should be plain text. Remove the backslash: '*Something to keep in mind...'
| \*Something to keep in mind is that executing syscalls is not a cheap operation, so in order to improve a program's performance we can try to minimize the quantity of those. | |
| *Something to keep in mind is that executing syscalls is not a cheap operation, so in order to improve a program's performance we can try to minimize the quantity of those. |
| But wait. How big is the process address space? The following table must help understand. | ||
|
|
||
| | 32-bit processors | 64-bit processors | | ||
| |----------------- |------------------ | |
There was a problem hiding this comment.
The table separator has inconsistent dashes. Should be consistent: '|------------------|-------------------|'
| |----------------- |------------------ | | |
| |------------------|-------------------| |
| ``` | ||
| ... |
There was a problem hiding this comment.
The code block is incomplete. The content after '...' on line 160 appears to be cut off, leaving an unfinished section before the References.
| ``` | |
| ... |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (12)
posts/2025-08-09-more-about-program-memory.md (12)
2-6: Front‑matter looks good; consider adding an excerpt/description for previews.If your blog engine supports it, adding an
description:/excerpt:improves SEO and list-page previews. Also confirmshow: trueis the intended flag on your platform.What front‑matter keys does your static site generator support for summaries (e.g.,
description,summary,image)? Want me to add them now?
22-32: Use a neutral code fence for the ASCII diagram.Labeling the block as bash suggests it’s executable. Prefer
textfor readability and correct syntax highlighting.-```bash +```text # +--------------------+ # | | # | Kernel space | # | | # +--------------------+ # | | # | User space | # | | # +--------------------+--- `38-43`: **Polish the bullets and clarify the KPTI sentence.** - “Less trashing TLB” → “Less TLB thrashing”. - The KPTI line has a double negative feel. Rephrase for clarity. ```diff -- Less trashing TLB (Translation Lookaside Buffer) +- Less TLB thrashing (Translation Lookaside Buffer) - -In modern Linux, for security reasons, the entire kernel is usually not always mapped. If you'd like to get a better understanding of this, search for Linux KPTI (Linux Kernel Page-Table Isolation). +In modern Linux, for security reasons, the kernel is not always mapped into user page tables (see KPTI: Kernel Page‑Table Isolation).
44-48: Use a proper heading instead of bold to satisfy markdownlint (MD036).Currently this triggers “Emphasis used instead of a heading.”
-**Kernel space** +### Kernel space
49-58: Make the compile command broadly portable.Not every system has
clang-18on PATH. Usingccorclang+-O2 -g -Wallmakes the snippet copy/paste‑friendly.-// clang-18 main.c -o userProgram +// cc -O2 -g -Wall main.c -o userProgram
74-77: Tighten the wording.Minor style polish and accuracy note: syscalls are relatively expensive compared to userspace calls, so minimizing them in hot paths can help.
-*Something to keep in mind is that executing syscalls is not a cheap operation, so in order to improve a program's performance we can try to minimize the quantity of those. +*Note: Syscalls are relatively expensive compared to userspace calls, so to improve performance—especially in hot paths—minimize their number when practical.
91-95: Clarify “address sizes” as virtual address size.
lscpureports “Address sizes” (physical, virtual). Explicitly calling out “virtual address size” avoids confusion with physical address width.-The result presents many details of your CPU, including the total cache size discriminated by the cache level, and the address sizes, which in my case uses 48 bits. +The result presents many details of your CPU, including cache sizes and the virtual address size (48 bits on my machine).
124-130: Nice use of$!and-XX; add a small robustness note.This works because the process is paused by
getchar(). It might be worth mentioning that without the pause the backgrounded process can exit beforepmapruns.
140-147: Tighten the pmap columns description and casing.
- “p” stands for private and “s” for shared.
- “inode” is lowercase; “RSS” should be uppercase.
- Clarify that “Size” is virtual, “RSS” is resident in physical memory;
-XXadds more details (like Dirty/Clean).-Next, after the column that presents the memory address for where that section starts, we have a column showing the permissions for the memory location, which could be the conventional read, write and execute, and there's an additional setting that defines if the memory address can be shared with other processes or not (p is for private). +Next to the start address, pmap shows permissions (r/w/x) and whether the mapping is private (`p`) or shared (`s`). @@ -Next, the Inode in Linux specifies the file id. So, if the Inode repeats at this table it means that the same file is being mapped into the process' memory addresses, maybe changing only the offset. +Next, the inode specifies the file id. If the inode repeats, the same file is mapped multiple times, possibly at different offsets. @@ -Then we have the Size column, which specifies the size of the memory segment in bytes, and Rss specifies what part of this memory segment is actually in physical memory. +Then we have the Size column (virtual bytes of the mapping) and RSS (resident bytes currently in physical memory). With `-XX`, you’ll also see additional breakdowns (e.g., Clean/Dirty).
150-159: Minorreadelfimprovements.
-Wavoids line wrapping for long names on 64‑bit ELFs; the comment is clearer as “WHAT to load WHERE” on one line.-# check what are the sections of the ELF file: -readelf -S userProgram +# list the sections in the ELF file +readelf -W -S userProgram @@ -# to display the program headers, which stores the information of WHAT to load -# WHERE: -readelf -l userProgram +# display the program headers (WHAT to load WHERE) +readelf -W -l userProgram
162-167: References look useful; consider adding one authoritative spec link.Adding a canonical-addressing reference (e.g., Intel® SDM or AMD64 APM volume covering canonical addresses) would strengthen the sections on VA widths and halves.
I can add vendor-manual citations and permalinks if you want.
97-105: Clarify table semantics and tidy formatting.
- Label the 64-bit column as implemented virtual address widths.
- Correct numeric values and clean up spacing.
- Refine wording to distinguish the ISA’s theoretical limit from real implementations.
-| 32-bit processors | 64-bit processors | -|----------------- |------------------ | -| 2^32 bytes = 4GB | 2^48 bytes = 256TB | -| | 2^57 bytes = 4PB | +| 32-bit processors | 64-bit processors (implemented VA) | +|-------------------|------------------------------------| +| 2^32 bytes = 4 GB | 2^48 bytes = 256 TB | +| | 2^57 bytes = 128 PB |-As you can see, there are currently two possibilities for 64-bit processors, and both use less than 64 bits. Why don't we have 2^64 bytes on 64-bit processors? +As you can see, there are two common virtual address widths in 64-bit systems, both less than the full 64 bits. Why don’t we have 2^64 bytes of virtual address space?-Well, the explanation is related to current implementation limitations. Even though 2^64 (16 exabytes) is the theoretical limit for this architecture, 48 and 57 bits is what current implementations support for practical reasons. +Because of page-table and hardware design trade-offs, the implemented canonical virtual address widths are typically 48 or 57 bits. The ISA’s theoretical 2^64 (16 exabytes) virtual address space isn’t fully implemented in mainstream CPUs.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
public/post-images/more-about-program-memory/lscpu-001.pngis excluded by!**/*.pngpublic/post-images/more-about-program-memory/pmap-001.pngis excluded by!**/*.png
📒 Files selected for processing (1)
posts/2025-08-09-more-about-program-memory.md(1 hunks)
🧰 Additional context used
🪛 LanguageTool
posts/2025-08-09-more-about-program-memory.md
[grammar] ~38-~38: There might be a mistake here.
Context: ...mance benefits: - Cheaper mode switch - Cheaper context switch (kernel stays map...
(QB_NEW_EN)
[grammar] ~39-~39: There might be a mistake here.
Context: ...per context switch (kernel stays mapped) - Less trashing TLB (Translation Lookaside...
(QB_NEW_EN)
[style] ~76-~76: Consider a more concise word here.
Context: ...g syscalls is not a cheap operation, so in order to improve a program's performance we can ...
(IN_ORDER_TO_PREMIUM)
[grammar] ~98-~98: There might be a mistake here.
Context: ...|----------------- |------------------ | | 2^32 bytes = 4GB | 2^48 bytes = 256TB...
(QB_NEW_EN)
[grammar] ~99-~99: There might be a mistake here.
Context: ...2^32 bytes = 4GB | 2^48 bytes = 256TB | | | 2^57 bytes = 4PB ...
(QB_NEW_EN)
[style] ~140-~140: In contexts where ‘if’ is followed by ‘or’, using ‘whether’ may be more appropriate (and formal).
Context: ...re's an additional setting that defines if the memory address can be shared with o...
(IF_WHETHER)
[grammar] ~166-~166: There might be a mistake here.
Context: ...s://landley.net/writing/memory-faq.txt).
(QB_NEW_EN)
🪛 markdownlint-cli2 (0.17.2)
posts/2025-08-09-more-about-program-memory.md
44-44: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
| On Linux using 64-bit processors and x86-64 architecture, by default the memory is split 50% for the kernel space and 50% for the user space. This is configurable using some flags. | ||
|
|
||
| And why we map both the kernel and the user program memory into a single memory space? The answer is that this brings some performance benefits: |
There was a problem hiding this comment.
💡 Verification agent
❓ Verification inconclusive
Qualify the “50/50 split” and the “configurable flags” claim.
On x86‑64 with 4‑level paging (48‑bit canonical addresses), Linux maps user space in the lower half and kernel in the upper half (effectively 50/50 of the implemented VA, not the theoretical 64 bits). With 5‑level paging (57‑bit), the split and limits differ. Calling this “configurable using some flags” is vague and may mislead; it isn’t a simple runtime toggle and depends on kernel build/CPU features.
-On Linux using 64-bit processors and x86-64 architecture, by default the memory is split 50% for the kernel space and 50% for the user space. This is configurable using some flags.
+On Linux/x86‑64, with the commonly deployed 4‑level page tables (48‑bit canonical addresses), user space occupies the lower canonical half and the kernel the upper canonical half (effectively a 50/50 split of the implemented VA width). With 5‑level paging (57‑bit), limits and layout differ. These are architecture/kernel‑configuration decisions rather than simple runtime flags.🌐 Web query:
Confirm the user/kernel virtual address split for Linux on x86-64 with 4-level and 5-level paging and whether it’s adjustable via kernel config or runtime flags.
Error during web search
Clarify x86-64 VA split and remove vague flags claim
On Linux/x86-64, the “50/50 split” refers to equal halves of the implemented virtual address space—under 4-level paging (48-bit canonical addresses), the lower 2¹⁴⁷ bytes (~128 TiB) are for user-space and the upper 2¹⁴⁷ bytes for kernel-space. With 5-level paging (57-bit), enabled via the CONFIG_X86_5LEVEL kernel configuration, the VA space grows to 2⁵⁷ bytes (~128 PiB) and is likewise split into two 2⁵⁶-byte halves (~64 PiB each). These divisions are determined by CPU architecture and build-time settings, not by simple runtime flags. (mjmwired.net, docs.kernel.org)
Please update the text accordingly (in posts/2025-08-09-more-about-program-memory.md):
-On Linux using 64-bit processors and x86-64 architecture, by default the memory is split 50% for the kernel space and 50% for the user space. This is configurable using some flags.
+On Linux/x86-64, with the standard 4-level page tables (48-bit canonical addresses), user-space occupies the lower 2¹⁴⁷ bytes (~128 TiB) and the kernel the upper 2¹⁴⁷ bytes. With 5-level paging (57-bit), enabled via CONFIG_X86_5LEVEL, the VA space extends to 2⁵⁷ bytes (~128 PiB) and is similarly divided into 2⁵⁶-byte halves (~64 PiB each). These layouts arise from the CPU architecture and kernel build-time configuration rather than simple runtime flags.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| On Linux using 64-bit processors and x86-64 architecture, by default the memory is split 50% for the kernel space and 50% for the user space. This is configurable using some flags. | |
| And why we map both the kernel and the user program memory into a single memory space? The answer is that this brings some performance benefits: | |
| On Linux/x86-64, with the standard 4-level page tables (48-bit canonical addresses), user-space occupies the lower 2¹⁴⁷ bytes (~128 TiB) and the kernel the upper 2¹⁴⁷ bytes. With 5-level paging (57-bit), enabled via CONFIG_X86_5LEVEL, the VA space extends to 2⁵⁷ bytes (~128 PiB) and is similarly divided into 2⁵⁶-byte halves (~64 PiB each). These layouts arise from the CPU architecture and kernel build-time configuration rather than simple runtime flags. | |
| And why we map both the kernel and the user program memory into a single memory space? The answer is that this brings some performance benefits: |
🤖 Prompt for AI Agents
In posts/2025-08-09-more-about-program-memory.md around lines 34 to 36, replace
the vague “50% for the kernel/user” and “some flags” wording with a precise
explanation: state that on x86-64 the default split refers to equal halves of
the implemented virtual address space (under 4-level paging/48-bit canonical
addresses the lower 2^47 bytes are user and the upper 2^47 bytes are kernel;
with 5-level paging/57-bit addresses enabled via CONFIG_X86_5LEVEL the VA space
grows accordingly to 2^57 bytes and is split into two 2^56-byte halves), and
clarify that these divisions are determined by CPU architecture and kernel
build-time configuration rather than simple runtime flags; remove the original
“some flags” claim and adjust the transition sentence leading into the
performance benefits accordingly.
| ```bash | ||
| # compile the program | ||
| clang-18 main.c -o userProgram | ||
|
|
||
| # show the syscalls performed by the process | ||
| strace ./userProgram | ||
|
|
||
| # and count the quantity of syscalls | ||
| strace ./userProgram 2>&1 | wc -l | ||
| # which gives me 37 syscalls performed | ||
| ``` |
There was a problem hiding this comment.
Count syscalls with strace’s built‑in -c rather than wc -l.
wc -l counts lines, not syscalls, and includes non‑syscall output. strace -c summarizes syscall counts and time.
-# show the syscalls performed by the process
-strace ./userProgram
-
-# and count the quantity of syscalls
-strace ./userProgram 2>&1 | wc -l
-# which gives me 37 syscalls performed
+# show the syscalls performed by the process
+strace -o /dev/stderr ./userProgram
+
+# and count/summary of syscalls
+strace -qq -c ./userProgram
+# sample output: calls, errors, total time per syscall📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ```bash | |
| # compile the program | |
| clang-18 main.c -o userProgram | |
| # show the syscalls performed by the process | |
| strace ./userProgram | |
| # and count the quantity of syscalls | |
| strace ./userProgram 2>&1 | wc -l | |
| # which gives me 37 syscalls performed | |
| ``` |
🤖 Prompt for AI Agents
In posts/2025-08-09-more-about-program-memory.md around lines 62 to 72, the
guide uses `strace ./userProgram 2>&1 | wc -l` to count syscalls which is
incorrect because wc counts lines and can include non‑syscall output; replace
that step with using strace’s built‑in summary mode (`strace -c ./userProgram`)
and update the explanatory text to say to read the syscall counts from the
strace summary output (and adjust the stated “37 syscalls” result if the summary
reports a different count).
| Notice that in this table, except from the first memory address which is 64-bit size, the others are 48-bit size, as expected. | ||
|
|
||
| Other than that, something else to keep in mind is that, in Linux, memory addresses with 48-bits, where the most significant bit is 1 (48th bit) are associated with the kernel space. If this bit is 0, then this memory address is associated with the user space. | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
❓ Verification inconclusive
Fix canonical-addressing explanation (bit numbering and zero‑padding).
- pmap prints 64‑bit zero‑padded values; user addresses “look” 64‑bit even if only 48 bits are canonical.
- On x86‑64 with 4‑level paging, bit 47 being 1 means the upper canonical half; bits 63..48 are sign‑extended to 1. Calling it “the 48th bit is 1 ⇒ kernel” is imprecise and kernel/user is a convention, not solely the bit value.
-Notice that in this table, except from the first memory address which is 64-bit size, the others are 48-bit size, as expected.
-Other than that, something else to keep in mind is that, in Linux, memory addresses with 48-bits, where the most significant bit is 1 (48th bit) are associated with the kernel space. If this bit is 0, then this memory address is associated with the user space.
+Note: pmap prints zero‑padded 64‑bit hex addresses; user addresses are still displayed as 64‑bit even though only the lower 48 bits are canonical (with 4‑level paging).
+On x86‑64 (4‑level), canonical addresses have bits 63..48 equal to bit 47. Addresses with bit 47 = 0 belong to the lower canonical half (user space on Linux), and bit 47 = 1 to the upper canonical half (where Linux maps the kernel).🌐 Web query:
Verify canonical address rules on x86-64 (sign-extension of bit 47 for 4-level, bit 56 for 5-level) from AMD64/Intel manuals or Linux memory layout docs.
Error during web search
Clarify x86-64 canonical address explanation (bit numbering and zero-padding)
File posts/2025-08-09-more-about-program-memory.md, lines 136–139
• pmap prints full 64-bit zero-padded hex addresses; user-space pointers still look 64-bit even though only the lower 48 bits are valid (4-level paging).
• On x86-64 with 4-level paging, valid “canonical” addresses must have bits 63..48 set equal to bit 47 (sign-extension).
– bit 47 = 0 ⇒ addresses 0x0000_0000_0000_0000…0x0000_7FFF_FFFF_FFFF (lower half; user space conventionally)
– bit 47 = 1 ⇒ addresses 0xFFFF_8000_0000_0000…0xFFFF_FFFF_FFFF_FFFF (upper half; kernel space conventionally)
• Referring to “the 48th bit” is imprecise—canonicality is enforced by sign-extending bit 47 into bits 48–63, and user/kernel mapping is a Linux convention, not an inherent hardware rule.
Suggested diff:
-Notice that in this table, except from the first memory address which is 64-bit size, the others are 48-bit size, as expected.
-Other than that, something else to keep in mind is that, in Linux, memory addresses with 48-bits, where the most significant bit is 1 (48th bit) are associated with the kernel space. If this bit is 0, then this memory address is associated with the user space.
+Note: pmap prints zero-padded 64-bit hex addresses; user-space pointers still appear 64-bit even though only the lower 48 bits are canonical (4-level paging).
+On x86-64 with 4-level paging, canonical addresses require bits 63..48 to replicate bit 47 (sign-extension):
+ - bit 47 = 0 ⇒ 0x0000_0000_0000_0000…0x0000_7FFF_FFFF_FFFF (lower canonical half; user space by Linux convention)
+ - bit 47 = 1 ⇒ 0xFFFF_8000_0000_0000…0xFFFF_FFFF_FFFF_FFFF (upper canonical half; kernel space by Linux convention)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Notice that in this table, except from the first memory address which is 64-bit size, the others are 48-bit size, as expected. | |
| Other than that, something else to keep in mind is that, in Linux, memory addresses with 48-bits, where the most significant bit is 1 (48th bit) are associated with the kernel space. If this bit is 0, then this memory address is associated with the user space. | |
| Note: pmap prints zero-padded 64-bit hex addresses; user-space pointers still appear 64-bit even though only the lower 48 bits are canonical (4-level paging). | |
| On x86-64 with 4-level paging, canonical addresses require bits 63..48 to replicate bit 47 (sign-extension): | |
| - bit 47 = 0 ⇒ 0x0000_0000_0000_0000…0x0000_7FFF_FFFF_FFFF (lower canonical half; user space by Linux convention) | |
| - bit 47 = 1 ⇒ 0xFFFF_8000_0000_0000…0xFFFF_FFFF_FFFF_FFFF (upper canonical half; kernel space by Linux convention) |
🤖 Prompt for AI Agents
In posts/2025-08-09-more-about-program-memory.md around lines 136–139, update
the paragraph to correctly describe x86-64 canonical addresses and pmap output:
explain that pmap prints full 64-bit zero‑padded hex addresses so user pointers
appear 64-bit though only the low 48 bits (4-level paging) are meaningful;
replace the imprecise “48th bit” wording with the canonicality rule that bits
63..48 must equal bit 47 (sign‑extension), and show the two ranges: bit47=0 →
0x0000_0000_0000_0000..0x0000_7FFF_FFFF_FFFF (lower half, user space convention)
and bit47=1 → 0xFFFF_8000_0000_0000..0xFFFF_FFFF_FFFF_FFFF (upper half, kernel
space convention); also note that the user/kernel division is a Linux convention
rather than a hardware rule and that canonicality is enforced by sign‑extension
of bit47 into bits48–63.
Summary by CodeRabbit
New Features
Documentation