18 Aralık 2021 Cumartesi

Linux from Scratch ve LVM İçin initrd Oluşturmak


Merhaba. Uzun zamandır elektronikle ilgili yazmak istediysem de, maalesef yine olmadı. Bugünkü konum, beni iki hafta uğraştıran bir konfigürasyon. Daha doğrusu initrd oluşturmak. Peki neden böyle bir şeye ihtiyaç duydum?

Öncelikle LVM'den başlayayım. LVM'le ilk başlarda kötü deneyimlerim olsa da, 2014'ten beri o kadar alıştım ki, şu an bence LVM'siz linux kurulumu düşünülemez. Birden çok diske yayılmış bölümlerim yok ama bence en güzel özelliği diski taşıma ve yeniden boyutlandırma konusundaki esnekliği.

Konunun diğer tarafı Linux from Scratch (LFS) projesi. LFS, kullanıcılara linux çekirdeğini, bash kabuğunu ve linux'ta temel düzeyde çalışabilmek için gerekli araçları, sıfırdan yani kaynak kodlarından nasıl derleneceğini adım adım anlatan bir proje. Elbette çalışan bir derleyiciyle başlamak gerekiyor. gcc'yi cross compiler olarak derledikten sonra, temel araçlar bununla derleniyor ve minimal bir chroot (change root) ortamı oluşturuluyor. Son adımdaysa linux çekirdeği derleniyor ve grub ile boot edilebilir duruma getiriliyor. Bu projeden, Viktor Engelmann'ın videoları sayesinde haberim oldu. Ben burada onunkinden farklı bir yol izleyeceğim. Zaten ikimiz de LFS dökümantasyonundan, (LFS terminolojisiyle "kitap"tan) kısmen farklı bir yol izliyoruz ancak yapılacaklar kitabı temel alıyor. Benim izleyeceğim bir kaç adım (B)eyond LFS kitabından olacak.

Kitap, systemd ve initd sürümü olarak ikiye ayrılıyor ama ikisi arasında sekizinci bölümdeki paketlere kadar fark yok. Bu yazıda kitabın v11.0 sürümünü temel alıp ikisini de derleyeceğim. LFS'in sayfasında "Download" ve "Read Online" olmak üzere kitabın iki farklı sürümü var. Download bölümündeki .pdf dosya daha derli toplu olsa da, .html sürümü komutları kopyalayıp yapıştırmak için daha rahat. Bu arada projede zamanın çoğu paket derlemekle geçtiği için (tüm LFS'i derlemek benim makinamda 16 saat civarında sürdü, testler hariç), tek tek komutları tekrarlamak yerine yalnız kitaptan farklı yaptığım adımlara değineceğim.

Not: Bu yazıda verdiğim bağlantılar, yazıldığı tarih itibariyle kitabın son sürümüne bağlı. Dolayısıyla, v11'den daha güncel bir sürüm çıktığında, bazı bağlantılar farklı bölümleri açacaktır.


Sistem Gereksinimleri

Kitapta 10 GB'lik bir bölümün paketleri derlemek için yeterli olduğu, ama günlük kullanım için 30 GB'lik bir bölümün gerekeceği yazıyor. Çekirdeğin çok fazla özelliğini açıp (Fedora çekirdeğini) derlerken disk kullanımı 20 GB'yi geçtiği oldu.

Ben LFS için iki diskli bir sanal makina'nın (VM) birinci diskine işletim sistemi kurup, paketleri ikinci diskte derleyeceğim. Bunun avantajı, yanlışlıkla (chroot'ta olmadığımı unutup) kendi sistemimi bozma riskinin bulunmaması. Kod derlemek, yüksek disk performansı gerektiren bir iş. Bu nedenle, en iyisi SSD'de çalışmak. VM, zaten hostunun kendi disk performansına yakın bir performans verecektir. VM'in bir avantajı da, fiziksel diskte yeni bölüm ayıracak kadar boş yer yoksa bile izole bir ortam sağlayabilmesi.

Viktor Engelmann, videolarında paketleri USB belleğe indirip derliyor. Ben bunu mantıklı bulmuyorum. Dönen diskler bile USB belleklerden daha fazla performans sağlıyor. USB'de çalışmak bu iş için verimli değil. Eğer LFS, USB'den boot edilecekse, herşey diskte derlenip grub adımından önce USB belleğe kopyalanır ve önyükleyici en sonda USB'ye yazılabilir.

Projede disk dışında bir gereksinim bulunmuyor. Elbette hızlı bir işlemci, derleme süresini kısaltacaktır ama yavaş bir işlemcide de aynı sonucu almak mümkün.

Birinci diske kurulacak işletim sistemi konusunda da bir kısıtlama yok. Ben kendimi RedHat tabanlı sistemlerle rahat hissettiğim ve uzun zamandır CentOS 8 Stream'i denemek istediğim için bunu kullanacağım.


Sanal Makina Kurulumu ve Diskleri Oluşturmak

