Wednesday, October 12, 2022

GTC Open October Part II

 10A - Урвууг тоолох

f(n, k)-гаар k урвуу хостой n сэлгэмэлийн тоог тэмдэглэе.

1-ээр эхэлсэн сэлгэмэлийн k урвуу хостой сэлгэмэлийн тоо f(n-1, k). Яагаад?

Харин 1 гэсэн тоо сэлгэмэлийн эхнээсээ 2 дахь байрлалд орвол f(n-1, k-1) байна. Яагаад гэхээр 2 дахь байрлалд орох болгондоо хос үүсгэнэ гэсэн үг.

За үүнтэй ижил 1 гэсэн тоо 3, 4.. гээд байрлаад явбал бодлогын хариу яаж нэмэгдэх вэ гэдэгийг хараарай. Тэгэхээр ерөнхий тохиолдолд:

f(n, k) = sum(f(n-1, k-i)) for (0<=i<n) болно.

Дээрх томъёогоор бодвол бодолт O(N^2 * K) болно. Хугацааны хязгаарлалт хэтрэх нь ойлгомжтой.

Томъёогоо эмхэтгээд үзвэл f(n, k) = f(n, k-1) + f(n-1, k) - f(n-1, k-1) болж бодолт O(N*K). Санах ойгоо хэмнэх үүднээс заавал n*k*sizeof(int) авч явах хэрэг байна уу? f(n-1, k), f(n, k)-г хадгалаад явчихаж бас болно.

Бодолт


10D - Graph

Энэ бодлого бол Articulation Point-той холбоотой бодлого.

За би уг нь Таржаны алгоритмыг энэ пост дээрээ тайлбарлая гэж бодож энэ удаагийн тэмцээний анализыг 2 хэсэгтэй болгосон юм. Даанч ажил ерөөсөө болж өгөхгүй завгүй байгаа тул таржаны алгоритм тайлбарлахыг дараа болоё. Бодолтруугаа шууд ороё, уг нь их амархан.

Аль нэг оройгоос гүний нэвтрэлт хийгээд гүний нэвтрэлтийн модоо үүсгэе

1 дугаартай хүсэлтийн хувьд:

a, b, c, d гэж үзье. Мөн гүний нэвтрэлтийн модны хувьд d орой нь c оройн доор байрлаж байгаа гэж үзье. Үнэндээ бидэнд d орой хамгийн чухал. d орой articulation point биш буюу ямар нэгэн циклд агуулагдсан бол c-d ирмэгийг устгахад a->b очиж чадах нь ойлгомжтой. d орой харин articulation point бол бид a болон b орой d оройн доор хоюул оршиж байна уу эсвэл дээр хоюул оршиж байна уу гэдэгийг шалгах хэрэгтэй.

2 дугаартайг ч гэсэн графын хаана байрлаж байгаагаас шалтгаалаад тооцоолчихно, кодноос дэлгэрэнгүй хараарай. Асууж тодруулах зүйл байвал коммент үлдээгээрэй, дуртай бас шуурхай хариулах болно оо

Бодолт


Sunday, October 9, 2022

GTC Open October

 Хоёр дахь тэмцээн маань амжилтттай болж өнгөрлөө. Тэмцээнд нийт 29 хүн бүртгүүлж 13 оролцогч оролцлоо. Цаг заваа зохицуулан хэрэг гаргаж оролцсон оролцогч нартаа их баярлалаа.

Ингээд эхний 3-н байр:

1-р байранд _mrsn_ 340 оноотой

2-р байранд MazaalaiMNGL 330 оноотой

3-р байранд Turkhuu 330 оноотой 

тэмцээнийг тэргүүллээ. Дээрх оролцогч нар тэмцээний facebook хуудсаар холбогдож шагналаа авна уу.


Бодлогын анализдаа ороё.

10B - Zoom

Энэ удаагийн тэмцээний хамгийн амархан бодлого. Шууд симулац хийгээд хэвлэчихэж болно эсвэл жижигхэн мат ашиглаад шинээр үүсэх текстийн (i, j) дэх тэмдэгт нь хуучин текстийн (i/zr, j/zc)дэх тэмдэгт гэдэгийг харчихвал арай бага код бичиж болох юм.

Бодолт

10C - Count

Зөв хаалт зохион байгуулах бодлого. Иймэрхүү бодлого ихэвчлэн рекурс буюу стэк өгөгдөлийн бүтэцээр шийдэгддэг.

Шууд хүчээр шалгаад бодвол квадрат бодолт үүсэж хугацааны хязгаарлалт хэтэрсэн алдаа авна.

