CPU dəyişənləri, tipləri və ya bölmələri anlamır. O, yalnız "X ünvanını oxu" və ya "Y ünvanına yaz" kimi xam əmrləri icra edir. O, yalnız yaddaş ünvanlarını başa düşür.
Dəyişən elan etdikdə, əslində saxlama yeri tələb edirsiniz. Kompilyator onu məntiqi bölməyə (məsələn, .data və ya .bss) təyin edir, Bağlayıcı (Linker) isə Bağlayıcı Skriptinizdə müəyyən edilmiş qaydalara əsasən son fiziki ünvanı hesablayır.
Bu xəritələməni başa düşmürsünüzsə, yaddaş pozulmasının və performans darboğazlarının əsl səbəblərinə korsunuz. Gömülü sistemlərdə səhv yaddaşa yerləşdirilmiş düzgün məntiq yenə də sıradan çıxmış sistemdir.
Mündəricat
- C Kodundan Binariyə: Yaddaş Yerləşdirməsini Kim Müəyyənləşdirir
- FLASH Yaddaş Strukturu (Qeyri-Uçucu Bölmələr)
- RAM Yaddaş Strukturu (Uçucu Bölmələr)
- Başlanğıc Kodu: main()-dən Əvvəl Görünməz Əl
- Həqiqət Cədvəli: Hara Gedir?
- Yaddaş Yerləşdirməsinin Yoxlanılması
- Yadda Saxlanılmalı Son Qaydalar
- Nəticə
C Kodundan Binariyə: Yaddaş Yerləşdirməsini Kim Müəyyənləşdirir
C kodunuz sadəcə binariyə çevrilmir. O, dörd mərhələli çevrilmədən keçir. Bu çevrilmə tamamilə qurma zamanı baş verir, binari yüklənmədən və ya CPU-da icra edilmədən çox əvvəl. Bu boru kəmərini anlamaq göstərir ki, C sintaksisi məntiqi müəyyən edir, Bağlayıcı Skripti isə yerləşməni müəyyən edir.
1. Ön-prosessor: Tez-tez unudulan ilk addım. O, #include fayllarını idarə edir və #define makrolarını genişləndirir. Yaddaş və ya məntiq haqqında fikirləşmir; sadəcə kompilyator üçün təmiz C faylı hazırlamaq üçün mətn manipulyasiyası həyata keçirir.
2. Kompilyator: Kompilyator C məntiqini Assembly təlimatlarına çevirir. Bu mərhələdə alət yer tutucularla (.data və ya .bss kimi məntiqi kateqoriyalarla) işləyir. Fiziki yaddaş yerlərini müəyyən etmir. Yaddaşın harada olduğunu bilmir. Yalnız yer tutucularla işləyir.
3. Assembler: Assembler həmin assembly təlimatlarını Maşın Koduna çevirir. O, yerdəyişdirilə bilən obyekt faylları istehsal edir. Bu fayllar binar məntiqi ehtiva edir, lakin ünvanlar hələ yerdəyişdirilə biləndir. Onlar hələ RAM və ya FLASH-da fiziki yerə bağlı deyillər.
4. Bağlayıcı (Linker): Bağlayıcı memardır. O, bütün yerdəyişdirilə bilən obyekt fayllarını götürür və hər simvola FLASH və ya RAM-da sabit, fiziki ünvan təyin etmək üçün Bağlayıcı Skriptindən (.ld) istifadə edir.
Əsas Nəticə: Siz
int x = 10;yazırsınız, lakin o10-un0x20000004ünvanında (RAM) yaşayıb-yaşamadığını və ya toqquşmaya səbəb olub-olmadığını bağlayıcı qərar verir. Yaddaş yerləşdirməsi tamamilə bağlayıcı skripti tərəfindən idarə olunur.
FLASH Yaddaş Strukturu (Qeyri-Uçucu Bölmələr)
Flash, proqramınızın bildiyi, lakin dəyişdirməyə ehtiyac duymadığı hər şey üçün daimi evdir. Məzmunu yenidən başlatmalardan və enerji itkisindən sağ çıxır.