Yazıyı gereksiz uzatmamak için kurulumun detaylarına girmeyeceğim. Sanallaştırmayı hem VBox hem vmware'de denedim. Önceki yazıda, çekirdeği vmware için derlerken fazladan iki özelliğin aktifleştirilmesi gerektiğini anlattım, buna yeri geldiğinde tekrar değinirim. 2 GB RAM'li ve 20 GB diskli bir VM oluşturdum. Kurulumda temel olarak "Minimal Install" ve ek paket olarak "Development Tools"u seçtim (yanda). Tüm ayarlar aşağıdaki görseldeki gibi. Kurulumda LVM kendiliğinden oluşturuluyor, ben elle ayarlamadım. Benim asıl ilgilendiğim LFS'teki LVM yapısı, ve bunu elle oluşturacağım. Ama önce 40 GB'lık bir disk daha eklemeliyim. VBox'ta sanal makina çalışırken disk eklenemiyor (Sonradan gelen düzeltme: aslında eklenebiliyormuş). Vmware'de diski, VM çalışırken ekleyip echo "- - -" > /sys/class/scsi_host/host2/scan komutuyla tanıtmak veya makinayı yeniden başlatmak gerekiyor. Bende disk host2'ye bağlıydı (VBox için de aynı komut uygulanır).

CentOS'ta sshd çalışır olarak geliyor. Makinanın IP'sini bulup ssh ile bağlandım. Komutları kopyalayıp terminale yapıştırmak konsola elle girmekten daha kolay. VM, NAT'la bağlıysa "port forwarding"i ayarlamak gerekiyor. Buna da eski yazıların birinde değinmiştim.

Benim LVM şablonum, diskin başında 512 MB'lik bir /boot bölümü, kalanında LVM bölümü ve LVM içinde 4'er GB'lik /var, /home bölümleriyle 2'şer GB'lik /tmp ve swap, kalan alanda da kök dizin. Kök dizin harici bölümlerde toplam kullanım 60 MB'ı aşmıyor. Haliyle 12.5 GB'lik alan boş kalıyor. Yukarıda yazdığım gibi kök dizin kullanımı 16 GB'yi bulabilir. Bu yüzden 40 GB'lık görece büyük bir disk ekledim. Kurulum bittiğinde disk dosyasının net büyüklüğü 28.8 GB'dı.

Diski aşağıdaki komutla hızlıca bölümlendirdim:

echo -ne "n\np\n1\n\n+512M\nn\np\n2\n\n\nt\n2\n8E\np\nw\n" | sudo fdisk /dev/sdb

Sonuç, komutun çıktısından kontrol edilebilir:

Device     Boot   Start      End  Sectors  Size Id Type
/dev/sdb1          2048  1050623  1048576  512M 83 Linux
/dev/sdb2       1050624 83886079 82835456 39.5G 8e Linux LVM

Sonra sırayla LVM içindeki bölümleri oluşturdum:

sudo pvcreate /dev/sdb2
sudo vgcreate vg_lfs /dev/sdb2
sudo lvcreate -n lv_var  -L 4G vg_lfs
sudo lvcreate -n lv_home -L 4G vg_lfs
sudo lvcreate -n lv_swap -L 2G vg_lfs
sudo lvcreate -n lv_tmp  -L 2G vg_lfs
sudo lvcreate -n lv_root -l100%FREE vg_lfs
sudo lvscan

Son komutla hem yeni bölümleri hem de CentOS'un bölümlerini görmeliyim. Bölümler oluşturulduktan sonra formatlanmaları gerek:

sudo mkfs.ext4 /dev/sdb1
sudo mkfs.ext4 /dev/vg_lfs/lv_root
sudo mkfs.ext4 /dev/vg_lfs/lv_tmp
sudo mkfs.ext4 /dev/vg_lfs/lv_home
sudo mkfs.ext4 /dev/vg_lfs/lv_var
sudo mkswap    /dev/vg_lfs/lv_swap

LFS kitabı, kurulumda ext4 dosya sistemi kullanıldığını varsayıyor (bölüm 2.5).


LFS Çalışma Ortamını Oluşturalım

İlk olarak bölüm 2.2'deki betiği kaydedip çalıştırdığımda, yalnızca python3 ile makeinfo bulunamadı. Python'ı sudo dnf install python3 komutuyla yükledim. Diğer pakete sonra geleceğim.

export LFS=/mnt/lfs komutuyla LFS değişkenini oluşturup, bunu .bash_profile'a da ekledim (bölüm 2.6). Elbette ilgili dizini de oluşturdum. Diskleri mount etmem gerek (bölüm 2.7) ama dört bölümü elle mount etmemek için bir betik hazırladım:

#!/bin/bash

if [[ x$LFS == "x" ]]; then
    echo '$LFS' variable is empty.
    exit 1
fi

STEP=1
for PARTITION in "/" "var" "home" "tmp"; do
    if [[ $PARTITION == "/" ]]; then
        LVMNAME="root";
    else
        LVMNAME=$PARTITION;
    fi

    echo "[ $STEP / 5 ] Mounting $LVMNAME partition"
    if [ ! -d "$LFS/$PARTITION" ]; then
        sudo mkdir -pv "$LFS/$PARTITION";
        sudo chown $USER:$GROUPS "$LFS/$PARTITION";
    fi

    sudo mount "/dev/vg_lfs/lv_$LVMNAME" $LFS/$PARTITION
    sudo chown $USER:$GROUPS "$LFS/$PARTITION";

    STEP=$((STEP+1))