Эхлээд бодлогоо дараалалд байгаа бүх хүн ялгаатай өндөртэй үед бодоё.

Бодлогын өгүүлбэрээс ажиглавал А гэсэн хүн өөрөөс нь хойно зогсож байгаа хүмүүсээс хамгийн эхний өөрөөс нь эрс өндөр Б гэсэн хүнээс хойно зогсож байгаа хүмүүсийг харахгүй нь тодорхой буюу А-н хувьд Б-ээс хойно зогсох хүмүүс хамаагүй болж байгаа юм: Тэгэхээр яг ийм А Б гэсэн интервалыг дэд дараалал гэж үзье.

Хүмүүсийг зогсож буй дарааллаар нь 1, 1-ээр нь шалгаад явая. Дэд дараалалд шинээр орж ирэх хүн дандаа хамгийн хойно зогсож байгаа хүнээс намхан эсвэл ижил өндөртэй хүн байх нь ойлгомжтой. Мөн дэд дараалалд хүн шаардлага хангаад нэмж орж ирэх болгонд бодлогын хариу 1-ээр нэмэгдэнэ. Яагаад гэвэл хөрш хүмүүс бүгд бие биенээ харах учир.

Одоо дэд дараалалд маань хамгийн хойно зогсож байгаа хүнээс өндөр хүн ороод ирэхэд яах вэ? тэгвэл бид дахиад шинэ дэд дараалал үүсгэнэ. Ингэхдээ, өмнөх дэд дараалалд байгаа хүмүүсээс хамгийн хойно зогсох гэхдээ шинэ орж ирж байгаа хүнээс өндөр, тэр хүний хойно энэ шинэ хүн зогсож дахин дэд шинэ дараалал үүснэ. Тэгэхээр хэрэв дэд дараалалын хойно байгаа хүн шинэ хүнээс намхан бол хасаад явна, ингэх тоолонд бодлогын хариу бас 1-ээр нэмэгдэнэ. Яагаад гэвэл энэ шинэ хүнийг эдгээр хасагдаж байгаа хүмүүс бүгд харж чадах ёстой, яагаад гэхээд манай дэд дараалал угийн буурахаар эрэмбэлэгдсэн байгаа.

Дээрэх алгоритмыг стек ашиглаж дэд дараалалд хүн нэмэх болон хүн хасах үйлдлийг O(1)т хийж чадвал шугаман бодолт болно.

Одоо, дарааллаж зогсож буй хүмүүс ижил өндөртэй үед яах вэ? Ер нь ижил өндөртэй үед дээрх алгоритмаар бодвол яагаад болохгүй вэ? Үнэндээ дарааллаж зогсож буй хүмүүс ижил өндөртэй л биш бол бүх хүмүүсийн өндөр ялгаатай байх нь чухал биш, яагаад?

Бодолт

Part II-р дараагийн 2 бодлогын анализыг хийе

Sunday, September 11, 2022

GTC Open September

 Тэмцээнд оролцсон бүх оролцогч нартаа баяр хүргье.


Тусгайлан, эхний гурван байр болох ТөрхүүБат-ЭрдэнэБаттулга баяр хүргье. Энэ удаагийн тэмцээнд нийт 68 оролцогч бүртгүүлж 24 оролцогч бодолт илгээж оролцожээ.

Түрүүлсэн оролцогч Төрхүүгийн хувьд Дархан хот, Оюуны Ирээдүй Цогцолбор сургууль 12-р анги

2т орсон Бат-Эрдэнэ МУИС 1-р курсын оюутан,

3т орсон Баттулгын хувьд програмист залуу байх нь ээ.


Эхний тэмцээнээ ямарч байсан оролцогч нарыхаа түвшинг харах үүднээс хүнд бодлого тавилгүй явууллаа. Хурдаараа л үзэлцлээ гэсэн үг. Бодлогуудын өгүүлбэрт зарим нэг жижиг алдаа гаргасныг эс тооцвол амжилттай сайхан болж өнгөрлөө. Тэмцээнийг ивээн тэтгэсэн Гүүд Ти Си ХХК-н хамт олондоо баярлалаа.

Тэмцээн цаашдаа сар бүр болох болно. Бодлогуудыг би өөрөө бэлдэж оруулаад явна аа.