.isr_vector (Xəritə)
FLASH-ın ən başlanğıcında yerləşir (adətən 0x00000000).
O, ilkin stek göstəricisini və Reset Handler-in və bütün Kəsmə Xidmət Proqramlarının ünvanlarını ehtiva edir. Yenidən başlatmada CPU icranı necə başlayacağını bilmək üçün əvvəlcə bu cədvəli gətirir.
.text (Təlimatlar)
Tətbiq, kitabxanalar və ISR-lər üçün kompilyasiya edilmiş maşın təlimatlarını ehtiva edir. CPU Execute-In-Place (XIP) istifadə edərək bu kodu birbaşa FLASH-dan icra edir.
.rodata (Sabitlər)
const qlobal dəyişənlər, axtarış cədvəlləri və sətir literalları kimi yalnız oxunan məlumatları saxlayır.
Niyə const RAM-a qənaət edir:
const int table[] = {1, 2, 3}; yazsanız, massiv yalnız Flash-da yaşayır. const-u unutsanız, bağlayıcı onu RAM-a məcbur edir (redaktə edə biləsiniz deyə), heç vaxt dəyişməyən məlumatlar üçün qiymətli SRAM-ı israf edir. Axtarış cədvəlləri üçün həmişə const istifadə edin.
Sətir Literal Tələsi
const char *ptr = "Hello";→ "Hello" sətri FLASH-da (.rodata) saxlanılır, lakinptrgöstəricisi RAM-da yaşayır. Təhlükəsiz və RAM-a qənaətli.char arr[] = "Hello";→ "Hello" sətri Flash-da saxlanılır və başlanğıcda RAM-a kopyalanır. (Əlavə RAM xərcləyir, dəyişiklik lazım olduqda modifikasiyaya icazə verir).
Xəbərdarlıq: Göstəricidən const-u çıxarsanız (char *ptr = "Hello";), sətir hələ də Flash-da (.rodata) yaşayır.
constilə: Kompilyator ona yazmağa çalışsanız xəta verir.constolmadan: Kompilyator yazmağa icazə verir, çünki tip sistemi artıq yalnız oxunan girişi tətbiq etmir, baxmayaraq ki, əsas yaddaş hələ də yalnız oxunandır, lakin CPU Yalnız Oxunan Flash ünvanına yazmağa çalışdıqda, sistem HARD FAULT yaradır və çökür.
Qayda: const-u silmək sətri RAM-a köçürmür. Yalnız qorumanı silir və qeyri-müəyyən davranışı mümkün edir.
FLASH-da Gizli Məlumat Bölmələri: .data və .bss
.data və .bss işləmə zamanı RAM bölmələri olsa da, FLASH onların inisializasiyasında mühüm rol oynayır. Saxlama (Flash) və icra (RAM) arasında körpünü təmsil edir.
.data İnisializasiya Edilmiş Qlobal Dəyişənlər (LMA vs VMA)
Qlobal inisializasiya edilmiş dəyişənlər (məs., int score = 100;). Bu dəyişən dəyişdirə biləsiniz deyə mütləq RAM-da yaşamalıdır. Lakin RAM enerji itkisində silinir. Bəs 100 haradan gəlir?
Bu bölmə ikili həyat yaşayır.
- Flash-da (LMA - Load Memory Address): İlkin dəyər (
100) enerji itkisindən sağ qalmaq üçün burada saxlanılır. - RAM-da (VMA - Virtual Memory Address): Başlanğıc kodu burada dəyişən üçün yer ayırır.
- Mexanizm:
main()işləməzdən əvvəl başlanğıc kodu dəyərləri Flash-dan (LMA) RAM-a (VMA) kopyalayır.
.bss — Sıfır-İnisializasiyalı Qlobal
.bss bölməsi inisializasiya edilməmiş və ya açıq şəkildə sıfıra təyin edilmiş qlobal və statik dəyişənləri ehtiva edir
(məs., int counter;, static int flag;).
Bu dəyişənlər üçün FLASH-da yer ayrılmır; yalnız RAM ayrılır.
Başlanğıcda işləmə zamanı main() icra edilməzdən əvvəl bütün .bss bölgəsini sıfıra təmizləyir.
Bu, sıfırları saxlamaq üçün FLASH yerinin israfından qaçır, buna görə .bss yalnız RAM istifadə edir.
RAM Yaddaş Strukturu (Uçucu Bölmələr)
RAM sistemin işçi yaddaşıdır. Bütün yazıla bilən işləmə zamanı vəziyyətini saxlayır və hər yenidən başlatmada yenidən qurulur.