done

echo "[ 5 / 5 ] Activating swap.."
sudo swapon /dev/vg_lfs/lv_swap  2> /dev/null

Betiği tek bir kullanıcı çalıştıracaksa $USER ve $GROUPS yerine kullanıcı adı ve grubu yazılabilir. Bir de son adımda swap'i aktifleştirmeye bence gerek yok.


LFS Paketleri

Paket denildiğinde .rpm veya .deb dosyaları anlaşılmasın. Bizimkiler kaynak kod paketleri. Bunları indirmeden önce, eksik bir kaç paketi daha kurmak gerek. wget, vim-enhanced ve son olarak atladığım makeinfo. Bu, texinfo paketinde geliyor ama paketin bulunduğu "powertools" reposu aktif değil. Bu yüzden şöyle yaptım:

sudo dnf install --enablerepo="powertools" texinfo wget vim-enhanced

Sonra sources dizinini oluşturup (bölüm 3.1), wget listesiyle dosyaları buraya indirdim ve tabii ki md5'lerini kontrol ettim. İndirmede bazen sorun olursa, wget'e --no-check-certificate parametresini vermek gerekiyor.

Bölüm 4.2'deki komutları çalıştırmak için root'a geçtim ama LFS değişkenini root'ta tanımlamadığımdan önce bunu tanımladım, sonra komutları çalıştırdım. VM'de olduğum için lfs kullanıcısı (4.3) oluşturmayacağım. Dizinlerin sahipliğini kendi kullanıcıma aktadım. Verilen .bashrc'yi, lfs_env.sh adıyla ev dizinime (root değil) kaydettim ve source'la yükledim. VM'i reboot edersem bunu çalıştıracağım.

Beşinci ve altıncı bölümleri olduğu gibi uyguladım. 7.2'deki ve 7.3.2'ye kadar olan komutları çalıştırdım. Bundan sonraki komutlar chroot ortamına her girişte çalışacağı ve çıkışta da mount edilen kaynakların ters sırada umount edilmeleri gerektiği için, verilen komutlardan bir betik oluşturdum:

#!/bin/bash

if [[ x$LFS == "x" ]]; then
    echo '$LFS' variable is empty.
    exit 1
fi

mount -v --bind /dev $LFS/dev
mount -v --bind /dev/pts $LFS/dev/pts
mount -vt proc proc $LFS/proc
mount -vt sysfs sysfs $LFS/sys
mount -vt tmpfs tmpfs $LFS/run

if [ -h $LFS/dev/shm ]; then
  mkdir -pv $LFS/$(readlink $LFS/dev/shm)
fi

chroot "$LFS" /usr/bin/env -i HOME=/root  TERM="$TERM"  PS1='(lfs chroot) \u:\w\$ ' PATH=/bin:/usr/bin:/sbin:/usr/sbin /bin/bash --login +h

umount -v $LFS/run
umount -v $LFS/sys
umount -v $LFS/proc
umount -v $LFS/dev/pts
umount -v $LFS/dev

chroot'dan çıkarken özellikle /dev/pts unmount edilmediğinde, sanal makinada yeni bir terminal açılamıyor ve makinayı yeniden başlatmak gerekiyor.

chroot'a girip gerekli dizin ve dosyaları oluşturmaya devam ettim. Bu arada bölüm 7.6'da /etc/passwd ve /etc/group dosyaları, systemd ile sysVinit'in farklılaştıkları ilk nokta.

Betik, chroot'dan çıkışta kaynakları unmount ettiğinden, bölüm 7.14'teki umount'lara gerek kalmadı. Ayrıca kurulumu sanal makinada yaptığımdan yedeklemeyi de snapshot'la yapabilirim. Bu nedenle bu adımlara da gerek yok.

8.25'te shadow'u cracklib desteğiyle derlerken "undefined reference to `FascistCheck'" hatasını aldım. Aşağıdaki komutla bir kere daha konfigüre edip derlediğimde sorun olmadı:

LDFLAGS=-lcrack ./configure --sysconfdir=/etc --with-libcrack --with-group-name-max-length=32

8.26'daki "make -k check" adımı çok uzun sürüyor. Öyle ki testi yatmadan önce başlattım, sabah kalktığımda hala tamamlanmamıştı. Anladığım kadarıyla 350K'dan fazla test var ve testlerin bir kısmı stres testi. Ayrıca kitapta bazı testlerin başarısız olduğunun bilindiği yazılmış. Test sonuçlarına şuradan ulaşılabilir. Bendeki test sonuçları da hemen hemen bunlarla aynıydı. Bu bölümün sonunda basit bir derleme testi var. Belki sadece o testi yapmak yeterli olabilir ama kitap "make check" adımının kesinlikle atlanmamasını istiyor.

Bölüm 8.69'da artık systemd ve sysVinit paketleri farklılaşıyor.

Son olarak 8. bölümün sonunda lfs_enter_chroot.sh betiğinde bash'a verdiğim +h parametresini kaldırıp tekrar kaydettim.