Жич: Бодлогын анализ, шагнал гардуулах уулзалт зохион байгуулах байсныгаа болиод онлайнаар бодлогын анализаа энд хийгээд байр эзэлсэн оролцогч нарын шагналыг дансаар шилжүүлэхээр боллоо. Учир нь, 1т оролцогч нараас уулзалтанд ирэх нь их хүндрэлтэй байх шиг байна, 2т ихэнх оролцогч нар 10-н жилийх байгаа тул зарим эцэг эх хүүхдээ орой, түгжрэлд явуулах боломжгүйгээ илэрхийлсэн. За тэгээд орчин үе, ихэнх зүйл зайнаас харьцдаг болж байгаагийх заавал цуглах гээд яахавдээ гэж үзлээ.

Бодлогын анализаа эхлэе

Хүрээ

Шууд симулац хийгээд зураад хүрээгээ үүсгээд явчих бодлого. Бодолт

Үлдэгдэл

42т хуваахад гарч болох хамгийн том үлдэгдэл нь 41, хамгийн бага нь 0. Тэгэхээр 42 урттай бүүл массив аваад орж ирсэн тоог хуваагаад үлдэгдэлийг нь массивын индэкс болгоод тэмдэглээд явчихаж болно. Бодолт

Тойрог

Евклидын гёометрт тойргийн талбай pi * r * r.

Евклидын бус гёометрын тойрогийг евклидын гёометрт дүрсэлбэл квадрат болж дүрслэгдэнэ. Яагаад? Тэгэхээр квадратын диагоналийн урт өгөгдсөн бол талбайг олох бодлого болох нь ээ. Бодолт

Вирус

Жингүй чиглэлгүй граф өгөгдөхөд аль нэг оройгоос түвшний нэвтрэлт хийхэд хүрч болох бүх орой хүртэлх хамгийн богино замууд олдоно. Вирус байгаа цэгүүдээс яг саяны хэлсэнчлэн түвшний нэвтрэлт хийж вирус хүрч болох өрөө бүрт хамгийн хурдандаа хэдэн минутанд очихыг нь олчихно.

Үүний дараа S оройгоос дахин нэг түвшний нэвтрэлт хийж D-д хэр хурдан очихыг нь олчихно. Ингэхдээ, түвшний нэвтрэлт хийх явцад, мэдээж дайран өнгөрөх өрөө бүрт вирусаас бага хугацаанд очих ёстой гэдэгээ шалгаад явчихна. Бодолт

180

Шууд хүчээр шалгахад хугацааны хязгаар хүрэхгүй байхаар бодож оруулсан бодлого. Даанч hackerrank-н сервер бодож байснаас хүчтэй байх шиг байна, O(N^4) бодолтууд давсан харагдана лээ.

Ер нь бол үндсэн бодолт O(N^4) зөв. Хугацаандаа багтааж зөв бодохын тулд хэд хэдэн сайжруулалт хийнэ.

Анзаарах ёстой зүйл, 180 эргүүлэхэд ижил байх матриц доторх бүх дэд матрицууд мөн 180 эргүүлэхэд ижил байх матрицууд байна. Гэхдээ эдгээр матрицууд бүгд 1 төвтэй байх ёстой шүү.

Эсрэгээрээ 180 эргүүлэхэд тэнцүү биш матриц байх юм бол энэ матрицыг агуулсан аль ч матриц 180 эргүүлэхэд тэнцүү байж чадахгүй.

Дээрх 2-г анзаараад кодоо цэвэрхэн бичвэл ер нь давчихна.

Дараагийн сайжруулалт бол аль нэг цэг, нүднээс 1, 1-ээр ихэсгэж квадрац матриц үүсгэж шалгах шалгалтыг сайжруулж болно. Санаа бол, аль нэг нүднээс матрицаа томруулахдаа 1, 1-ээр биш 64 бит тооны тоогоор масклаад шалгавал нүднүүдээ 1, 1-ээр биш 64 хэмжээтэй блокоор шалгах боломж гарна. Ийм төрлийн сайжруулалт бодлогуудад их тус болдог. Бодолтноос дэлгэрэнгүй хараарай.

За тэмцээнд оролцсон, санаа тависан мөн сонирхсон бүх хүмүүстээ баярлалаа.

Дараагийн тэмцээнийг facebook хуудсаараа зарлаад хийх болно оо. Бусдадаа түгээж, аль болох олуулаа оролцоод хөгжөөд явцгаая.

Saturday, February 6, 2021

Linking Part I

 Энэ удаагийн пост мэргэжлийн талдаа пост. Програм, код бичээд ажлын талбар дээр хоёр гурван жил болцон хүмүүст зориулж чухал ойлголтыг тайлбарлая. Үйлдлийн системийн нэгэн хэсэг болох ‘linking’ хэрхэн ажилдаг, яагаад чухал болоод мэдэх шаардлагатай вэ?

