> Linux Çekirdeğinin Derlenmesi : Bu bölümde Linux kaynak kodlarının derlenmesi ve sistemin yeni çeekirdekle açılması üzerinde duracağız. Çekirdek kodlarının derlenmesi tüm Linux sistemleri için aynı biçimde yapılmaktadır. Ancak sistemin yeni çekirdekle açılması kullanılan "boot loader" programın ayarlarıyla ilgilidir. Bugün masaüstü bilgisayarlarında ağırlıklı olarak GRUB isimli boot lader kullanılmaktadır. Biz burada bu nedenle sürecin GRUB'ta nasıl yürütüleceğini ele alacağız. Gömülü sistemlerde ise ağırlıklı olarak U-Boot denilen boot loader kullanılmaktadır. Biz kursumuzda U-Boot hakkında bir açıklama yapmayacağız. İşletim sistemlerinin çekirdeklerini belleğe yükleyip kontrolün çekirdek kodlarına bırakılmasını sağlayan araçlara "önyükleyici (boot loader)" denilmektedir. Microsoft Windows sistemlerinde kendi önyükleyici programını kullanmaktadır. Buna "Windows Boot Manager" ya da kısaca "bootmgr" de denilmektedir. UNIX/Linux dünyasında çeşitli önyükleyici programlar kullanılmıştır. Halen en yaygın kullanılan önyükleyici program "grub" isimli programdır. Tabii "grub" aynı zamanda Windows işletim sistemini de yükleyebilmektedir. Grub önyükleyicisinden önce Linux sistemlerinde uzun bir süre "lilo" isimli önyükleyici kullanılmıştır. Gömülü sistemlerde de çeşitli önyükleyiciler kullanılabilmektedir. Bazı gömülü sistemlerde o gömülü sistemi üreten kurum tarafından oluşturulmuş olan önyükleyiciler kullanılmaktadır. Ancak gömülü sistemlerde en çok kullanılan önyükleyici "U-Boot" isimli önyükleyicidir. Nasıl C'deki main fonksiyonuna komut satırı argümanları geçiriliyorsa işletim sistem sistemi çekirdeklerine de çeşitli biçimlerde parametreler geçirilebilmektedir. Böylece çekirdek belli bir konfigürasyonla işlev görecek biçimde başlatılabilmektedir. Linux çekirdeğini önyükleyici yüklediğine göre çekirdek parametreleri de önyükleyici tarafından çekirdeğe aktarılmaktadır. Linux'ta bu parametreler "grub" önyükleyicisinin başvurduğu dosyalarda belirtilmektedir. Grub önyükleyicisinin kullanımı biraz ayrıntılıdır. Ancak biz burada grub işlemlerini daha basit ve görsel biçimde yapabilmek için "grub-customizer" isimli bir programdan faydalanacağız. Bu programı Debian türevi sistemlerde aşağıdaki gibi yükleyebilirsiniz: $ sudo add-apt-repository ppa:danielrichter2007/grub-customizer $ sudo apt-get update $ sudo apt-get install grub-customizer Pekiyi nedne işletim sistemini yüklemek için ayrı bir programa gereksinim duyulmuştur? Eskiden işletim sistemleri doğrudan BIOS kodları tarafındna yüklenebiliyordu. Ancak zamanla işletim sistemleri parametreler alacak biçimde geliştirildi. Önyükleyiciler birden fazla çekirdeğin bulunduğu durumlarda basit ayarlarla sistem yöneticisinin istediği çekirdekle boot işlemini yapabilmektedir. Diskte birden fazla işletim sisteminin bulunduğu durumlarda sistemin istenilen bir işletim sistemi tarafından boot edilmesini sağlayabilmektedir. Örneğin makinemizde hem Windows hem de Linux aynı anda bulunuyor olabilir. Önyükleyicimiz bize bir menü çıkartıp hangi işletim sistemi ile boot işlemini yapmak istediğimizi sorabilir. Eskiden basit boot prosedürleri zamanla daha karmaşık hale gelmiştir. Önyükleyici programlara gereksinim duyulmaya başlanmıştır. Pekiyi nedenleri biz Linux çekirdeğini kaynak kodlardan yeniden derlemek isteyebiliriz? İşte bunun tipik nedenleri şunlar olabilir: -> Bazı çekirdek modüllerinin ve aygıt sürücülerin çekirdek imajından çıkartılması ve dolayısıyla çekirdeğin küçültülmesi için. -> Yeni birtakım modüllerin ve aygıt sürücülerin çekirdek imajına eklenmesi için. -> Çekirdeğe tamamen başka özelliklerin eklenmesi için. -> Çekirdek üzerinde çekirdek parametreleriyle sağlanamayacak bazı konfigürasyon değişikliklerinin yapılabilmesi için. -> Çekirdek kodlarında yapılan değişikliklerin etkin hale getirilmesi için. -> Çekirdeğe yama yapılması için. -> Yeni çıkan çekirdek kodlarının kullanılabilir hale getirilmesi için. Çekirdeğin derlenmesi için öncelikle çekirdek kaynak kodlarının derleme yapılacak bilgisayara indirilmesi gerekir. Pek çok dağıtım default durumda çekirdeğin kaynak kodlarını kurulum sırasında makineye çekmemektedir. Çekirdek kodları "kernel.org" sitesinde bulundurulmaktadır. Tarayıcdan "kernel.org" sitesine girilip "pub/linux/kernel" dizinine geçildiğinde tüm yayınlanmış çekirdek kodlarını göreceksiniz. İndirmeyi tarayıcıdan doğrudan yapabilirsiniz. Eğer indirmeyi komut satırından "wget" programıyla yapmak istiyorsanız aşağıdaki URL'yi kullanabilirsiniz: https://cdn.kernel.org/pub/linux/kernel/v[MAJOR_VERSION].x/linux-[VERSION].tar.xz Buradaki MAJOR_VERSION "3", "4", "5" gibi tek bir sayıyı belirtmektedir. VERSION ise çekirdek büyük ve küçük numaralarını belirtmektedir. Örneğin biz çekirdeğin 5.15.12 versiyonunu şöyle indirebiliriz: $ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.12.tar.xz Örneğin çekirdeğin 6.8.1 versiyonunu da şöyle indirebiliriz: $ wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.8.1.tar.xz Çekirdek kodları indirildikten sonra onun açılması gerekir. Açma işlemi tar komutuyla aşağıdaki gibi yapılabilir: $ tar -xvJf linux-5.15.12.tar.xz Debian tabanlı sistemlerde o anda makşnede yüklü olan mevcut çekirdeğin kaynak kodlarını indirmek için aşağıdaki komutu kullanabilirisiniz: $ sudo apt-get install linux-source Burada yükleme "/usr/src" dizinine yapılacaktır. Linux kaynak kodlarının verisyonlanması eskiden daha farklıydı. Çekirdeğin 2.6 versionundan sonra versiyon numaralandırma sistemi değiştirilmiştir. Eskiden (2.6 ve öncesinde) versiyon numaraları çok yavaş ilerletiliyordu. 2.6 sonrasındaki yeni versiyonlamada versiyon numaraları daha hızlı ilerletilmeye başlanmıştır. Bugün kullanılan Linux versiyonları nokta ile ayrılmış üç sayıdan oluşmaktadır: Majör.Minör.Patch-Extra (-rcX, -stable, -custom, -generic) Buradaki "majör numara" büyük ilerlemeleri "minör numara" ise küçük ilerlemeleri belirtmektedir. Eskiden (2.6 ve öncesinde) tek sayı olan minör numaralar "geliştirme versiyonlarını (ya da beta versiyonlarını)", çift olanlar ise stabil hale getirilmiş dağıtılan versiyonları belirtiyordu. Ancak 2.6 sonrasında artık tek ve çift minör numaralar arasında böyle bir farklılık kalmamıştır. Patch numarası birtakım böceklerin ya da çok küçük yeniliklerin çekirdeğe dahil edildiği versiyonları belirtmektedir. Bu bağlamda minör numaralardan daha küçük bir ilerlemenin söz konusu olduğunu anlatmaktadır. Burada Extra ile temsil edilen alanda "rcX (X burada bir sayı belirtir) "stable", "custom", "generic", "realtime" gibi sözcükler de bulunmaktadır. "rc" harfleri "release candidate" sözcüklerin kısaltmadır. Satabil sürümün öncesindeki son geliştirme sürümlerini belirtmektedir. "stable" sözcüğü dağıtılan kararlı sürümü belirtir. Eğer sistem programcısı çekirdekte kendisi birtakım değişiklikler yapmışsa genellikle bunun sonuna "custom" sözcüğünü getirir. Tabii bu "custom" sözcüğünü ayrıca "-" biçiminde numaralar da izleyebilir. Buradaki numaralar sistem programcısının kendi özelleştirmesine ilişkin numaralardır. "generic" sözcüğü ise genel kullanım için yapılandırılmış bir çekirdek olduğunu belirtmektedir. "realtime" yapılandırmanın gerçek zamanlı sistem özelliği kazandırmak için yapıldığını belirtmektedir. "generic" ve "realtime" sözcüklerinin öncesinde "-N-" biçiminde bir sayı da bulunabilmektedir. Bu sayı "dağıtıma özgü yama ya da derleme numarasını belirtmektedir. Çalışmakta olan Linux sistemi hakkında bilgiler "uname -a" komutu ile elde edilebilir. Örneğin: $ uname -a inux kaan-virtual-machine 5.15.0-91-generic #101-Ubuntu SMP Tue Nov 14 13:30:08 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux Bu bilgi içerisindeki çekirdek versiyonu "uname -r" ile elde edilebilir: $ uname -r 5.15.0-91-generic Buradan biz çekirdeğin "5.15.0" sürümünün kullanıldığını anlıyoruz. Burada genel yapılandırılmış bir çekirdek söz konusudur. 91 sayısı dağıtıma özgü yama ya da derleme numarasını belirtir. Aslında "uname" komutu bu bilgileri "/proc" dosya sisteminin içerisinde almaktadır. Örneğin: $ cat /proc/version Linux version 5.15.0-91-generic (buildd@lcy02-amd64-045) (gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #101-Ubuntu SMP Tue Nov 14 13:30:08 UTC 2023 Çekirdeğin derlenmesi için zaten çekirdek kodlarında bir "build sistemi" oluşturulmuştur. Buna "KConfig sistemi" ya da "KBuild sistemi" denilmektedir. Biz önce çekirdek derleme işleminin hangi adımlardan geçilerek yapılacağını göreceğiz. Sonra çekirdeğin önemli konfigürasyon parametreleri üzerinde biraz duracağız. Sonra da çekirdekte bazı değişiklikler yapıp değiştirilmiş çekirdeği kullanacağız. Linux'ta çekirdeğin davranışını değiştirmek için farklı olanaklara sahip 5 yöntem kullanılabilmektedir: -> Çekirdeğin boot parametreleri yoluyla davranışının değiştirilmesi. Bunun için çekirdeğin yeniden derlenmesi gerekmez. -> Kernel mode aygıt sürücüsü yazmak yoluyla çekirdeğin davranışının değiştirilmesi. Bunun çekirdek kodlarının yeniden derlenmesi gerekmez. -> Çekirdeğin konfigürasyon parametrelerinin değiştirilmesiyle davranışının değiştirilmesi. Bunun için çekirdeğin yeniden derlenmesi gerekir. -> Çekirdeğin kodlarının değiştirilmesiyle davranışının değiştirilmesi. Bunun için de çekirdeğin yeniden derlenmesi gerekir. -> Çekirdeğin bazı özellikleri "proc" dosya sistemindeki bazı dosyalara birtakım değerler yazarak da değiştirilebilmektedir. Aslında bu tür değişiklikler "systemd" init sisteminde "systemctl" komutuyla da yapılabilmektedir. Örneğin sistem çalışırken bir prosesin açabileceği dosya sayısını "proc" dosya sistemi yoluyla şöyle değiştirebiliriz: $ echo 2048 | sudo tee /proc/sys/fs/file-max Linux'ta çekirdek derlemesi tipik olarak aşağıdaki aşamalardan geçilerek gerçekleştirilmektedir: >> Derleme öncesinde derlemenin yapılacağı makinede bazı programların yüklenmiş olması gerekmektedir. Gerekebilecek tipik programlar aşağıda verilmiştir: $ sudo apt update $ sudo apt install build-essential libncurses-dev bison flex libssl-dev wget gcc-arm-linux-gnueabihf \ binutils-arm-linux-gnueabihf libelf-dev dwarves >> Çekirdek kodları indirilerek açılır. Biz bu konuyu yukarıda ele almıştık. İndirmeyi şöyle yapanbiliriz: $ wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.9.2.tar.xz Bu işlemden sonra "linux-6.9.2.tar.xz" isimli dosya indirilmiş durumdadır. Onu aşağıdaki gibi açabiliriz: $ tar -xvJf linux-6.9.2.tar.xz Bu işlemden sonra "linux-6.9.2" isminde bir dizin oluşturulacaktır. Ayıca ek bir bilgi olarak eğer Ubuntu türevi bir dağıtımda çalışıyorsanız istediğniz bir çekirdeği aşağıdaki gibi indirip kurabilirsiniz: sudo apt install linux-image-<çekirdek_sürümü> Örneğin: $ sudo apt install linux-image-5.15.0-91-generic >> Çekirdek derlenmeden önce konfigüre edilmelidir. Çekirdeğin konfigüre edilmesi birtakım çekirdek özelliklerin belirlenmesi anlamına gelmektedir. Konfigürasyon bilgileri çekirdek kaynak kod ağacının kök dizininde (örneğimizde "linux-6.9.2" dizini) ".config" ismiyle bulunmalıdır. Bu ".config" dosyası default durumda kaynak dosyaların kök dizininde bulunmamaktadır. Bunun çekirdeği derleyen kişi tarafından oluşturulması gerekmektedir. Çekirdek konfigürasyon parametreleri oldukça fazladır. Biz izleyen paragraflarda önemli çekirdek konfigürasyon parametrelerini göreceğiz. Çekirdek konfigürasyon parametreleri çok fazla olduğu için bunlar bazı genel amaçları karşılayacak biçimde default değerlerle önceden oluşturulmuş durumdadır. Bu önceden oluşturulmuş default konfigürasyon dosyaları "arch//configs" dizininin içerisinde bulunmaktadır. Örneğin Intel X86 mimarisi için bu default konfigürasyon dosyaları şöyledir: $ ls arch/x86/configs hardening.config i386_defconfig tiny.config x86_64_defconfig xen.config Burada biz 64 bit Linux sistemleri için "x86_64_defconfig" dosyasını kullanabiliriz. O halde bu dosyayı kaynak dosyaların bulunduğu dizininin kök dizinine ".config" ismiyle kopyalayabiliriz: $ cp arch/x86/configs/x86_64_defconfig .config Biz bütün işlemlerde çekirdek kaynak kodlarının kök dizininde bulunduğumuzu (current working directory) varsayacağız. Ancak burada bir noktaya dikkatinizi çekmek istiyoruz. Linux kaynak kodlarındaki default konfigürasyon dosyaları minimal biçimde konfigüre edilmiştir. Bu nedenle pek çok modül bu default konfigürasyon dosyalarında işaretlenmiş değildir. Bu default konfigürasyon dosyalarını kullanarak derleme yaptığınızda bazı çekirdek modüllerinin seçilmemiş olması nedeniyle sisteminiz açılmayabilir. Bu tür denemeleri zaten var olan konfigürasyon dosyalarını kullanarak yaparsanız daha fazla modül dosyası oluşturulabilir ancak daha az zahmet çekebilirsiniz. Linux sistemlerinde genel olarak "/boot" dizini içerisinde "configs-<çekirdek_sürümü>" ismi altında mevcut çekirdeğe ilişkin konfigürasyon dosyası bulundurulmaktadır. Burada bir noktaya dikkatinizi çekmek istiyoruz. Çekirdek kaynak kodlarındaki "arch//configs/x86_64_defconfig" dizinindeki konfigürasyon dosyası ".config" ismiyle kopyalandıktan sonra ayrıca "make menuconfig" gibi bir işlemle onun satırlarına bazı default değerlerin de eklenmesi gerekir. Bu default değerler "arch/" dizinindeki "Kconfig" dosyasından gelmektedir. Bu nedenle bu default konfigürasyon dosyalarını kaynak kök dizine ".config" ismiyle kopyaladıktan sonra aşağıda belirtildiği gibi "make menuconfig" yapmalısınız. Aslında ".config" dosyasını oluşturmanın başka alternatif yolları da vardır: -> make defconfig: Bu komut çalıştığımız sisteme uygun olan konfigürasyon dosyasını temel alarak mevcut donanım bileşenlerini de gözden geçirerek sistemin açılması için gerekli minimal bir konfigürasyon dosyasını ".config" ismiyle oluşturmaktadır. Örneğin biz 64 bit Intel sistemine ilişkin bir bilgisayarda çalışıyorsak "make defconfig" dediğimizde "arch/x86/configs/x86_64_defconfig" dosyası temel alınarak o anda çalışılmakta olan çekirdek donanımları da dikkate alınarak nispeten minimal olan bir konfigürasyon dosyası oluşturmaktadır. -> make oldconfig: Bu seçeneği kullanmak için kaynak kök dizinde bir ".config" dosyasının bulunyor olması gerekir. Ancak bu seçenek KConfig dosyasındaki ve kaynak dosya ağacındaki diğer değişiklikleri de göz önüne alarak bu eski ".config" dosyasını eğer söz konusu mimaride birtakım değişiklikler söz konusu ise o değişikliklere uyumlandırmaktadır. Yani örneğin biz eski bir ".config" dosyasını kullanıyor olabiliriz. Ancak çekirdeğin yeni versiyonlarında ek birtakım başka konfigürasyon parametreleri de eklenmiş olabilir. Bu durumda "make oldconfig" bize bu eklenenler hakkında da bazı sorular sorup bunların dikkate alınmasını sağlayacaktır. -> make _defconfig: Bu seçenek belli bir platformun default konfig dosyasını ".config" dosyası olarak save etmektedir. Örneğin biz Intel makinelerinde çalışıyor olabiliriz ancak BBB için default konfigürasyon dosyası oluşturmak isteyebiliriz. Eğer biz "make defconfig" yaparsak Intel tabanlı bulunduğumuz platform dikkate alınarak ".config" dosyası oluşturulur. Ancak biz burada örneğin "make bb.org_defconfig" komutunu uygularsak bu durumda Intel mimarisinde çalışıyor olsak da "bb.org_defconfig" konfigürasyon dosyası ".config" olarak save edilir. Tabii bu durumda biz aslında yine ilgili platformun konfigürasyon dosyasını manuel olarak ".config" biçiminde de kopyalayabiliriz. -> make modules: Bu seçenek ile yalnızca modüller derlenir. Yani bu seçenek ".config" dosyasında belirtilen aygıt sürücü dosyalarını derler ancak çekirdek derlemesi yapmaz. Yalnızca "make" işlemi zaten aynı zamanda bu işlemi de yapmaktadır. Aşağıdaki ilave konfig seçenekleri ise seyrek kullanılmaktadır: -> make allnoconfig: Tüm seçenekleri hayır (no) olarak ayarlar (minimal yapılandırma). -> make allyesconfig: Tüm seçenekleri evet (yes) olarak ayarlar (maksimum özellikler). -> make allmodconfig: Tüm aygıt sürücülerin çekirdeğin dışında modül (module) biçiminde derleneceğini belirtir. -> make localmodconfig: Sistemde o anda yüklü modüllere dayalı bir yapılandırma dosyası (".config" dosyası) oluşturur. -> make silentoldconfig: Yeni seçenekler için onları görmezden gelir ve o yeni özellikler ".config" dosyasına yansıtılmaz. -> make dtbs: Kaynak kod ağacında "/arch/platform/boot/dts" dizininideki aygıt ağacı kaynak dosyalarını derler ve "dtb" dosyalarını elde eder. Gömülü sistemlerde bu işlemin yapılması ve her çekirdek versiyonuyla o versiyonun "dtb" dosyasının kullanılması tavsiye edilir. Yukarıda da belirttiğimiz gibi aslında pek çok dağıtım o anda yüklü olan çekirdeğe ilişkin konfigürasyon dosyasını "/boot" dizini içerisinde "config-$(uname -r)" ismiyle bulundurmaktadır. Örneğin kursun yapılmakta olduğu Mint datıtımında "/boot" dizinin içeriği şöyledir: $ ls /boot config-5.15.0-91-generic grub initrd.img-5.15.0-91-generic vmlinuz efi initrd.img System.map-5.15.0-91-generic vmlinuz-5.15.0-91-generic Buradaki "config-5.15.0-91-generic" dosyası çalışmakta olduğumuz çekirdekte kullanılan konfigürasyon dosyasıdır. Benzer biçimde BBB'deki built-in eMMC içerisinde bulunan çekirdekteki "/boot" dizininin içeriği de şöyledir: SOC.sh dtbs System.map-5.10.168-ti-r71 initrd.img-5.10.168-ti-r71 uboot config-5.10.168-ti-r71 vmlinuz-5.10.168-ti-r71 Buradaki konfigürasyon dosyası da "config-5.10.168-ti-r71" biçimindedir. Eğer çalışılan sistemdeki konfigürasyon dosyasını temel alacaksanız bu dosyayı Linux kaynak kodlarının bulunduğu kök dizine ".config" ismiyle kopyalayabilirsiniz. Örneğin: $ cp /boot/config-$(uname -r) .config Fakat eski bir konfigürasyon dosyasını yemni bir öçekirdekle kullanmak için ayrıca "make oldconfig" işleminin de yapılması gerekmektedir. >> Şimdi elimizde pek çok değerin set edilmiş olduğu ".config" isimli bir konfigürasyon dosyası vardır. Artık bu konfigürasyon dosyasından hareketle yalnızca istediğimiz bazı özellikleri değiştirebiliriz. Bunun için "make menuconfig" komutunu kullanabiliriz: $ make menuconfig Bu komut ile birlikte grafik ekranda konfigürasyon seçenekleri listelenecektir. Tabii buradaki seçenekler default değerler almış durumdadır. Bunların üzerinde değişiklikler yaparak ".config" dosyasını save edebiliriz. Aslında "make menuconfig" işlemi hiç ".config" dosyası oluşturulmadan doğrudan da yapılabilmektedir. Bu durumda hangi sistemde çalışılıyorsa o sisteme özgü default config dosyası temel alınmaktadır. Biz en azından "General stup/Local version - append to kernel release" seçeneğine "-custom" gibi bir sonek girmenizi böylece yeni çekirdeğe "-custom" soneki iliştirmenizi tavsiye ederiz. ".config" dosyası elde edildiğinde çekirdek imzalamasını ortadan kaldırmak için dosyayı açıp aşağıdaki özellikleri belirtildiği gibi değiştirebilirsiniz (bunların bazıları zaten default durumda aşağıdaki gibi dde olabilir): CONFIG_SYSTEM_TRUSTED_KEYS="" CONFIG_SYSTEM_REVOCATION_KEYS="" CONFIG_SYSTEM_TRUSTED_KEYRING=n CONFIG_SECONDARY_TRUSTED_KEYRING=n CONFIG_MODULE_SIG=n CONFIG_MODULE_SIG_ALL=n CONFIG_MODULE_SIG_KEY="" Çekirdek imzalaması konusu daha ileride ele alınacaktır. Yukarıda a belirttiğimiz gibi derlenecek çekirdeklere yerel bir versiyon numarası da atanabilmektedir. Bu işlem Bu işlem "make menuconfig" menüsünde "General Setup/Local version - append custom release" seçeneği kullanılarak ya da ".config" dosyasında "CONFIG_LOCALVERSION" kullanılarak yapılabilir. Örneğin: CONFIG_LOCALVERSION="-custom" Artık çekirdek sürümüne "-custom" sonekini eklemiş olduk. >> Derleme işlemi için "make" komutu kullanılmaktadır. Örneğin: $ make Eğer derleme işleminin birden fazla CPU ile yapılmasını istiyorsanız "-j" seçeneğini komuta dahil edebilirsiniz. Çalışılan sistemdeki CPU sayısının "nproc" komutuyla elde edildiğini anımsayınız: $ make -j$(nproc) Derleme işlemi bittiğinde ürün olarak biz -> "çekirdek imajını", -> "çekirdek tarafından yüklenecek olan modül dosyalarını (aygıt sürücü dosyalarını)" -> diğer bazı dosyaları elde etmiş oluruz. Derleme işleminden sonra elde oluşturulan dosyalar ve onların yerleri şöyledir (buradaki <çekirdek_sürümü> "uname -r" ile elde edilecek değeri belirtiyor): -> Sıkıştırılmış Çekirdek Imajı: "arch//boot" dizininde "bzImage" ismiyle oluşturulmaktadır. Denemeyi yaptığımız Intel makinede dosyanın yol ifadesi "arch/x86_64/boot/bzImage" biçimindedir. -> Çekirdeğin Sıkıştırılmamış ELF İmajı: Kaynak kök dizininde "vmlinux" isminde dosya biçiminde ooluşturulur. -> Çekirdek Modülleri (Aygıt Sürücü Dosyaları): "drivers" dizininin altındaki dizinlerde ve "fs" dizininin altındaki dizinlerde ve "net" dizininin altındaki dizinlerde. Ancak "make modules_install" ile bunların hepsi belirli bir dizine çekilebilir. -> Çekirdek Sembol Tablosu: Kaynak kök dizininde "System.map" ismiyle bulunuyor. Çekirdeğin derlemesi ne kadar zaman almaktadır? Şüphesiz bu derlemenin yapıldığı makineye göre değişebilir. Ancak derleme sürecinin uzamasına yol açan en önemli etken çekirdek konfigüre edilirken çok fazla modülün seçilmesidir. Pek çok dağıtım "belki lazım olur" gerekçesiyle konfigürasyon dosyalarında pek çok modülü dahil etmektedir. Bir dağıtımın konfigürasyon dosyasını kullandığınız zaman çekirdek derlemesi uzayacaktır. Ayrıca çekirdek konfigüre edilirken çok fazla modülün dahil edilmesi modüllerin çok fazla yer kaplamasına da yol açabilmektedir. Çekirdek kodlarındaki platforma özgü default konfigürasyon dosyaları daha minimalist bir biçimde oluşturulmuş durumdadır. >> Derleme sonrasında farklı dizinlerde oluşturulmuş olan aygıt sürücü dosyalarını (modülleri) belli bir dizine kopyalamak için "make modules_install" komutu kullanılmaktadır. Bu komut seçeneksiz kullanılırsa default olarak "/lib/modules/<çekirdek_sürümü>" dizinine kopyalama yapar. Her ne kadar bu komut pek çok ".ko" uzantılı aygıt sürücü dosyasını hedef dizine kopyalıyorsa da bunların hepsi çekirdek tarafından belleğe yüklenmemektedir. Çekirdek gerektiği zaman gereken aygıt sürücüleri bu dizinden alarak yüklemektedir. Örneğin: $ sudo make modules_install Aslında "make modules_install" komutunun modül dosyalarını (aygıt sürücü dosyalarını) istediğimiz bir dizine kopyalamasını da sağlayabiliriz. Bunun için INSTALL_MOD_PATH komut satırı argümanı kullanılmaktadır. Örneğin: $ sudo INSTALL_MOD_PATH=modules make modules_install Burada aygıt sürücü dosyaları "/lib/modules/<çekirdek_sürümü>" dizinine değil bulunulan yerdeki "modules" dizinine kopyalanacaktır. Pekiyi "make modules_install" komutu yalnızca modül dosyalarını mı hedef dizine kopyalıyor? Hayır aslında bu komut modül dosyalarının kopyalanması dışında bazı dosyaları da oluşturup onları da hedef dizine kopyalamaktadır. Bu komut sırasıyla şunları yapmaktadır: -> Modül dosyalarını "/lib/modules/<çekirdek_sürümü>" dizinine kopyalar. -> "modules.dep" isimli dosyayı oluşturur ve bunu "/lib/modules/<çekirdek_sürümü>" dizinine kopyalar. -> "modules.alias" isimli dosyayı oluşturur ve bunu "/lib/modules/<çekirdek_sürümü>" dizinine kopyalar. -> "modules.order" isimli dosyayı oluşturur ve "/lib/modules/<çekirdek_sürümü>" dizinine kopyalar. -> "modules.builtin" isimli dosyayı "/lib/modules/<çekirdek_sürümü>" dizinine kopyalar. Aslında burada oluşturulan dosyaların bazıları mutlak anlamda bulunmak zorunda değildir. Ancak sistemin öngörüldüğü gibi işlev göstermesi için bu dosyaların ilgili dizinde bulunması uygundur. Bir aygıt sürücü başka bir aygıt sürücüleri de kullanıyor olabilir. Bu durumda bu aygıt sürücü yüklenirken onun kullandığı tüm sürücülerin özyinelemeli olarak yüklenmesi gerekir. İşte "modules.dep" dosyası bir aygıt sürücünün yüklenmesi için başka hangi sürücülerin yüklenmesi gerektiği bilgisini tutmaktadır. Aslında "modules.dep" bir text dosyadır. Bu text dosya" satırlardan oluşmaktadır. Satırların içeriği şöyledir: : ... Dosyanın içeriğine şöyle örnek verebiliriz: ... kernel/arch/x86/crypto/nhpoly1305-sse2.ko.zst: kernel/crypto/nhpoly1305.ko.zst kernel/lib/crypto/libpoly1305.ko.zst kernel/arch/x86/crypto/nhpoly1305-avx2.ko.zst: kernel/crypto/nhpoly1305.ko.zst kernel/lib/crypto/libpoly1305.ko.zst kernel/arch/x86/crypto/curve25519-x86_64.ko.zst: kernel/lib/crypto/libcurve25519-generic.ko.zst ... Eğer bu "modules.dep" dosyası olmazsa bu durumda "modeprob" komutu çalışmaz ve çekirdek modülleri yüklenirken eksik yükleme yapılabilir. Dolayısıyla sistem düzgün bir biçimde açılmayabilir. Eğer bu dosya elimizde yoksa ya da bir biçimde silinmişse bu dosyayı yeniden oluşturabiliriz. Bunun için "dempmod -a" komutu kullanılmaktadır. Komut doğrudan kullanıldığında o anda çekirdek sürümü için "modules.dep" dosyasını oluşturmaktadır. Örneğin: $ sudo depmod -a Ancak siz yüklü olan başka bir çekirdek sürümü için "modules.dep" dosyasını oluşturmak istiyorsanız bu durumda çekirdek sürümünü de komut satırı argümanı olarak aşağıdaki gibi komuta vermelisiniz: $ sudo depmod -a <çekirdek sürümü> Tabii depmod komutunun çalışabilmesi için "/lib/modules/<çekirdek_sürümü> dizininde modül dosyalarının bulunuyor olması gerekir. Çünkü bu komut bu dizindeki modül dosyalarını tek tek bulup ELF formatının ilgili bölümlerine bakarak modülün hangi modülleri kullandığını tespit ederek "modules.dep" dosyasını oluşturur. "modules.alias" dosyası belli bir isim ya da id ile aygıt sürücü dosyasını eşleştiren bir text dosyadır. Bu dosyanın bulunmaması bazı durumlarda sorunlara yol açmayabilir. Ancak örneğin USB port'a bir aygıt takıldığında bu aygıta ilişkin aygıt sürücünün hangisi olduğu bilgisi bu dosyada tutulmaktadır. Bu durumda bu dosyanın olmayışı aygıt sürücünün yüklenememesine neden olabilir. Dosyanın içeriği aşağıdaki formata uygun satırlardan oluşmaktadır: alias Örnek bir içerik şöyle olabilir: ... alias usb:v05ACp*d*dc*dsc*dp*ic*isc*ip*in* apple_mfi_fastcharge alias usb:v8086p0B63d*dc*dsc*dp*ic*isc*ip*in* usb_ljca alias usb:v0681p0010d*dc*dsc*dp*ic*isc*ip*in* idmouse alias usb:v0681p0005d*dc*dsc*dp*ic*isc*ip*in* idmouse alias usb:v07C0p1506d*dc*dsc*dp*ic*isc*ip*in* iowarrior alias usb:v07C0p1505d*dc*dsc*dp*ic*isc*ip*in* iowarrior ... Bu dosya bir biçimde silinirse yine "depmod" komutu ile oluşturulabilir. (Yani depmod komutu yalnızca "modules.dep" dosyasını değil bu dosyayı da oluşturmaktadır.) "modules.order" dosyası aygıt sürücü dosyalarının yüklenme sırasını barındıran bir text dosyadır. Bu dosyanın her satırında bir çekirdek aygıt ssürücüsünün dosya yol ifadesi bulunur. Daha önce yazılmış aygıt sürücüler daha sonra yazılanlardan daha önce yüklenir. Bu dosyanın olmaması genellikle bir soruna yol açmaz. Ancak modüllerin belli sırada yüklenmemesi bozukluklara da neden olabilmektedir. Bu dosyanın da silinmesi durumunda yine bu dosya da "depmod" komutuyla oluşturulabilmektedir. >> Eğer gömülü sistemler için derleme yapıyorsanız kaynak kod ağacındaki "arch//boot/dts" dizini içerisindeki aygıt ağacı kaynak dosyalarını da derlemelisiniz. Tabii elinizde zaten o versiyona özgü aygıt dosyası bulunuyor olabilir. Bu durumda bu işlemi hiç yapmayabilirsiniz. Aygıt ağacı kaynak dpsyalarını derlemek için "make dtbs" komutunu kullanabilirsiniz: $ make dtbs Derlenmiş aygıt ağacı dosyaları "arch//boot/dts" dizininde oluşturulacaktır. >> Bizim çekirdek imajını, geçici kök dosya sistemine ilişkin dosyayı ve aygıt ağacı dosyasını uygun yere yerleştirmemiz gerekir. Bu dosyalar "/boot" dizini içerisinde bulunmalıdır. Ancak aslında bu işlem de "make install" komutuyla otomatik olarak yapılabilmektedir. "make install" komutu aynı zamanda "grub" isimli bootloder programın konfigürasyon dosyalarında da güncelleme yapıp yeni çekirdeğin "grub" menüsü içerisinde görünmesini de sağlamaktadır. Komut şöyle kullanılabilir: $ sudo make install Bu komut ile sırasıyla yapılanlar şunlardır: -> Çekirdek imaj dosyası "arch//boot/bzImage" hedef "/boot" dizinine "vmlinuz-<çekirdek_sürümü>" ismiyle kopyalanır. -> "System.map" dosyası hedef "/boot" dizinine "System.map-<çekirdek_sürümü>" ismiyle kopyalanır. -> ".config" dosyası "/boot" dizinine "config-<çekirdek_sürümü>" ismiyle kopyalanır. -> "Geçici kök dosya sistemi dosyası oluşturulur ve hedef "/boot" dizinine "initrd.img-<çekirdek_sürümü>" ismiyle kopyalanır. -> Eğer "grub" boot loader kullanılıyorsa "grub" konfigürasyonu güncellenir ve "grub"" menüsüne yeni girişler eklenir. Böylece sistemin otomatik olarak yeni çekirdekle açılması sağlanır. Yukarıda da belirttiğimiz gibi derleme işlemi sonucunda elde edilmiş olan dosyaların hedef sistemde bazı dizinlerde bulunuyor olması gerekir. Bu yerleri bir kez daha belirtmek istiyoruz: -> Çekirdek Imajı ---> "/boot" dizinine -> Çekirdek Sembol Tablosu ---> "/boot" dizinine -> Modül Dosyaları ---> "/lib/modules/<çekirdek_sürümü>/kernel" dizinin altında Ancak yukarıdaki dosyalar dışında isteğe bağlı olarak aşağıdaki dosyalar da hedef sisteme konuşlandırılabilir: -> Konfigürasyon Dosyası ---> "/boot" dizini -> Geçici Kök Dosya Sistemi Dosyası ---> "/boot" dizinine -> Modüllere İlişkin Bazı Dosyalar ---> "/lib/modules/<çekirdek_sürümü>" dizinine Pekiyi yukarıda belirttiğimiz dosyalar hedef sistemdeki ilgili dizinlere hangi isimlerle kopyalanmalıdır? İşte tipik isimlendirme şöyle olmalıdır (buradaki <çekirdek_sürümü> "uname -r" komutuyla elde edilecek olan yazıdır): -> Çekirdek İmajı: "/boot/vmlinuz-<çekirdek_sürümü>". Örneğin "vmlinuz-6.9.2-custom" gibi. -> Çekirdek Sembol Tablosu: "/boot/System.map-<çekirdek_sürümü>". Örneğin "System.map-6.9.2-custom" gibi. -> Modüllere İlişkin Dosyalar: Bunlar yukarıda da belirttiğimiz gibi "/lib/modules/<çekirdek_sürümü>" dizininin içerisine kopyalanmalıdır. -> Konfigürasyon Dosyası: "/boot/config-<çekirdek_sürümü>". Örneğin "config-6.9.2-custom" gibi. -> Geçici Kök Dosya Sistemine İlişkin Dosya: "/boot/initrd.img-<çekirdek_sürümü>". Örneğin "initrd.img-6.9.2-custom" gibi. Ayrıca bazı dağıtımlarda "/boot" dizini içerisindeki; -> "vmlinuz" dosyası default olan "vmlinuz-<çekirdek_sürümü>" dosyasına, -> "inird.img" dosyası da "/boot/initrd.img-<çekirdek_sürümü>" dosyasına sembolik link yapılmış durumda olabilir. Ancak bu sembolik bağlantıları "grub" kullanmamaktadır. Aşağıda Intel sistemindeki "/boot" dizinin default içeriğini görüyorsunuz: $ ls -l total 141168 -rw-r--r-- 1 root root 261963 Kas 14 2023 config-5.15.0-91-generic drwx------ 3 root root 4096 Oca 1 1970 efi drwxr-xr-x 7 root root 4096 Ara 5 19:02 grub lrwxrwxrwx 1 root root 28 Ara 5 20:28 initrd.img -> initrd.img-5.15.0-91-generic -rw-r--r-- 1 root root 126391088 Tem 11 20:19 initrd.img-5.15.0-91-generic -rw------- 1 root root 6273869 Kas 14 2023 System.map-5.15.0-91-generic lrwxrwxrwx 1 root root 25 Ara 5 20:28 vmlinuz -> vmlinuz-5.15.0-91-generic -rw-r--r-- 1 root root 11615272 Kas 14 2023 vmlinuz-5.15.0-91-generic Pekiyi derleme sonucunda elde ettiğimiz dosyaları manuel isimlendirirken çekirdek sürüm yazısını nasıl bileceğiz? Bunun için "uname -r" komutunu kullanamayız. Çünkü bu komut bize o anda çalışmakta olan çekirdeğin sürüm yazısını verir. Biz yukarıdaki denemede Linux'un "6.9.2" sürümünü derledik. Bunun sonuna da "-custom" getirirsek sürüm yazısının "6.9.2-custom" olmasını bekleriz. Ancak bu sürüm yazısı aslında manuel olarak isim değiştirmekle oluşturulamamaktadır. Bu sürüm yazısı çekirdek imajının içerisine yazılmaktadır ve bizim bazı dosyayalara verdiğimiz isimlerin çekirdek içerisindeki bu yazıyla uyumlu olması gerekir. Default olarak "kernel.org" sitesinden indirilen kaynak kodlar derlendiğinde çekirdek sürümü "6.9.2" gibi üç haneli bir sayı olmaktadır. Yani yazının sonunda "-generic" gibi "-custom" gibi bir sonek yoktur. İşte çekirdeği derlemeden önce daha önceden de belirttiğimiz gibi ".config" dosyasında "CONFIG_LOCALVERSION" özelliğine bu sürüm numarasından sonra eklenecek bilgiyi girebilirsiniz. Örneğin: CONFIG_LOCALVERSION="-custom" Anımsayacağınız gibi bu işlem "make menuconfig" menüsünde "General Setup/Local version - append custom release" seçeneği kullanılarak da yapılabilmektedir. Biz buradaki örneğimizde bu işlemi yaparak çekirdeği derledik. Dolayısıyla bizim derlediğimiz çekirdekte çekirdek imajı içerisinde yazan sürüm ismi "6.9.2-custom" biçimindedir. Pekiyi biz bu ismi unutsaydık nasıl öğrenebilirdik. Bunun basit bir yolu sıkıştırılmamış çekirdek dosyası içerisindeki (kaynak kök dizindeki "vmlinux" dosyası) string tablosunda "Linux version" yazısını aramaktır. Örneğin: $ strings vmlinux | grep "Linux version" Linux version 6.9.2-custom (kaan@kaan-virtual-machine) (gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) # SMP PREEMPT_DYNAMIC Linux version 6.9.2-custom (kaan@kaan-virtual-machine) (gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #2 SMP PREEMPT_DYNAMIC Thu Dec 5 17:55:14 +03 2024 Buradan sürüm yazısının "6.9.2-custom" olduğu görülmektedir. O halde bizim derleme sonucunda elde ettiğimiz dosyaları manuel biçimde kopyalarken sürüm bilgisi olarak "6.9.2-custom" yazısını kullanmalıyız. Çekirdek imajının "/boot" dizinine manuel kopyalanması işlemi şöyle yapılabilir (kaynak kök dizinde bulunduğumuzu varsayıyoruz): $ sudo cp arch/x86_64/boot/bzImage /boot/vmlinuz-6.9.2-custom Konfigürasyon dosyasını da şöyle kopyalayabiliriz: $ sudo cp .config /boot/config-6.9.2-custom Tabii bizim çekirdek modüllerini de "/lib/modules/6.9.2-custom/kernel" dizinine kopyalamamız gerekir. Ayrıca bir de geçici kök dosya sistemine ilişkin dosyayı da kopyalamamız gerekir. Çekirdek modüllerinin kopyalanması biraz zahmetli bir işlemdir. Çünkü bunlar derlediğimiz çekirdekte farklı dizinlerde bulunmaktadır. Bu kopyalamanın en etkin yolu "make modules_install" komutunu kullanmaktır. Benzer biçimde çekirdek dosyalarının ve gerekli diğer dosyaların uygun yerlere kopyalanması için en etkin yöntem "make install" komutudur. Normal olarak biz "make install" yaptığımızda eğer sistemimizde "grub" önyükleyicisi varsa komut "grub" konfigürasyon dosyalarında güncellemeler yaparak sistemin yeni çekirdekle açılmasını sağlamaktadır. Ancak kullanıcı bir menü yoluyla sistemin kendi istediği çekirdekle açılmasını sağlayabilir. Grub menüsü otomatik olarak görüntülenmemektedir. Boot işlemi sırasında ESC tuşuna basılırsa menü görüntülenir. Eğer "grub" menüsünün her zaman görüntülenmesi isteniyorsa "/etc/default/grub" dosyasındaki iki satır aşağıdaki gibi değiştirilmelidir: GRUB_TIMEOUT_STYLE=menu GRUB_TIMEOUT=5 Buradaki GRUB_TIMEOUT satırı eğer menünün müdahale yapılmamışsa en fazla 5 saniye görüntüleneceğini belirtmektedir. Bu işlemden sonra "update-grub" programı da çalıştırılmalıdır: $ sudo update-grub Bu tür denemeler yapılırken "grub" menüleri bozulabilmektedir. Düzeltme işlemleri bazı konfigürasyon dosyalarının edit edilmesiyle manuel biçimde yapılabilir. Konfigürasyon dosyaları güncelleendikten sonra "update-grub" programı mutlaka çalıştırılmalıdır. Ancak eğer "grub" konfigürasyon dosyaları konusunda yeterli bilgiye sahip değilseniz "grub" işlemlerini görsel bir biçimde "grub-customizer" isimli programla da yapabilirsiniz. Bu program "debian depolarında" olmadığı için önce aşağıdaki gibi programın bulunduğu yerin "apt" kayıtlarına eklenmesi gerekmektedir: $ sudo add-apt-repository ppa:danielrichter2007/grub-customizer $ sudo apt-get update Bu işlemden sonra kurulum yapılabilir: $ sudo apt-get install grub-customizer Biz yukarıda çekirdek derleme ve yeni çekirdeği kurma sürecini maddeler halinde açıkladık. Şimdi yukarıdaki adımları özet hale getirelim: -> Çekirdek derlemesi için gerekli olan araçlar indirilir. -> Çekirdek kodları indirilir ve açılır. -> Zaten hazır olan konfigürasyon dosyası ".config" biçiminde kaynak kök dizine save edilir. -> Konfigrasyon dosyası üzerinde "make menuconfig" komutu ile değişiklikler yapılır. -> Çekirdek derlemesi "make -j$(nproc)" komutu ile gereçekleştirilir. -> Modüller ve ilgili dosyalar hedefe "sudo make modules_install" komututu ile konuşlandırılır. -> Çekirdek imajı ve ilgili dosyalar "sudo make install" komutu ile hedefe konuşlandırılır. Pekiyi yeni çekirdeği derleyip sisteme dahil ettikten sonra nasıl onu sistemden tamamen çıkartabiliriz? Tabii yapılan işlemlerin tersini yapmak gerekir. Bu işlem manuel biçimde şöyle yapılabilir: -> "/lib/modules/<çekirdek_sürümü>" dizini tamamen silinebilir. -> "/boot" dizinindeki çekirdek sürümüne ilişkin dosyalar silinmelidir. -> "/boot" dizininden çekirdek sürümüne ilişkin dosyalar silindikten sonra "update-grub" programı sudo ile çalıştırılmalıdır. Bu program "/boot" dininini inceleyip otomatik olarak ilgili girişleri "grub" menüsünden siler. Yani aslında "grub" konfigürasyon dosyaları üzerinde manuel değişiklik yapmaya gerek yoktur. "grub" işlemleri için diğer bir alternatif ise "grub-customizer" programı ile görsel silme yapmaktır. Ancak bu program "/boot" dizini içerisindeki dosyaları ve modül dosyalarını silmez. Yalnızca ilgili girişleri "grub" menüsündne çıkartmaktadır. Pekiyi biz Intel sisteminde çalışırken ARM için çekirdek derlemesini nasıl yapabiliriz? Bir platformda çalışırken başka bir platform için derleme yapılabilir. Ancak hedef platforma ilişkin ismine "araç zinciri (toolchain)" denilen bir paketin yüklenmiş olması gerekir. Araç zincirleri yalnızca derleyicilerden değil sistem programlama için gerekli olan çeşitli programları barındıran paketlerdir. Örneğin ARM platformu için çeşitli araç zincirleri bulunmaktadır. ARM platformu için en yaygın kullanılan araç zincirleri aşağıdaki bağlantıdan indirilebilir: https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads Örneğin Beaglebone Black (BBB) için Windows'ta çalışan araç zinciri bu sitede aşağıdaki bağlantıya tıklanarak indirilebilir: arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-linux-gnueabihf.zip Genel olark araç zincirleri kullanılmadan önce birkaç çevre değişkeninin set edilmesi gerekmektedir: -> CROSS_COMPILE isimli çevre değişkeni araç zincirinin öneki ile set edilmelidir. Örneğin: $ export CROSS_COMPILE=arm-none-linux-gnueabihf- -> PATH çevre değişkenine araç zincirine ilişkin "bin" dizininin eklenmesi gerekir: $ PATH=$PATH:/home/kaan/Study/UnixLinux-SysProg/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-linux-gnueabihf/bin -> ARCH çevre değişkeninin hedef platformu belirten bir yazı ile set edilmesi gerekir. ARM platformu için bu yazı "arm" biçimindedir: $ export ARCH=arm Bundan sonra çekirdeğin kaynak kodları yukarıda belirtildiği gibi derlenebilir. Burada bu işlemin ayrıntısı üzerinde durmayacağız. Şimdi de çekirdek kodlarının değiştirilip derlenmesine bir örnek verelim. Çekirdek kodlarında değişiklik yapmanın birkaç yolu olabilir: -> Çekirdek kodlarındaki bir dosya içerisinde bulunan fonksiyon kodlarında değişiklik yapılması. -> Çekirdek kodlarındaki bir dosya içerisine yeni bir fonksiyon eklenmesi. -> Çekirdek kodlarındaki bir dizin içerisine yeni bir C kaynak dosyası eklenmesi. -> Çekirdek kodlarındaki bir dizin içerisine yeni bir dizin ve bu dizinin içerisinde çok sayıda C kaynak dosyalarının eklenmesi. Bu yollardan, >> Eğer biz birinci maddedeki ve ikinci maddedeki gibi çekirdek kodlarına yeni bir dosya eklemiyorsak çekirdeğin derlenmesini sağlayan make dosyalarında bir değişiklik yapmamıza gerek yoktur. >> Ancak üçüncü ve dördüncü maddedeki gibi çekirdeğe yeni bir kaynak dosya ya da dizin ekleyeceksek bu eklemeyi yaptığımız dizindeki make dosyasında bu ekleme izleyen paragraflarda açıklayacağımız biçimde belirtilmelidir. Böylece çekirdek yeniden derlendiğinde bu dosyalar da çekirdek imajının içerisine eklenmiş olacaktır. >> Eğer kaynak kod ağacında bir dizinin altına yeni bir dizin eklemek istersek bu durumda o dizini yine ana dizine ilişkin make dosyasında belirtmemiz ve o dizinde ayrı bir Makefile oluşturmamız gerekmektedir. Pekiyi çekirdek kodlarındaki bir dosya içerisindeki bir fonksiyonda değişiklik yaptığımızda çekirdek modüllerini yeniden hedef makineye aktarmamız gerekir mi? İşte genel olarak bu tür basit değişikliklerde çekirdek modüllerinin güncellenmesi gerekmemektedir. Ancak ne olursa olsun bu durum yapılan değişikliklere de bağlıdır. Bu nedenle çekirdek modüllerinin de yeniden "make modules_install" komutu hedef makineye çekilmesi önerilir. Örneğin biz çekirdek kaynak kod ağacında "fs/open.c" içerisinde "chdir" sistem fonksiyonun aşağıdaki gibi bir satır ekleyelim: SYSCALL_DEFINE1(chdir, const char __user *, filename) { struct path path; int error; unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; printk(KERN_INFO "directory is changing...\n"); // YENİ EKLENEN KOD PARÇASI retry: error = user_path_at(AT_FDCWD, filename, lookup_flags, &path); if (error) goto out; error = path_permission(&path, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; set_fs_pwd(current->fs, &path); dput_and_out: path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } out: return error; } Bu işlemden sonra sırasıyla aşağıdakiler yapıp sisytemi yeni ekirdekle açabiliriz: make -j$(nproc) make modules_install make install Bu yeni çekirdekte ne zaman bir dizin değiştirilse bir log yazısı oluşturulmaktadır. Bu yazıları "dmesg" komutuyla görebilirsiniz. Pekiyi biz çekirdeğin kaynak kod ağacına yeni bir ".c" dosyası eklemek istersek ne yapacağız? İşte bu durumda çekirdeğin make dosyalarında bu eklemenin belirtilmesi gerekmektedir. Çekirdek kodlarında her kaynak kod dizininde ayrı bir Makefile dosyası bulunmaktadır. Programcı yeni kaynak dosyayı hangi dizine ekliyorsa o dizine ilişkin Makefile içerisine aşağıdaki gibi bir satır eklemesi gerekir: obj-y += dosya_ismi.o Böylece artık make işlem yapıldığında bu dosya da derlenip çekirdek imajına dahil edilecektir. Buradaki += operatörü obj-y isimli hedefe ekleme yapma anlamına gelmektedir. "obj" sözcüğünün yanındaki "-y" harfi ilgili dosyanın çekirdeğin bir parçası biçiminde çekirdek imajının içerisine gömüleceğini belirtmektedir. Make dosyalarının bazı satırlarında "obj-y" yerine "obj-m" de görebilirsiniz. Bu da ilgili dosyanın ayrı bir modül biçiminde derleneceği anlamına gelmektedir. Eklemeler genellikle çekirdek imajının içine yapıldığı için biz de "obj-y" kullanırız. Eğer bir dosyayı biz çekirdek imajının içine gömmek yerine ayrı bir çekirdek modülü olarak derlemek istiyorsak bu durumda dosyayı yerleştirdiğimiz dizinin "Makefile" dosyasına aşağıdaki gibi bir ekleme yaparız: obj-m += dosya_ismi.o Eğer çekirdek kaynak kodlarına tümden bir dizin eklemek istiyorsak bu durumda o dizini oluşturduğumuz dizindeki "Makefile" dosyasına aşağıdaki gibi bir ekleme yaparız: obj-y += dizin_ismi/ Burada dizin isminden sonra '/' karakterini unutmayınız. Tabii bu ekleme bir modül biçiminde de olabilirdi: obj-m += dizin_ismi/ Fakat bu ekleme yapıldıktan sonra bizim ayrıca yarattığımız dizinde "Makefile" isimli bir dosya oluşturmamız ve o dosyanın içerisinde o dizinde çekirdek kodlarına ekleyeceğimiz dosyaları belirtmemiz gerekir. Örneğin biz "drivers" dizininin altına "mydriver" isimli bir dizin oluşturup onun da içerisine "a.c" "b.c" ve "c.c" dosyalarını eklemiş olalım. Bu durumda önce "drivers" dizini içerisindeki Makefile dosyasına aşağıdaki gibi bir satır ekleriz: obj-y += mydriver/ Sonra da "mydriver" dizini içerisinde "Makefile" isimli bir dosya oluşturup bu dosyanın içerisinde de bu dizin içerisindeki dosyaları belirtiriz. Örneğin: obj-y += a.o obj-y += b.o obj-y += c.o Örneğin biz kaynak kod ağacında "drivers" dizinin altında "mydriver" isimli dizin yaratıp onun içerisine "mydrive.c" dosyasını yerleştirmek isteyelim. Sırasıyla şunları yapmamız gerekir: >> "drivers" dizini altında "mydriver" dizini yaratırız. >> "drivers" dizini içerisindeki Makefile dosyasına aşağıdaki satır ekleriz: obj-y += mydriver/ >> "drivers/mydriver" dizini içerisinde "mydriver.c" dosyasını oluştururz. Dosyanın içeriği şöyle olabilir: #include #include static int __init helloworld_init(void) { printk(KERN_INFO "Hello World...\n"); return 0; } static void __exit helloworld_exit(void) { printk(KERN_INFO "Goodbye World...\n"); } module_init(helloworld_init); module_exit(helloworld_exit); >> "drivers/mydriver" dizini içerisinde "Makefile" isimli dosya oluştururz ve içine aşağıdaki satır ekleriz: obj-y += mydriver.o >> Çekirdek kod dizinin kök dizinine gelip ve sırasıyla aşağıdaki komutları uygularız: make -j$(nproc) make modules_install make install Böylece sistem yeni çekirdekle açılabilir. Aygıt sürücünün çekirdeğe dahil edildiğini geçmiş dmesg mesajlarına bakarak aşağıdaki gibi anlayabilirssiniz: $ dmesg | grep -i "Hello World..." [ 0.949515] Hello World... Şimdi de yeni bir sistem fonksiyonunu çekirdeğe eklemek isteyelim. Linux çekirdeğinde sistem fonksiyonlarının adresleri bir fonksiyon gösterici dizisinde tutulmaktadır. Bu gösterici dizisinin her elemanı bir sistem fonksiyonun adresini içerir. O halde çekirdeğe bir sistem fonksiyonu ekleyebilmek için sistem fonksiyonunu bir dosya içerisine yazmak ve bu tabloya o fonksiyonu gösteren bir giriş eklemek gerekir. Bunun yapılış biçimi Linux'un çeşitli versiyonlarında değiştirilmiştir. Aşağıda güncel bir versiyonda bu işlemin nasıl yapıldığına ilişkin bir örnek vereceğiz: >> Sistem fonksiyonumuz "mysyscall" biçiminde isimlendirmiş olalım. Önce yine çekirdek kaynak kod ağacında uygun bir dizine yine bir dosya eklemek gerekir. Bunun için en uygun dizin "kernel" dizinidir. Bu durumda sistem fonksiyonumuzu "kernel" dizini içerisinde "mysyscall.c" ismiyle yazabiliriz: /* mysyscall.c */ #include #include #include SYSCALL_DEFINE0(mysyscall) { printk(KERN_INFO "My system call\n"); return 0; } Bundan sonra kernel dizini içerisindeki "Makefile" dosyasına aşağıdaki satırı ekleriz: obj-y += mysyscall.o >> Sistem fonksiyon tablosuna ilgili sistem fonksiyonu bir eleman olarak girilir. Sistem fonksiyon tablosu "arch//syscall/xxx.tbl" dosyasında belirtilmektedir. 64 bit Linux sistemleri için bu dosya "arch/x86/entry/syscalls/syscall_64.tbl" biçimindedir. Ekleme bu dosyanın sonuna aşağıdaki gibi yapılabilir: ...... 544 x32 io_submit compat_sys_io_submit 545 x32 execveat compat_sys_execveat 546 x32 preadv2 compat_sys_preadv64v2 547 x32 pwritev2 compat_sys_pwritev64v2 # This is the end of the legacy x32 range. Numbers 548 and above are # not special and are not to be used for x32-specific syscalls. 548 common mysyscall sys_mysyscall >> Artık çekirdek aaşağıdaki gibi derlenebilir: $ sudo make -j$(nproc) >> Çekirdek modüllerini aşağıdaki gibi install edebiliriz: $ sudo make modules_install >> Çekirdeğin kendisini de şöyle install edebiliriz: $ sudo make install Sistem fonksiyonunu çekişrdeğe yerleştirip yeni çekirdekle makinemizi açtıktan sonra fonksiyonun tesitini aşağıdaki gibi yapabiliriz: #include #include #include #define SYS_mysyscall 548 int main(void) { printf("running...\n"); syscall(SYS_mysyscall); return 0; } Bu programı derleyip çalıştırdıktan sonra "dmesg" yaptığımızda aşağıdaki gibi bir çıktı elde etmeliyiz: .... file uses a different sequence number ID, rotating. [ 144.816248] warning: `ThreadPoolForeg' uses wireless extensions which will stop working for Wi-Fi 7 hardware; use nl80211 [ 487.365691] My system call