Dokuzuncu bölüm initd/systemd ayarları. Yani bu bölüm iki kitapta tamamen farklı. Bu bölümde kitaptakileri birebir uyguladım. Türkçe klavye için initd'de /etc/sysconfig/console veya systemd'de /etc/vconsole.conf dosyasına KEYMAP=trq girdim, herhangi birşey girmeyince İngilizce klavye yükleniyor. /tmp için ayrı bir bölüm ayırdığımdan systemd kitabında 9.10.3'ü atladım.

Bölüm 10.2'de fstab ayarlanacağı için bu bölüm önemli. En başta oluşturduğum bölümleri burada fstab'a girmem gerek. /boot için ayırdığım bölüm ikinci diskte olduğundan şu anda aygıtın adı /dev/sdb1. Ama LFS'i boot etmek için birinci diski sanal makinadan ayırdığım zaman, bu bölüm /dev/sda1 olacak. Bu nedenle bu adlandırmayı kullanamam. Linux'ta her diskin eşsiz ve değişmez bir UUID'i bulunur. Bu değeri kullanmalıyım ki, /boot her zaman mount edilebilsin. Bunun için ls -la /dev/disk/by-uuid çıktısında sdb1'e linkli değeri bulurum veya:

lsblk -o NAME,MAJ:MIN,RM,SIZE,RO,TYPE,MOUNTPOINT,UUID

komutuyla diskleri UUID'leriyle birlikte listelerim. lsblk çıktısı daha ayrıntılı ama nedense chroot içinde UUID sütunu boş. Bu nedenle, komutu chroot'un dışında çalıştırdım ve değeri not alıp fstab'i oluşturdum:

/dev/mapper/vg_lfs-lv_root   /       ext4   defaults  1  1
/dev/mapper/vg_lfs-lv_var    /var    ext4   defaults  0  0
/dev/mapper/vg_lfs-lv_home   /home   ext4   defaults  0  0
/dev/mapper/vg_lfs-lv_tmp    /tmp    ext4   defaults  0  0
/dev/mapper/vg_lfs-lv_swap   swap    swap   pri=1     0  0
UUID=01234567-89ab-cdef-0123-456789abcdef /boot ext4 defaults 0 0

ve elbette initd sürümü için proc, sysfs, devpts vb. girdilerin de eklenmesi gerekiyor. Bu girdiler kitapta zaten olduğundan burada yer vermedim.


Linux Kernel'ini Derlemek

Bütün paketleri hazırladıktan sonra artık iş kerneli (çekirdeği) derlemeye kaldı. Her iki kitabın 10.3 bölümü çekirdeği derlemekle ilgili. Ben öncelikle make mrproper ve ardından make defconfig komutlarıyla çekirdeğin en standart özelliklerini seçtim. Bir önceki yazıda bunları yeterince detaylı açıklamıştım. Diğer özellikleri düzenlemek için make menuconfig'i kullanacağım.

make menuconfig metin arayüzü
systemd ayarları
initd sürümü için defconfig'de fazla değişikliğe gerek yok. uevent helper'ın kapalı ve devtmpfs desteğinin açık olması yeterli. Ama systemd sürümü için biraz daha fazla seçeneği değiştirmek gerekiyor (yanda).

Systemd'nin Errata'sında da belirtiliyor, yandaki CONFIG_SECCOMP'un yeri yanlış ama sorun değil, çünkü defconfig'de bu özellik zaten açık. Bu özellik yine de menuconfig içinde aratılabilir veya .config'den bakılabilir:

(lfs chroot) root:/sources/linux-5.13.12# grep -n SECCOMP  .config
687:CONFIG_HAVE_ARCH_SECCOMP=y
688:CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
689:CONFIG_SECCOMP=y
690:CONFIG_SECCOMP_FILTER=y
691:# CONFIG_SECCOMP_CACHE_DEBUG is not set

Bu arada, bu yalnız benim kişisel görüşüm ama kendi deneyimim (irc) ve posta listesi arşivine dayanarak LFS destek kanallarındakilerin biraz ters insanlar olduklarını düşündüğüm için, bu tür hataları kendime saklamayı tercih ediyorum.

Konuya döneyim. Hedefim LFS'i LVM desteğiyle kurmaktı. LFS, en temel linux kurulumunu içeriyor, örn. pencere yöneticisi yok. Temel kurulumdan sayılmayan LVM de, (B)eyond LFS adında ayrı bir projenin parçası. BLFS kitabının LVM bölümünde, LVM için açılması gereken diğer özelliklerin listesi verilmiş. Bunları açıp modüler olanları da çekirdeğe dahil edeceğim (kişisel tercih). Bu arada LVM için Gentoo dökümanlarının ne önerdiğine bakıp, BLFS'te olmayıp Gentoo'da önerilen bir kaç özelliği daha aktifleştirdim. Eksik özelliklerden ötürü kerneli tekrar derlemektense, bir kaç KB büyük bir kernelle boot etmeyi tercih ederim.

Son olarak vmware'le çalışan okurların, bir önceki yazının ana konusunu oluşturan "Fusion MPT device support"u da aktifleştirmesi gerek. Bu, özetle vmware'deki SCSI denetleyicinin sürücüsü ve bu olmadan, çekirdek sabit diski bulamayıp boot edemiyor. Bu özellik "Device Drivers" menüsü altında bulunabilir.