.data (Aktiv Dəyişənlər)
Başlanğıc zamanı FLASH-dan kopyalanan inisializasiya edilmiş qlobal və statik dəyişənləri ehtiva edir. Bu dəyişənlər icra zamanı sərbəst oxunur və dəyişdirilir.
.bss (Sıfırlanmış Dəyişənlər)
Açıq ilkin dəyərləri olmayan qlobal və statik dəyişənləri saxlayır. Bu bütün bölgə proqnozlaşdırıla bilən davranış üçün başlanğıcda sıfıra təmizlənir.
Heap (Dinamik Yaddaş)
.bss-dən sonra başlayır- Yuxarıya doğru böyüyür
malloc()/free()tərəfindən istifadə olunur- Fraqmentasiyaya meyilli
- Sərhəd yoxlaması yoxdur
- Gömülü sistemlərdə nəzarətsiz heap istifadəsi Fraqmentasiyaya səbəb olur. Bir çox təhlükəsizlik-kritik sistemlər qeyri-sabitlikdən qaçmaq üçün heap istifadəsini məhdudlaşdırır və ya tamamilə qarşısını alır.
Stack (İcra Konteksti)
- RAM-ın yuxarısından başlayır
- RAM-ın sonundan aşağıya doğru böyüyür.
- Funksiya çağırış çərçivələrini, lokal dəyişənləri, qayıdış ünvanlarını və kəsmə kontekstini saxlayır
Stack aşağıya, Heap isə yuxarıya böyüdüyündən, onlar toqquşma kursundadırlar. Stack çox dərinə getsə (rekursiya), Heap və ya .bss dəyişənlərini səssizcə üzərinə yazacaq. Bu, "xəyal xətaları"nın 1 nömrəli səbəbidir.
Başlanğıc Kodu: main()-dən Əvvəl Görünməz Əl
Standart C kursunda sizə "icra main()-dən başlayır" öyrədilir. Mikrokontrollerdə bu yalandır.
Mikrokontrollerdə icra main()-dən başlamır.
İstifadəçi kodu işləməzdən əvvəl başlanğıc kodu icra mühitini hazırlayır:
- Stek Göstəricisi İnisializasiyası: Vektor cədvəlindən Əsas Stek Göstəricisini (MSP) yükləyir. Bu olmadan funksiyalar çağırıla bilməz.
.dataKopyalama: İlkin dəyərləri Flash-dan RAM-a kopyalayır. Bu uğursuz olarsa, dəyişənlər zibil dəyərlərlə başlayır..bssSıfırlama: Bütün.bssbölgəsi RAM-da sıfıra təmizlənir.- Sistem İnisializasiyası: Saat və aşağı səviyyəli hardware konfiqurasiyası həyata keçirilir.
main()-ə Keçid: Yalnız yaddaş hazırlandıqdan sonra icra tətbiqə daxil olur.
Bu addımlardan hər hansı biri uğursuz olarsa, dəyişənlər zibil ehtiva edir, stek yaddaşı pozur və uğursuzluqlar əsl səbəblə əlaqəsiz görünür.
Həqiqət Cədvəli: Hara Gedir?
Dəyişənlərinizin harada yerləşəcəyini proqnozlaşdırmaq üçün qısa bir istinad bələdçisi.
| Dəyişən Elanı | Seqment | Niyə? |
|---|---|---|
int x; (Qlobal) |
.bss | İlkin dəyər yoxdur. Başlanğıc kodu tərəfindən sıfırlanır. |
int x = 10; (Qlobal) |
.data | Sıfırdan fərqli ilkin dəyər lazımdır. Flash-dan kopyalanır. |
const int x = 10; (Qlobal) |
.rodata | Yalnız oxunan. Flash-da qalır. |
static int x = 5; (Lokal) |
.data | static "həmişəlik qal" deməkdir. Stack-da yaşaya bilməz. |
int x = 5; (Lokal) |
Stack | Müvəqqəti. Yalnız funksiya işləyərkən mövcuddur. |
char *s = "Text"; |
.rodata | Sətir Flash-dadır; Göstərici RAM-dadır. |
char s[] = "Text"; |
Stack | Massiv Stack-dadır; Sətir ona kopyalanır. |
malloc(10) |
Heap | Proqramçı tərəfindən əl ilə tələb edilir. |
Yaddaş Yerləşdirməsinin Yoxlanılması
Yaddaş strukturunu anlamaq yoxlanılmadıqca mənasızdır. Gömülü sistemlər fərziyyələrə tolerant deyil. Nəzəriyyəni mühəndisliyə çevirmək üçün bu alətlərdən istifadə edin.
Addım 1: Dəyişənləri yaddaşın hər bölməsinə məcbur etmək üçün bu minimal kod parçasından istifadə edin.
#include <stdio.h>
#include <stdlib.h>
int var_bss; // İnisializasiya edilməmiş -> .bss
int var_data = 42; // İnisializasiya edilmiş -> .data
const int var_rodata = 100; // Yalnız oxunan -> .rodata (Flash)
void memory_map_test() {
int var_stack = 5; // Lokal -> Stack
static int var_static = 10; // Statik Lokal -> .data
int *var_heap = malloc(4); // Dinamik -> Heap
printf("Code (.text): %p\n", memory_map_test);
free(var_heap);
}
int main() {
memory_map_test();
return 0;
}
Addım 2: Yüksək Səviyyəli İz (size)
Ümumi istehlakı görmək üçün size <filename.exe> əmrini icra edin.
size test.exe
text data bss dec hex filename
14696 1560 116 16372 3ff4 test.exe
Addım 3: Məhkəmə Araşdırması (nm)
Hər dəyişənin hansı bölməni tutduğunu sübut etmək üçün nm-dən istifadə edin.
Bu əmri icra edin
nm test.exe | grep var_
nm test.exe | grep var_
00407070 B _var_bss
00404004 D _var_data
00405064 R _var_rodata
0040400 8 d _var_static.2277
- T = Text (Flash)
- R = Read-only (Flash)
- D = Data (RAM)
- B = BSS (RAM)
Addım 4: Əsl Həqiqət (Map Faylı)
IDE-nizdə bağlayıcı map faylını (-Wl,-Map=output.map) aktiv edin. Bu, hər simvolu və onun fiziki ünvanını göstərən son sənəddir. Simvollarınızın toqquşmadığını və .ld skriptinizdə müəyyən edilmiş düzgün yaddaş sərhədləri daxilində yerləşdirildiyini yoxlamaq üçün istifadə edin.
Yadda Saxlanılmalı Son Qaydalar
- CPU yalnız ünvanları başa düşür
- Bağlayıcı yaddaş yerləşdirməsini qərar verir
.dataFLASH + RAM xərcləyir.bssyalnız RAM xərcləyir- Stack daşqınları səssizdir
- Həmişə alətlərlə yaddaşı yoxlayın
Nəticə
Ya siz yaddaşı idarə edirsiniz, ya da yaddaş sizi idarə edir. Kompilyatordan Bağlayıcıya qədər boru kəmərini anlayaraq və alətlərlə strukturunuzu yoxlayaraq, C proqramçısından Gömülü Mühəndisə çevrilirsiniz.