Пост 3 тусдаа постноос бүрдэнэ


  • Linking Part I

  • Linking Part II

  • E-Barimt, Монголын цахим төлбөрийн баримтын системтэй хэрхэн холболт хийх


Linking гэдэг үгийг орчуулахгүй, шууд ашиглаад явна. Мөн програмчлалын С хэлийг ашиглах болно


Linking бол үнэндээ програмыг яг ажилдаг програм болгодог үйлдэл, operating system нь үүнийг хийж өгдөг. Compiler бол тухайн програмчлалын хэл дээр бичигдсэн кодыг, source code, шалгаж, сайжруулаад тухайн компьютер, машин таниж ажиллуулж чадах үйлдлүүдийн дараалал болгоод, machine code, машин код бүтэцтэй буюу хоёртын тоололд хөрвүүлэгч нь юм, object file гэж нэрлэдэг. Харин linking нь шууд санах ой буюу RAM рүү хуулж дангаараа ажиллах чадамжтай файл буюу програм үүсгэдэг, үүнийг executable object file гэдэг. Энэ бол компьютерын програм. Windows дээр бол .exe файл. Loader гэж тухайн executable object file--г санах ойруу дамжуулж тухайн програмд өөрт нь бүх эрхийг өгч ажиллах боломжийг нь олгодог үйлдлийн системийн програм. Доод түвшиндөө ямар нэгэн програм ажиллаж байхад, үйлдлийн систем ажиллахгүй, үйлдлийн систем ажиллаж байхад ямар ч програм ажиллахгүй. Ийм л зүйл байдаг, үүнийг бүр нарийн судалж үзье гэвэл үйлдлийн системийн онолыг судлаарай. Loader--г тайлбарлахдаа “бүрэн эрхийг өгч” гэдэг үгийг ашигласан нь ийм л учиртай юм. За одоо дээрх ойлголтуудаа ашиглан арай дээр байдлаар тайлбарлая.


Linking бол хэд хэдэн код, мөн дата--нуудын хамт нийлүүлээд нэг executable object file үүсгэх үйлдэл, үүсгэдэг үйлдлийн системийн програм. Linking--г дараах үед ашиглаж болно:


  1.  програмыг компайлдаж байх үед, тайлбарлавал, source code--г machine code болгон хөврүүлэх үед linking ашиглаж өөр source code, data--г холбож, нэгтгэж болно гэсэн үг.

  2. Loader ажиллаж байх үед буюу санах ойруу хуулаад, loader ажиллаж байх үед нь мөн linking ашиглаж болно

  3. Тухайн програм ажиллаж байх үед ч хүртэл linking ашиглаж болно


Програм хангамж хөгжүүлэлт linking маш чухал үүрэг гүйцэтгэдэг. Нэг дор маш том source file бичиж байснаас хэрэгтэй, хэрэгтэй модулиар нь салгаж тус тусад нь source file үүсгэж компайлдаад linking ашиглаж үндсэн програмаа гаргаж авна. Аль нэг модульд нь шинэчлэл, засвар хийвэл тухайн модулиа компайлдаад дахин linking ашиглаж холбоод л болоо. Модулиудад хуваан хэсэгчилэх санааг компьютерын шинжлэх ухааны аль ч хэсэг, салбарт ашигладаг. Monolithic VS micro-service/kernel.


Reference гэж, програм бичихэд ашиглагдсан функцүүдийн нэр, хувьсагчуудын нэрнүүдийг хэлнэ шүү, цаашдаа шууд reference гээд явна.

Reference resolve гэж тухайн reference--ийн ард ажиллах код, утга, дата--г нь олохыг хэлнэ


Linking--г бүрэн сайн ойлгосноор


  • Том хэмжээтэй, олон модулиас бүрдсэн програмыг link хийхэд ихэвчлэн ‘missing modules’, ‘missing libraries’, ‘incompatible library versions’ гэх алдаанууд гардаг. Линк хийхэд reference--үүдийн тодорхойлогч буюу яг цаана нь байгаа утга, функц болон функцын ажиллах кодыг, хэрхэн хайж олж холбодогийг ойлгохгүй бол энэ алдаа гарсаар л байх болно.

  • Linux--н линкерүүд ямар алгоритмаар reference resolve хийдэгийг мэдэхгүйгээс аюултай алдаа, bug--ууд гардаг. Тэр нь мэдэгдэхгүй, ямар ч warning байхгүй явчихдаг. Жишээ нь ижил нэртэй reference олдох үед ямар алгоритмаар resolve хийдэгийг мэдэхгүй л бол, энэнээс болоод програм чинь шаал өөрөөр ажичихдаг, буруу код ажилцан явж байдаг. Энэ ийм ажиллах үеийн буруу ажиллагаанууд debug хийхэд маш төвөгтэй

  • За шууд эхлэе