Tüm bu adımları tamamladıktan sonra, LFS bölüm 10.3.1'e dönüp kerneli make'le derledim ve ardından modülleri yükledim. Benim /boot bölümü /dev/sdb1'de olduğu için mount /dev/sdb1 /boot ile boot bölümünü mount edip; vmlinuz (kernel), System.map ve config dosyalarını kopyaladım.


Önyükleyici: GRUB

Kerneli derleyip /boot'a kopyaladıktan sonra sıra GRUB'u ayarlamaya geldi. Bu aşamada iki seçeneğim var: GRUB'u /dev/sdb'ye kurabilirim (aslında başta planım buydu) veya CentOS'un GRUB'ına (/dev/sda) ekleyebilirim. Birinci seçeneğin avantajı, diskin CentOS'tan bağımsız çalışacak biçimde ayarlanıyor olması. İkincinin avantajıysa kolay olması.

Önce birinci seçenek: grub-install /dev/sdb komutunu girdim. grub.cfg dosyası kitaptakinden biraz farklı olacak:

set default=0
set timeout=5

insmod ext2
set root=(hd0,1)

menuentry "GNU/Linux, Linux 5.13.12-lfs-11.0-systemd" {
    linux   /vmlinuz-5.13.12-lfs-11.0-systemd root=/dev/vg_lfs/lv_root ro
}

set root'la ayarlanan "grub root" sıfırıncı diskin ilk bölümü. Grub açısından LFS diski, henüz sıfırıncı değil ama CentOS'un kurulduğu diski çıkarınca öyle olacak. /boot bölümü ayrı olduğundan linux satırında /boot'a gerek yok. Çekirdeğe girilen root parametresiyse, root disk bölümünün aygıt yolu (device path). Bu arada yukarıda systemd için olan ayarlar var. initd için, menuentry ve linux satırlarında "-systemd" ifadesi olmaycak.

Bunu kaydettikten sonra chroot'tan çıkıp, sanal makinayı kapattım ve birinci diski sanal makinadan çıkarttım. Makinayı tekrar açtığımda, az önce oluşturduğum grub menüsünü gördüm ve bu girdiden boot ederken kernel panic aldım. Yeey!. Tamam, her ne kadar sevinilecek bir durum olmasa da, bu iki şeyi gösteriyor: (1) grub doğru şekilde ayarlanmış, (2) kernel dosyaları düzgün derlenmiş ve /boot'a kopyalanmış.

O halde neden kernel panic aldım. Call trace'de görüldüğü gibi mount_block_root fonksiyonunda, kök dizine mount edeceği (grub'da root= ile verilen) diski bulamamış. Neden? Çünkü LVM henüz aktif olamadı. Burada yapacak birşey olmadığından çıkardığım diski geri takıp CentOS'a döndüm.

Peki her defasında LFS'i açmak için diski sökmek ve sorun olduğunda CentOS'a geri dönmek için geri takmak zorunda mıyım? Hayır! CentOS'tayken /etc/grub.d/40_custom dosyasının sonuna şu satırları ekledim:

menuentry "GNU/Linux, Linux 5.13.12-lfs-11.0-systemd" {
  set root=(hd1,1)
  linux   /vmlinuz-5.13.12-lfs-11.0-systemd root=/dev/mapper/vg_lfs-lv_root ro
}

Bu, yukarıda andığım ikinci grub seçeneği ve aslında ilk konfigürasyonun aynısı, sadece set root, menuentry içinde. Örnek yine systemd için. initd için "-systemd" kısmı olmayacak. Sonra eklediğim satırı, CentOS'un grub.cfg'sine aktardım:

GRUB_DISABLE_OS_PROBER=true  grub2-mkconfig -o /boot/grub2/grub.cfg

OS prober diğer işletim sistemlerini otomatik bulup grub'a ekleyen güzel bir özellik ama bir bug yüzünden düzgün çalışmıyor. Şimdi, işletim sistemleri arasında geçiş yapmak kolaylaştığına göre kaldığım yerden devam edebilirim.


initramfs

BLFS kitabındaki LVM bölümünü (veya systemd LVM bölümünü) açtım. About LVM'in sondan ikinci paragrafında, kök dosya sisteminde LVM kullanabilmek için initramfs gerektiği yazıyor. initramfs, temel bazı programları ve konfigürasyonları içeren sıkıştırılmış sanal bir disk. Initial RAM Filesystem'in kısaltması. Bu dosya (varsa) önyükleyici tarafından kök dizine açılıyor. İçindeki programlar ve conf dosyaları sistemin açılmaya devam etmesi için gereken işleri yapıyor. Bir çok dağıtımla gelen "rescue kernel" da aslında kabuğu içeren basit bir initramfs.

BLFS'in kendi initramfs oluşturma betiği var. LVM desteğini initramfs'e eklemek için öncelikle lvm yüklü olmalı. Bunun için de;

1) Önce LVM'in önkoşulu olan libaio
2) which sadece LVM için değil, sorun bulmada da çok işe yarayan bir araç
3) mdadm. Testleri atlanabilir, zaten kitaptaki komutla çalışmıyorlar.*
4) initramfs'i sıkıştırmak için cpio
5) LVM. Bunun da testleri uzun sürüyor ve bazıları sorunlu. LVM'i kitaptaki --with-thin* ve --with-cache* parametreleri yanısıra --with-vdo=none ile konfigüre ettim. Kritik olmasa da systemd için LVM'de fazladan bir komut var.
6) ve initramfs betiği yüklü olmalı.

* Test etmedim ama sanıyorum mdadm olmadan da bu sistem çalışabilir.

initramfs betiği iki parçadan oluşuyor. Birinci parça betiğin kendisi ve ikinci parça init.in adında, initramfs'e init adıyla kopyalanacak olan dosya. LFS v10.1'de bu betik sorunluydu (bug). initramfs'te de mutlaka bulunması gereken coreutils ve util-linux'un bileşenlerini (ls, cp, mount, umount vb.), /bin yerine /usr/bin'de aradığı için hata verip sonlanıyordu. Betiğin /usr/lib'de aradığı ama aslında /lib'de olan dosyalarda da aynı sorun vardı. Çözüm olarak dosyaları /usr/bin ve /usr/lib'de olmaları gereken yere linklemiştim. Bu bug v11'de düzeltilmiş.

Betiğe kernel sürümünü parametre olarak girdiğim için, kernel modüllerini (.ko dosyaları) de initramfs'e ekleyerek dosyayı oluşturdu.

(lfs chroot) root:~# mkinitramfs 5.13.12
Creating initrd.img-5.13.12... done.

Dosyayı /boot'a kopyaladım (bölüm bağlı olmalı) ve chroot'tan çıkmadan önce /boot/grub/grub.cfg'yi aşağıdaki gibi değiştirdim:

menuentry "GNU/Linux, Linux 5.13.12-lfs-11.0-systemd" {
  linux /vmlinuz-5.13.12-lfs-11.0-systemd root=/dev/vg_lfs/lv_root ro
  initrd  /initrd.img-5.13.12
}

CentOS'un grub'unu kullandığım için aslında yukarıda yaptığım konfigürasyon yalnız CentOS diski söküldüğü zaman çalışacak. chroot'tan çıkıp, eklediğim satırı /etc/grub.d/40_custom dosyasına da ekleyip, tekrar şu komutu çalıştırdım:

GRUB_DISABLE_OS_PROBER=true  grub2-mkconfig -o /boot/grub2/grub.cfg

Makinayı yeniden başlattım. LFS'i seçtim ve sonuç:

ve systemd'li kurulum için aynı sonuç:

systemd'li makinada çalışmayan bazı servisler olsa genel olarak ikisi de sorunsuz açılıyor.

4 Aralık 2021 Cumartesi

Linux Kernel'ini Vmware için Derlemek


Merhaba. Bu yazıda linux kernel (çekirdek) derleme hakkında genel bilgiler verip, karşılaştığım bir sorunun çözümünü ele alacağım. Bu aslında sonraki yazının bir bölümü olacaktı, ama sorun o kadar uğraştırdı ki, ayrı bir yazıda ele almaya değeceğini düşündüm. Başlık zaten sonraki yazı hakkında ipucu verecektir.


Linux'ta Kernel Derlemek

Kernel derlemek, özel bir cihazla çalışmıyorsanız (örn. gömülü (embedded) sistem veya standart kernel'ın desteklemediği çok yeni bir donanımla) veya LFS gibi deneysel veya Gentoo gibi kernel'ın derlenmiş olarak gelmediği bir dağıtım kullanmadıkça, gündelik kullanımda yapılması gereken birşey değil. Öte yandan, öğrenmek veya sisteme en düşük seviyede hakim olmak için bence bilinmesi mutlak gerekli. Zaten bir kaç tane önkoşulu yerine getirip, temelleri bildikten sonra, herhangi bir kaynak kodu derlemekten daha kolay.

Ben bu yazıda kernel v5.13.12'yi derleyeceğim. İhtiyacım olan öncelikle kaynak kod. Bu arada kernel sürümünün önemli olduğu tek yer .config dosyası, buna da ileriki paragraflarda değineceğim. Tüm kernel'lar www.kernel.org/pub adresinde linux/kernel alt dizininden ulaşılabilir. Daha küçük olduğu için yukarıda .xz dosyanın bağlantısını verdim ama aynı dosyanın .gz sürümü de bulunabilir.

Bu dosyayı indirip açtım. Elbette bir dosyayı derlemek için make, gcc ve gcc-c++ gibi olması zorunlu programlar makinamda zaten yüklüydü. Eksik olan tek paket ncurses-devel'di. Bu aslında zorunlu değil, make menuconfig için gerekli ama menuconfig, kernel özelliklerini seçerken kolaylık sağlıyor. kernel'in bağımlılıkları bunlarla sınırlı değil, v5.13 sürümü için ayrıntılı liste kernel.org dökümantasyonunda var. Ama listedeki tüm paketlerin yüklü olması zorunlu değil, örn. PPP'yle ilgili birşey derlemeyecekseniz, pppd'ye gerek yok veya OpenSSL yalnız kernel modülleri imzalanacaksa gerekli.

Derlemeye geçmeden önce bazı ön bilgiler: make clean bilindiği gibi derlenen dosyaları silip kodu baştan derlemeye hazır hale getirir. linux'a özgü make mrproper ise .config dosyasını ve diğer ayarları da siler [1]. Bu yüzden ben de başlamadan önce bu komutla dizini temizledim ve sonra make defconfig ile en standart ayarları aktifleştirdim. Sözünü ettiğim bu ayarlar .config dosyasında tutuluyor, dolayısıyla defconfig aslında standart .config dosyası oluşturuyor. Bu ayarlar bir metin düzenleyiciyle görülebilir veya make menuconfig komutuyla, ncurses TUI kullanarak düzenlenebilir. .config dosyasında 4845 satır olduğundan tüm ayarları bu dosyadan elle yapmak hiç mantıklı değil.

.config dosyası her kernel sürümü için farklıdır, çünkü her minör sürümde bir kaç özellik eklenir veya çıkarılır. Haliyle, o sürüme uyumlu olmayan bir .config dosyası kullanarak derleyemem. Ama daha eski bir kernel'a ait .config dosyasını, yeni kernel'a make oldconfig komutuyla uyarlayabilirim. Bu komut, kullanıcıya yalnız .config'de olmayan (o kernel'a yeni eklenmiş) seçenekleri sorar. Tabi ki eski .config derken, v2.6 config'ini v5.13'e uyarlamak pek mantıklı iş değil.