Жишээ програм:


main.c

sum.c 

int sum(int *a, int n);

int array[2] = {1, 2};

int main() {

    int val = sum(array, 2);

    return val;

}

int sum(int *a, int n) {

    int i, s = 0;

    for (i = 0; i < n; i++) {

        s += a[i];

    }

    return s;

}


main.c дээр 2 урттай массив дата--тай үүсгээд, sum.c дээр тодорхойлогдсон sum функцыг ашиглаж байна.


Дээрх жишээ програмыг линкиг--г тайлбарлахдаа ашиглаад явна аа.

Бид компайлер гэж ярьдаг боловч үнэндээ Compiler Driver нь яг бүтэн том зурагаар нь хөрвүүлэгч програмыг хэлдэг. Компайлер нь Compiler Driver--н нэг алхам нь юм. Гэхдээ энэ тэгээд холиод л хэрэглэдэглдээ.


Compiler Driver

Ихэнх компайлерууд дараах алхамуудыг хийдэг. Preprocessor, compiler, assembler and linker. Дээрх кодуудаа одоо компайлдаж үзье.


shell>> gcc -Og -o prog main.c sum.c


За дээрх коммандын ард дараах зүйлс болж байгаа.


shell>> cpp [args] main.c /tmp/main.i


C PreProcessor ажилласан ба үүрэг нь, файл доторх include хийсэн сангууд олж авчирч, бас # directive--г тодорхойлогчуудыг тавьж main.i файлыг үүсгэдэг.


shell>> cc1 /tmp/main.i -Og [args] -o /tmp/main.s


Одоо С компайлер ажиллаж ассембли хэлрүү хөрвүүлж main.s файл үүсгэнэ. 1 ассембли мөр нь CPU 1 instructor байдаг. -О флаг бол компайлерын сайжруулах түвшин. -O1, -O2, -O3 гээд тоо нь ихсэх тусам илүү сайжруулж хурдтай програм үүсгэдэг. Гэхдээ debug хийхэд улам төвөгтэй болдог, мөн алдаа гарах магадлал улам ихсэнэ. -o флаг бол үүсэх файлыг хаана юу гэж нэрлэхэд л ашигладаг.


shel>> as [args] -o /tmp/main.o /tmp/main.s


as ассэмблер ажиллаж ассемблэр кодоос object file үүсгэдэг. Sum.c файлын хувьд яг адилан процессууд явагдаад хамгийн сүүлд манай линкер орж ирнэ.


shel>> ld -o prg  [args] /tmp/main.o /tmp/sum.o


Одоо prg гэсэн executable object file үүснэ. ./prog гээд л шууд ажилуулахад үйлдлийн системийн loader гэсэн програмыг дуудааж ажилыг хийлгэнэ.


Object File

Гурван төрлийн object file байдаг


  • Relocatable object file: Энэ нь компайлдсан хоёртын кодоороо, дата--гаа агуулсан мөн мөн өөр relocatable object file--тай хамт компайлдах үед холбож шинэ executable object file үүсгэж болох файл. Дээр жишээ дэх object file бол үнэндээ relocatable object file ёом.

  • Executable object file: Санах ойруу шууд хуулаад ажилуулж болох файл. Програм гэдэг ч одоо яг энэ шүү дээ

  • Shared object file: За энэ бол ер нь relocatable object file, гэхдээ, санах ойруу шууд хуулаад ажиллаж байх үед нь link хийгээд ашиглаж болох файл. Ажиллаж байх үед болон яг ажилуулахын өмнө буюу load хийх үед link хийж болох файл. Dynamic Link гэж хэлдэг.


Компайлер болон ассемблерууд relocatable object file үүсгэдэг. За бас Shared Object File үүсгэдэг. Linker л executable object file үүсгэдэг. 

Object Module гэж хэдэн byte--уудын дараалал, Object File гэж диск дээр байгаа, hard disk ч юмуу, object module юм. Гэхдээ мөн л энэ хоёрыг хольж сольж нэрийг нь ярьдаг.


Object File--ууд нь дотроо бас хэдэн форматын дагуу үүсгэгддэг. Хамгийн анхны хувилбар буюу Bell Lab--д хийгдсэн Unix системд a.out формат, Windows дээр Portable Executable буюу PE, Mac дээр Mach-O форматаар, linux дээр ELF буюу executable and linkable гэсэн форматаар хадгалагддаг. Энэ удаа бол ELF ашиглана, гэхдээ бусад системүүдэд зарчим нь бол яг адилхан шүү.