make menuconfig metin arayüzü

make menuconfig komutunu çalıştırdım ve yukarıdaki menü çıktı. Ana menüdeki "--->" işaretli satırlar bir alt menüyü gösteriyor. Bu menüdeki tüm satırlar (eğer bir alt menüsü yoksa) .config'deki bir satıra karşılık geliyor. Kernel belgelerinde, açılması gereken özellikler yanında [*] ve açılmaması gerekenler yanında [ ] işaretiyle veriliyor. [M] ise özelliğin modüler olduğu anlamına geliyor, bunu daha sonra açıklayacağım. Örn. aşağıdaki görselde LFS'i systemd ile çalıştırmak için gereken kernel seçenekleri verilmiş:

LFS systemd kernel seçenekleri


Örneğin "Auditing Support", "Control Group support" ve "Configure standard kernel ..." gibi özellikler "General setup" altında; "open by fhandle syscalls" ise "Configure standard kernel ..."ın altında. Satır sonunda kareli parantez içinde verilen adlar, özelliğin .config içindeki maddesi (item). Bu arada, "Processor type and features" altında olması gereken "Enable seccomp ..."u bulamadım (kaynak hatalı). Bu özellik, Kernelconfig.io'ya göre v5.13'te "General architecture-dependent options" altında, ama eski sürümlerde mesela v5.9.9'da "Processor type and features" altında. Bu maddeler, menuconfig'deyken '/'a basıp, adlarıyla aratılabilir. Ancak ben menuconfig'den değil, .config dosyasından da bu ayarı değiştirebilirim (aşağıda). Bu arada özelliklerin genelde (her zaman değil) ilk harfi, aynı zamanda onun klavye kısa yolu, yani "Processor type..."a gidip "E" ye basarak, "E" ile başlayıp o an ekranda görünen seçenekler arasında gezebilirim.

$ grep -n SECCOMP .config
687:CONFIG_HAVE_ARCH_SECCOMP=y
688:CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
689:CONFIG_SECCOMP=y
690:CONFIG_SECCOMP_FILTER=y
691:# CONFIG_SECCOMP_CACHE_DEBUG is not set

Bu değer 689. satırda zaten açılmış. Gerekirse 'y'yi 'n' yapıp kapabilirim. Tüm bu özellikler .config dosyasında var. Sözünü ettiğim [M] içinse aşağıda bir örnek var:

BLFS: LVM için kernel seçenekleri


Bazı kernel seçeneklerinde bulunan M, özelliğin kernel modülü olarak derlenmesi anlamına geliyor. Bütün kernel fonksiyonları, /boot dizininde derlenmiş durumda bulunan vmlinuz-... adlı kernel dosyasında bulunmak zorunda değildir. Bu dosyayı olabildiğince küçük tutmak için, bazı fonksiyonlar modül olarak /usr/lib/modules altında tutulur, ve yalnız ihtiyaç olursa yüklenir. Bu sayede, örn. LVM diski olmayan bir makinada LVM modüllerinin veya ses kartı olmayan bir sanal makinada ses kartına ait modüllerin boşuna bellekte tutulması gerekmez.

Modül dosyaları .ko (kernel object) uzantılı dosyalardır. insmod veya modprobe ile yüklenir, lsmod'la listelenirler. Modüller, kernel'a sonradan eklenecek işlevlerin, kernel'ı tekrar derlemeye gerek kalmadan kullanılmasını sağlar [2]. Modüllere ait ayarlar /etc/modprobe.d/ dizinindeki .conf dosyalarında tutulur. Bu dosyalarla modüllere parametre girilebilir veya yüklenmesi engellenebilir. Kernel modülleri burada ele alabileceğimden çok daha geniş bir konu.

menuconfig yardımıyla veya elle istediğim seçenekleri ayarladıktan sonra make diyip yeni kernel'ı derledim. Tabii ki bu anında olmuyor. Benim bilgisayarımda standart seçeneklerle derlemek, yaklaşık 5dk sürüyor. Bazı seçenekler standartta modül olarak derlendiğinden, make'ten sonra make modules_install ile bu dosyaların o kernel'a karşılık gelen modül dizinine kopyalanması gerekiyor.

Derleme bittikten sonra, arch/x86/boot/bzImage (kernel) dosyası /boot altına vmlinuz-... adıyla kopyalanabilir ve grub yardımıyla açılabilir. Bu arada her dağıtım kernel'ı derlerken kullandığı .config dosyasını da /boot altına config-$(uname -r) adıyla kopyalar. Bu demektir ki, örn. kullandığım linux kernel'ının aynısını derlemek istersem, /boot'taki config dosyasını kernel dizinine .config adıyla kopyalayıp make edebilirim. Veya Fedora'nın v5.13.12 kernel'ını derlemek istersem, build sistemi koji'den kernel paketini aratır, x86_64 mimarisi için olan kernel-core paketini indiririm. Sonra rpm2cpio kernel-core-5.13.12-200.fc34.x86_64.rpm | cpio -idmv komutuyla açarım ve içinden çıkan lib/modules/5.13.12-200.fc34.x86_64/config dosyasını .config olarak linux-5.13.12 dizinine kopyalayıp, derlerim. Sonunda ortaya çıkan kernel Fedora'nın aynısı olacaktır (ek okuma: How to install a kernel from koji). Ancak Fedora gibi her makinada çalışması için üretilmiş bir kernel'da gerekli/gereksiz bir çok özelliğin açık olması, (örn. her ne kadar modüler de olsa Fibre Channel), derleme süresini aşırı arttırır. Öyle ki benim bilgisayarımda 5-6 saatte derlendi.

Bu kadar ön bilgiden sonra benim yaşadığım sorun ve çözümüne değinebilirim.


Vmware için Linux Kernel'ını Derlemek

Önce biri VBox'ta biri vmware'de iki makina kurdum. Bunlar ufak farklar dışında aynıydı. Yukarıda anlattığım gibi, standart config üzerine bir kaç özellik ekleyip kernel'ı derledim, /boot dizinine kopyaladım, initramfs'i ve bunları yükleyecek grub konfigürasyonunu oluşturdum. Buraya kadar herşey sorunsuz ilerledi.

Sonra sanal makinaları yeni kernel'la açtığımda VBox'taki çalışırken, vmware'deki çalışmayıp, initramfs'teki rescue shell'e düştü. Yandaki ekran görüntüsünden de görüleceği gibi, /dev altında sd* diskleri yok ve /dev/mapper boş. Bu olağandışı bir durum. Her iki makinada iki disk, ve disklerden birinde CentOS var. İkisini de CentOS'la tekrar açtım, çünkü sorunu tanılamaya yardımcı olacak lsblk, lspci gibi herhangi bir araç initramfs'te yok (Ubuntu Live da bu işi görecektir).

Aşağıdaki çıktıdan görüleceği gibi her iki makinadaki aygıtlara ve bunların kernel modüllerine lspci -k komutuyla baktım.

Vmware'de ata_piix, vmw_vmci, pcieport gibi genel geçer modüllerin yanısıra mptspi diye bir modül var, hem de SCSI denetleyicisi için. Alt taraftaki VBox çıktısındaysa ilginç herhangi bir sürücü yok. SATA controller olarak standart ahci kullanılıyor, mptspi bulunmuyor. Bu modülü biraz araştırdım. mptbase, mptscsih, mptspi sürücülerine ait konfigürasyon "Fusion MPT ScsiHost drivers for SPI" [3] olarak geçiyor, bu da kernelconfig.io'ya göre "Device Drivers/Fusion MPT device support" menüsü altında bulunuyor ve .config'de adı CONFIG_FUSION. Standart .config'de AHCI açık gelirken FUSION kapalı geliyor. Elbette bu seçenek CentOS'ta açık olduğundan onda bir sorun yaşanmıyor.

$ grep AHCI .config
CONFIG_SATA_AHCI=y
# CONFIG_SATA_AHCI_PLATFORM is not set
# CONFIG_SATA_ACARD_AHCI is not set

$ grep FUSION .config
# CONFIG_FUSION is not set

İşte sorunun kaynağı. Tekrar make menuconfig diyip, bu sürücüyü de seçtim. Makina sürekli vmware altında çalışacağından, modül olarak değil kernel'a dahil ederek bir daha derledim. Yeni kernel'i (arch/x86/boot/bzImage) /boot altına kopyalayıp makinayı yeniden başlattığımda makina düzgünce açıldı.




Kaynaklar:
[1]: Why both make clean and make mrproper are used?
[2]: https://en.wikipedia.org/wiki/Loadable_kernel_module
[3]: https://cateee.net/lkddb/web-lkddb/FUSION_SPI.html
 - : https://www.youtube.com/watch?v=WiZ05pnHZqM