ELF header

.text

.rodata

.data

.bss

.symtab

.rel.text

.rel.data

.debug

.line

.strtab

Section header table



Хамгийн доор байгаа section header table--ээс дээд мөрүүдийг бүгдийг нь тус бүр нэг нэг section гээд ойлгочих. ELF header section нь 16 byte ба энэ нь 1 word size--н хэмжээ, byte ordering буюу нөгөө big ednian, little endian аль н болох, за тэгээд линкерт хэрэгтэй мэдээллүүдийг агуулна. Word size--н хэмжээг яагаад заадаг вэ гэхээр, уг нь бол компьютерт санах ойн 1 хаягт байрлах хэсэг битүүдийн уртыг илэрхийлдэг. Тэгтэл анх интел хэд cpu нь ч билээ 8 бит--г word гэж явсанаас болоод энэ анх тохирч ярьсан зүйл форматын дагуу яваагүй. Тэгээд жишээ нь CPU Register--үүд double word зэрэг үг хэллэг ашиглахаас өөр аргагүй болсон. Уг нь бол 1 word нь тухайн компьютер, машины 1 дор дамжуулж чадах битын урт буюу энэ нь санах ойн 1 хаягт агуулагдах битүүдийн урт байдаг.


За, дээрээс нь эхлээд тайлбарлаад явья


.text

Энэ нөгөө source code--ны чинь машинкод нь

.rodata

Read-only датанууд. Жишээ нь printf--н форматууд

.data

С--гийн global and static хувьсагчууд. Гэхдээ анхны утга оноосон шүү

.bss

Яг .data шиг гэхдээ анхны утга оноогоогүй нь бюу initialize хийгдээгүй нь

.symtab

Symbol table. Энэ бол тухайн энэ модульд амжилттай reference resolve хийгдсэн section. Энэ гэхдээ, функц доторх локал хувьсагчууд биш шүү. Локал хувьсагчууд stack--д хадгалагдаад, устгагдаад явдаг. -g флаг заавал шаардлагагүй, энэ table--г гаргахад

.rel.text

.text дээр манай програмын код байгаа гэж хэлсэн. Linker ажиллаад өөр object file--ууд орж ирэхэд хаяг нь шинэчлэгдэх ёстой symbol--уудын жагсаалт. Энэ ер нь бол функц дуудалт, бас global хувьсагчууд бүгд хаяг нь шинэчлэгдэх ёстой, бодоод үзээрэй. Дахиад нэг юмыг бодоод үзээрэй, энэ секшн executable object file--д хамаагүй, яагаад гэвэл, угаса update хийцэн байхгүй юу кк

.rel.data

Энэ бол тухайн энэ модулд хэрэглэж байгаа хувьсагч, функцын жагсаалт, гэхдээ энэ модульд тодорхойлогдоогүй. Модул гэхээр ер нь яг ажиллаж байгаа source file, object file--г хэлнэ.

.debug

Дэбаг, тест хийхэд зориулсан секшн. Локал хувьсагч, typedef энэ тэр энд байна. Гэхдээ компайлдахдаа -g флагтай байх ёстой

.line

Ориг анхны source file дах мөрийн дугаар болон .text секшнд байгаа коммандын тус бүрийн дугаар, харгалзуулсан. Мөн л -g флагтай байх ёстой

.strtab



Линкерийн үндсэн хийх ёстой үйлдэл нь:


  1. Symbol Resolution: Програм бичихэд хувьсагч, функцууд маш их ашиглагддаг. Хувьсагчийн нэр болон функцын нэрийг object file дотор symbol гээд байгаа юм. Тэгэхээр линк хийхэд эдгээр symbol бүрийг юу юу гэдэгийг нь учрыг олж холбох ёстой.

  2. Reolcation: Програм ажиллахад санах ой дотор байрласан байж байж л ажиллана. Програм гэдэг бол доод түвшиндээ CPU ний хийдэг хэдэн instruction буюу заавруудын дараалал л байдаг. Эдгээр заавар болгон нь санах ойн тэр хаяганд байна гэдэгийг нь линкер тодорхойлж зааж өгдөг. Компайлер болон ассемблер нь програмын эхнээс 0 гэсэн хаягнаас л хамааралтайгаар relocatable object file үүгэдэг.


Symbol, Symbol Table:


Тухайн нэг Relocated Object File--г модуль гээд байгаа. M модул гэе. Тэгэхээр М модуль бүр тухайн модульд тодорхойлогсон symbol эсвэл гаднаа өөр модульд тодорхойлогдсон гэхдээ энэ М модульд зөвхөн ашиглагдаж байгаа symbol--ууд байгаа. Эдгээр symbol--н тайлбар мэдээллүүдийг агуулсан жагсаалтыг symbol table гэнэ.


Symbol--н хувьд 3 төрлийн симбол байж болно:

  • Global Symbol: Энэ нь тухайн М модульд тодорхойлогдсон, өөр модулиуд ашиглах боломжтой symbol. С дээр static биш функцууд, мөн global хувьсагчууд юм

  • Global Symbol: Энэ нь тухайн М модульд тодорхойлогдоогүй, зүгээр л уг модульд ашиглаж байгаа symbol юм. С дээр бол external гэж нэрлэдэг.

  • Local Symbol: Тухайн М модульд тодорхойлогдсон мөн зөвхөн тухайн модуль М--дээ л ашиглагддаг symbol--г хэлнэ. С дээр бол static функц, хувьсагчууд.

Дахин анхааруулахад, локал хувьсагчууд бол програм ажиллах үед стак санах ойн зохицуулалтаар зохицуулагддаг, линкерт хамаагүй шүү.

Бас нэг сонирхолтой зүйл байгаа, локал static хувьсагчууд програмын стакаар биш энэ линкерээр зохицуулагддаг. Доорх жишээ кодыг хараарай:


m.c

Int f() {

  static int x = 0;

  return x;

}

Int g() {

  static int x = 1;

  return x;

}


Ассемблэр болон линкер дээр 2ууланд нь хоёр өөр нэртэй symbol ирнэ. x.1 x.2 ч гэх юмуу. 

.symtab секшин--д симболууд дараах бүтэцтэй жагсаалт байдлаар байдаг.


  • name нь string table доторх байршил. Энэ нь null--аас төгссөн тэмдэгт мөрлүү заана. 

  • value нь хаяг. Аль секшинд байгаагаас хамаараад тухайн секшиндэх байршил relocatble object file--н хувьд, харин executable object file байвал санах ойн хаяг. 

  • Size бол тухайн symbol--н хэмжээ. Мэдээж byte--аар хэмжигдэнэ. Type нь бол data, func, section гэх утга авах, төрөл гэсэн үг. 

  • Binding нь локал эсвэл глобал гэдэгийг илтгэнэ. 

  • Section нь аль секшинд байгааг илтгэх тоо. Секшинийг дугаарладаг, .text --г 1 гээд л эхлэнэ. 


Бүх symbol тухайн модулийхаа аль нэг секшинд харгалзах ёстой. Зохиомол гурван секшин байгаа, тэр нь:

  • ABS: Энэ нь байршилыг нь сольж болохгүй симбол

  • UNDEF: Энэ нь уг модульд ашиглагдаж байгаа, гэхдээ өөр модульд тодорхойлогдсон симбол

  • COMMON: Анхны утга оноогоогүй хувьсагч


GNU readelf: Энэ програмаар object file--уудыг уншихад зүгээр. main.o файлыг уншиж үзье.


9 дээр жишээ нь манай main функц, 1 гэсэн секшин буюу .text дотор хамгийн эхэнд буюу 0 хаяг дээр 26 бит хэмжээтэй функц симбол байх нь. Дараа нь манай массив 8 байт хэмжээтэй, int=4byte, .дата секшинд байгаа. Хамгийн доор sum. Sum нь манайд тодорхойлогдоогүй зөвхөн ашиглагдаж байгаа симбол байх нь.


Symbol Resolution:


Дээр хэлсэнчлэн линкерийн эхний хамгийн чухал үйлдэл нь энэ симболуудын учрыг олох. Хамгийн чухал дүрэм бол 1 симбол яг 1 тодорхойлогч байх ёстой. Линкерийг дуудаж ажилуулахдаа оролтын файлууд нь дан object file байх ёстой. Тэр хэдэн файлуудаасаа л тодорхойлогч болон симболуудыг хооронд нь холбоно. Локал симболын хувьд бол амархан, компайлер учрыг нь олцон аваад ирнэ.

Харин глобал симбол л хамгийн төвөгтэй нь.


Хэрэвээ линкер тухайн модульд тодорхойлогдоогүй симбол олвол өөр модулиудад байгаа гэж үзээд линкертээ энэнийхээ учрыг ол гээд symbol table--д оруулаад орхидог. Хэрвээ линкер бусад модуль дотроос альнаас нь ч тохирох тодорхойлогч олохгүй бол учир битүүлэг алдааны мэссэж өгөөд зогсдог. Жишээ нь:




Symbol resolution--г төвөгтэй болгодог бас нэг тохиолдол нь тухайн симболд тохирсон тодорхойлогч хэд хэд олдох. Жишээ нь 2 өөр модульд 1 ижил нэртэй глобал функцууд байж болно.


foo.c

faa.c

int main() {

  return 0;

}

int main() {

  return 0;

}



Яг дээрх асуудлыг linux linker яаж шийддэг вэ гэхээр, бусад нь ч гэсэн адилхан шүү. Линкер програм ажилуулахад хэд хэдэн object file--аас бүрдэнэ. Object file болгон өөрсдийн symbol table байгаа. Ассемблер нь анхны утга оноосон хувьсагч бас функцуудын симболд ‘strong’ гэсэн төрөлд оруулдаг. Харин анхны утга оноогоогүй глобал симболуудыг ‘weak’ гэсэн төрөлд оруулдаг.

Дээрх 2 төрөлийг ашиглаад linux linker дараах алгоритмаар давхар тодорхойлогч олдсон симболуудыг зохицуулдаг:

  1. Хэрэв ижил нэртэй мөн хоёулаа strong төрөлийх байвал шууд алдаа өгөө зогсооно

  2. Хэрэв ижил нэртэй мөн нэг нь strong нөгөөх нь weak байвал шууд strong симболыг сонгоно

  3. Хэрэв ижил нэртэй мөн хоёулаа weak төрөлийх бол дурын аль симболыг нь сонгоно

Жишээ:


foo.c

faa.c

int x = 1111;

int main() {

  return 0;

}

int x = 99999;

int main() {

  return 0;

}


Дээрх жишээ нь дээр х нь strong төрөлийг 2 object file--н symbol table--д хоюуланд нь агуулж байгаа учир multiple definition гэсэн алдааг заана. Харин аль нэг файлд х--н анхны утгыг өгөөгүй байсан бол энэ алдаа гарахгүй, програм шууд асуудалгүй ажиллана. Жишээ нь:


foo.c

faa.c

include <stdio.h>

void f(void);

int x = 123;

int main() {

  f();

  printf(“x = %d\n”, x);

  return 0;

}

int x;

void f() {

  x = 9999;

}


Дээрх жишээ юуг хэвлэх вэ? x = 9999 гэж хэвлэнэ.

shell>> gcc -o pp foo.c faa.c

shell>> ./pp

shell>> 9999

 WHAAT? Уг нь нээх гайхаад байх юм биш, гэхдээ main функцын зохиогч талаас бол маш аюултай. Яагаад гэвэл линкер энэ давхар тодорхойлогчийн талаар анхааруулга ч өгөлгүй ажилуулчиж байгаа биз? Дээрх дүрэм дээр бичигдсэн 2 болон 3 бол маш том асуудлын голомт байгаа юм. Бүр том шүү.


Одоо ижил симбол хоюулаа weak төрөл үед байхыг үзье.



foo.c

faa.c

#include <stdio.h>

void f(void);

int x;

int main() {

  x = 15213;

  f();

  printf(“x = %d\n”, x);

  return 0;

}

int x;

void f() {

  x = 15212;

}


Ажилуулаад үзээрэй, бүр аюултай дараах жишээ байна:



foo.c

faa.c

#include <stdio.h>

void f(void);

int y = 15212;

int x = 15213;

int main() {

  f();

  printf(“x = %d, y = %y\n”, x, y);

}

double x;

void f() {

  x = -0.0;

}


shell>> gcc -o ff foo.c faa.c


shell>>./ff

shell>> x = 15213, y = -21…….


За одоо асар олон тооны object file--г линкерт оруулаад ажилуулж байна гээд төсөөлөөд үз. Тэгтэл линкер нь зүгээр л анхааруулаад эсвэл зарим тохиолдолд анхааруулга өгөхгүй шууд ажилуулна. Програм чинь шал өөрөө ажиллаад унах нь. Одоо яаж алдаагаа олох вэ? Ийм л аюултай зүйл байгаан. Linker ажилуулахдаа -fno-common гэх флаг ашиглавал олон тодорхойлогч олдвол зогсоо гэсэн флаг бас байгаа. -Werror флаг нь болохоор бүх warning--уудыг алдаа болгоод зогсоо гэсэн флаг.


За энэ удаагийн постоо энэ хүрээд өндөрлөе. Дараагийн постоороо library хэрхэн холбох вэ, ажиллаж байх үед хэрхэн library холбох гээд тэрний дараагийн постоор ebarimt--н сангуудтэй хэрхэн ажиллах талаар оруулаад энэ бүлэг пост дуусах юм.