1. Amaç    2

2. İşlemci ve Çekirdekler    2

3. Zamanlayıcı(Scheduler) ve Çekirdek İlişkisi    8

4. Çekirdek İzolasyonu    12

4.1 PC-Ubuntu Çekirdek İzolasyonu    13

4.2 IMX8MM Çekirdek İzolasyonu    15

5. İzole Edilen Çekirdeğin Üzerinde Uygulama Koşmak

Bu yazının pdf halini buradan indirebilirsiniz. JP

1. Amaç

Bu belgenin amacı Linux altında işlemcinin istenilen çekirdek izolasyonunun nasıl yapılacağını anlatmaktır. Bu konuyu anlatırken farklı çekirdek sayısına sahip üç  farklı bord kullanılmıştır. Konuyu anlamak için aslında çok çekirdekli tek bir bir bord olması yeterlidir. Birden fazla board üzerinde örnekler verme sebebi gömülü linux (embedded linux) kullanıcılarına konuyu daha yakın kılmaktır. Kullanılan bordlar PC-Ubuntu(12), IMX8MM Evk(4) ve BeagleBone Black(1).

Bu belgede özetle CPU’nun çekirdek sayısını ve özelliklerini inceleme, çekirde zamanlayıcısının(scheduler) çekirdekleri kullanımı, çekirdek izolasyonu, izole edilmiş çekirdekte uygulama koşma gibi konulara bakılacatır.

2. İşlemci ve Çekirdekler

İlk olarak sistemimizde bulunan işlemcimizi inceleyelim. İşlemcimizin tüm çekirdeklerine topluca bakalım. Bunun için $

 cat /proc/cpuinfo komutunu koşalım. İlk olarak BBB(BeagleBoneBlack) kartı üzerinde koştum. Bu kart tek çekirdek olduğu için işlemciye ait özellikler oldukça az satırda topluca görünmekte.  Kartın tek çekirdekli olduğunu ilk satırdaki processor:0 satırından sonra processor :1 satırının olmamasından anlayabiliriz.

Aynı komutu PC-Ubuntu üzerinde koşturduğumuzda aşağıdaki uzunca çıktıyı alırız. (çıktı çok uzun olduğu için sadece ilk iki çekirdek için olanı aldım. Görüldüğü gibi 12 çekirdek için ayrı ayrı bilgiler terminale yazılmakta. 

Son olarak dört çekirdekli IMX8MM-Evk ait çıktılar aşağıdaki gibidir.

İşlemci hakkında bilgi almak için birçok komut kullanabiliriz. Pc ortamında $lscpu komutunu kullanarak aşağıdaki çıktıları da alabilirsiniz. BBB ve IMX8MM-evk bu komutu içermediği  için koşulamadı.. 

Aşağıda direk işlemci çekirdek sayısını veren birkaç komut bulunmaktadır. Kullanılan her komuta ait çıktılar örnek olarak bulunmaktadır. 

$nproc    Bu komut ile çekirdek sayısını kolayca görebiliriz. $nproc aslında sistemdeki tüm fiziksel çekirdeklerin sayısını söylemez. Eğer sistemde izole edilmiş çekirdek bulunursa o çekirdek sayısını çıkartır ve dolayısı ile Linux kernel kullanabildiği çekirdek sayısının iletir.

Pc-Ubuntu 12 çekirdekli olmasına rağmen 11 sayısını görme sebebimiz 3. Çekirdeğin izole edilmiş olması. Bir sonraki başlıkta IMX8MM-Evk üzerinde çekirdek izolasyonu yapmamızdan sonra bu komutu tekrar koştuğumuzda dört rakamının değiştiğini göreceğiz.

$ grep “processor” /proc/cpuinfo    bu komutla da çekirdek sayısını görebiliriz


$ cat /proc/$$/status | tail -6

Yukarıdaki komutun hem kullanımı hem de çıktıları üzerinde konuşmak gerekir. $$ komutu kabuk(shell) ait process ID verir. Bunun doğruluğunu sınamak için aşağıdaki komutlara ve çıktılarına bakmak yeterlidir.

root@imx8mmevk:~# echo $$
3907root@imx8mmevk:~# ps
PID TTY          TIME CMD
3899 ttymxc1  00:00:00 login
3907 ttymxc1  00:00:00 sh
3976 ttymxc1  00:00:00 ps

Akla neden $$ yani shell process ID kullanıldı, neden ID değeri 1(init process) olan process kullanılmadı gelebilir. Linux terminaline login olunduğunda bir shell(kabuk) process oluştuğu için bu process varlığının garanti olması üzerine $$ kullanılmıştır ve ayrıca shell uygulaması her core üzerinde çalıştırılır.(aksi olmadıkça). Dolasyısı ile Cpus_allowed_list karşılık gelen çekirdekler tüm çekirdekleri kapsar. Örnek olması için process ID değeri 7 olan işlem üzerinden bu komutu koşmuş olsaydık Cpus_allowed: 1 ve Cpus_allowed_list: 0 olacaktı. Bu da tek bir çekirdek varmış gibi algılamaya neden olacaktı. Bu bilgiler sonrasında aslında $ cat /proc/$$/status | tail -6 komutu ilgili  işlemin hangi çekirdekleri üzerinde koşmaya izin verildiğini gösteriyor. 

Komutun kullanımının yanında komutun çıktılarına da bakmak gerekir ki bu çıktılar esas konumuz içi önemlidir. PC-ubuntu ortamında aldığımız çıktıda Cpus_allowed: ff7 ve Cpus_allowed_list: 0-2,4-11 satırlarını görüyoruz. Cpus_allowed satırında her bir çekirdek bir bite karşılık gelecek şekilde ifade edilmektedir. PC-Ubuntu 12 çekirdekli olduğu için dolayısı ile 12bitlik bir sayı ile ifade edilir. 3. Çekirdek(0 dan başlar) izole edildiği için ilk dört bitin karşılığı 0111 olur. Eğer 3. çekirdek izole edilmemiş olsaydı fff sayısını görecektik. Cpus_allowed_list satırında  0-2,4-11 aralıklarını görüyoruz. Bu değerler de Kernel tarafından kullanılabilen çekirdek numaralarını gösterir. 3. çekirdek izole edildiği için bu çekirdek liste dışıdır. Dolayısı ile kernel bu çekirdeğe bir iş vermeyecektir. 

3. Zamanlayıcı(Scheduler) ve Çekirdek İlişkisi

Bu başlık altında hangi çekirdekte hangi uygulama koşuyor ve aktif/pasif çekirdek konularına bakacağız.

Sistem açıldığında tüm çekirdekler aktif olarak sisteme hizmet eder. Hangi işi ne süre yapacaklarını zamanlayıcı(scheduler) belirler. İstenirse istenilen çekirdeğin durumu pasife alınarak zamanlayıcının bu çekirdeğe yeni iş vermesi engellenir. Bu kontrolü yapmak için ilk olarak /sys/devices/system/cpu dizinine gidelim ve her bir çekirdeğe ait olan dizinleri görelim.

root@UcanLinux:/sys/devices/system/cpu # lscpu0        cpuidle     kernel_max  online      power       ueventcpufreq     isolated    offline     possible    present
root@imx8mmevk:/sys/devices/system/cpu# lscpu0  cpu2  cpufreq  hotplug   kernel_max  offline  possible  presentcpu1  cpu3  cpuidle  isolated  modalias    online   power     uevent
zafer@zaferPC:/sys/devices/system/cpu$ lscpu0  cpu10  cpu2  cpu4  cpu6  cpu8  cpufreq  hotplug       isolated    microcode  offline  possible  present  ueventcpu1  cpu11  cpu3  cpu5  cpu7  cpu9  cpuidle  intel_pstate  kernel_max  modalias   online   power     smt      vulnerabilities

Daha sonra belirlediğimiz bir çekirdeğin dizinine gidelim. Elimdeki iki borda da ortak olması sebebiyle cpu2 seçiyorum.

Bu dizin altındaki online isimli dosya ile cpu2 kullanımını aktif yada pasif yapabiliriz. Online dosyasına 1 yazarak aktif, 0 yazarak pasir yapmış oluruz.

echo 0 > /sys/devices/system/cpu/cpu2/online  ===> cpu2 pasif

echo 1 > /sys/devices/system/cpu/cpu2/online  ===> cpu2 aktif

Cpu2 pasif yapılması ile artık bu çekirdeğe yeni iş atanmaz. Fakat bu çekirdek üzerindeki işler cpu2 pasif yapıldığı an başka bir çekirdeğe taşınmaz. Kısaca var olan işler yürütülmeye devam ederken yeni işler atanmaz. Yapılan bu işlem sistemin yeniden başlaması sonrasında geçerliliğini kaybeder ve cpu2 tekrar aktif konuma gelir.

Bu durumu test etmek için 4 çekirdekli IMX8MM-Evk bordumuzun üzerinde şöyle bir test yapalım. 1., 2. ve 3. çekirdekleri pasif hale getirelim ve arkasından işlemcimize bir iş yaptıralım. Sadece 0. Çekirdek aktif olduğu için bu seçilen işlemin sadece bu çekirdekte yapılması gerekir. Örneğe geçmeden önce hangi çekirdekte hangi işlerin yapıldığına bakalım. Bunun için şu komutu kullanmamız gerekir:
$ ps -eo psr,command | tr -s ” ” | grep “^ [1]”
Eğer birden fazla çekirdeği incelemek istersek [0] yerine [0|1] gibi OR operatörü ile çekirdek numaralarını yazmamız gerekir. Aşağıdaki çıktıda imx8mm-evk bordunda birinci çekirdekte koşan işlemleri görüyoruz. Hatta bu komutu tekrar koştuğumuzda farklı çıktılar alırız.

root@imx8mmevk:/sys/devices/system/cpu/cpu1# echo 0 > online
[ 4876.021078] IRQ 6: no longer affine to CPU1
[ 4876.021239] CPU1: shutdown
[ 4876.028163] psci: CPU1 killed.

root@imx8mmevk:/sys/devices/system/cpu# echo 0 > cpu2/online
[ 4906.141292] CPU2: shutdown
[ 4906.144013] psci: CPU2 killed.

root@imx8mmevk:/sys/devices/system/cpu# echo 0 > cpu3/online                    
[ 4914.096560] IRQ 6: no longer affine to CPU3
[ 4914.096729] CPU3: shutdown
[ 4914.103724] psci: CPU3 killed.

Yukarıdaki komutlar sonrasında artık sadece 0. Çekirdek kullanımda olur. 1. çekirdeğin üzerindeki işlere tekrar bakalım.

Görüldüğü gibi birinci çekirdek üzerindeki işler oldukça azalmıştır. Bu durumun tersi olarak tek aktif olan 0. çekirdeğin işleri ise çoğalmıştır. 

Örneği tamamlamak için aşağıdaki MyTest.sh isimli script kullanalım. Bu script sonsuz döngüde çalışmaktadır. Sonsuz döngüde olması sayesinde hangi çekirdekler üzerinde çalıştığını test edebiliriz. Script arka planda çalıştıralım ve çalışıp çalışmadığına bakalım.

root@imx8mmevk:~# ./MyTest.sh &
[1] 4429
root@imx8mmevk:~# ps
PID TTY          TIME CMD
3939 ttymxc1  00:00:00 login
3947 ttymxc1  00:00:00 sh
4429 ttymxc1  00:00:00 MyTest.sh
4432 ttymxc1  00:00:00 sleep
4433 ttymxc1  00:00:00 ps

Görüldüğü gibi 4429 PID ile çalışmakta. Daha sonrasında 0. Çekirdeğin koştuğu işlere baktığımızda kendi test script görürüz.

$ root@imx8mmevk:~# ps -eo psr,command | tr -s ” ” | grep “^ [0]”
…..
…..
0 -sh
0 [kworker/3:2]
0 [kworker/u8:2]
0 [kworker/u8:0]
0 [kworker/0:0H]
0 [kworker/0:2]
0 [kworker/u8:3]
0 [kworker/0:0]
0 [kworker/0:1]
0 /bin/bash ./MyTest.sh
0 sleep 1
0 ps -eo psr,command
0 tr -s
0 grep ^ [0]

Eğer diğer işlemciler için sorgulama yaptığımızda ($ ps -eo psr,command | tr -s ” ” | grep “^ [1]”) bu işlemin koşmadığını görürüz. 

Peki sistem bu durumda çalışmakta iken cpu1 tekrar aktif edersek ne olur? MyTest.sh cpu1 üzerinde koşulmaya başlanır mı? Çözüm için tek yapmamız gereken önce cpu1 aktif etmek ve arkasından cpu1 üzerinde koşan işlemlere bakmak olur. Cpu1 üzerindeki işleri sorguladığımızda eğer kendi uygulamamızı göremez isek bu durum çok normaldir.  Çünkü görevleri çekirdeklere atamak kernel zamanlayıcısının(scheduler) işidir ve biz sorgu yaptığımızda kernel zamanlayıcısı test uygulamamızın cpu0 koşmasını uygun görmüştür. Peş peşe birkaç sorgudan sonra cpu1 üzerinde test uygulamamızın koştuğunu görme ihtimalimiz artar. Aşağıda örnek çıktı bulunmakta. Aşağıdaki resimde bu anlatılara ait çıktılar bulunmaktadır. 

4. Çekirdek İzolasyonu

Bu başlık altında Kernel komutlarını/parameterlerini kullanarak istenilen çekirdeğin izolasyonun nasıl yapılacağına bakacağız. Daha sonra istenilen çekirdeğin ayrılıp ayrılmadığını test edeceğiz. 

Bu işlemleri PC-Ubuntu ve IMX8MM-Evk donanımları üzerinde yapacağız. BBB tek çekirdekli olduğu için bu başlık altında bu donanımı kullanmayacağız. PC-Ubuntu ve IMX8MM-Evk donanımlarının bootloader yapıları farklı olduğu için bu iki yapıda da bu işleri yapmayı görmüş olacağız. 

Elimizdeki donanımların farklı bootloader olması çekirdek izolasyonunun farklı yöntemlerle yapılmasına neden olmaktadır. Çünkü çekirdek izolasyonu bootloader tarafından kernel komutları/parametreleri ile kernel bildirilmektedir. PC-Ubuntu için  GRUB bootloader kullanılırken IMX8MM için uboot kullanılmaktadır. 

4.1 PC-Ubuntu Çekirdek İzolasyonu

PC-Ubuntu için GRUB bootloader kullanılmaktadır. Dolayısı ile çekirdek izolasyonu için gerekli parametreleri GRUB üzerinden kernel bildirmemiz gerekmektedir. Çekirdek izolasyonu için kullanılan kernel parametresi isolcpus parametresidir. Bu parametrenin kullanımına ait detaylı bilgiyi kernel parametrelerini anlatan bu linkten ulaşabilirsiniz. 

isolcpus=<cpu number>,….,<cpu number> 

Isolcpus yukarıdaki gibi kullanılır. Örnek birkaç kullanımı aşağıda gösterilmiştir.
isolcpus=2        ⇒  2. çekirdeği izole et
isolcpus=2,4     ⇒  2. ve 4. çekirdeği izole et
isolcpus=2-4     ⇒  2.,  3. ve 4. çekirdeği izole et (2 ile 4 arasını)

Üçüncü çekirdeği izole etmek için ilk olarak /etc/default/grub dosyasını düzenleme yapmak için bir text editörü ile açalım.
        $ gedit /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT değişkenine isolcpus=3 ekleyelim. Bu işlemler sonunda iligili satır aşağıdaki gibi olacaktır.
        GRUB_CMDLINE_LINUX_DEFAULT=”quiet splash isolcpus=3″
Bu ayarlamayı yaptıktan sonra dosya kaydedilerek çıkılır. Ayarlamaların aktif olması için grup güncellemesi yapılmalıdır. Bunun için aşağıdaki komut koşulur.
        $ sudo update-grub

Sitemi yeniden başlattığımızda artık 3. çekirdek izole edilmiş olur. Bu işlemlerin doğruluğu ve sonuçlarını sistemimizde görmek birçok yol vardır. İlk olarak kernel komutlarını içeren dosyanın içinde isolcpus komutunun eklenmiş olması gerekir. /proc/cmdline dosyasına bakarak kontrol edilebilir.

Diğer bir kontrol yöntemi ise sistem üzerindeki aktif kullanılabilir çekirdek sayısını kontrol etmektir. Bunun için $nproc komutu kullanılır. Bu konut sonrasında bir çekirdeği izole ettiğimiz için 12 çekirdekli olan cihazımızda nproc 11 değerini döner.
                   
$ cat /proc/$$/status | tail -6 komutunu koştuğumuzda Cpus_allowed_list açıklamasında 3. cpu olmadığını görürüz. (Bu komut ile açıklama daha öncesinde detaylı verildi.)

Kontrol için kullanılabilecek bir diğer komut da cpu3 üzerinde koşan uygulamalara bakmak. Doğal olarak cpu3 izole edildiği için bu cpu üzerinde bir uygulamanın koşmaması gerekir.
   
Yukarıdaki listenin tamamen boş olmasını beklentisi ile çalıştırmıztık. Aslında yukarıda belirtilenlerin hiç biri kullanıcı katmanına ait uygulamalar değil. Kernel uygulamasıdır. Bu liste kernel versiyonuna ve linux dağıtımına göre değişiklik gösterir. Migration kernel işlemini açıklaması şu şekildedir: “migration is the kernel process that handles moving processes from one CPU to another. So, for some reason your Linux scheduler decides that processes need moving to another CPU, and the migration process eats the CPU time.”

4.2 IMX8MM Çekirdek İzolasyonu

IMX8MM işlemcisi için çekirdek izolasyonu yapmak için bootloader üzerinden isolcpus kernel parametresini girmek gerekir. Embedded Linux kartlarda çok büyük oranda uboot kullanılmaktadır. Bu yüzden bu başlık altında yapılanlar IMX8MM kartına ait değildir. 

uboot üzerinden kernel parametresi girmeden önce kernel ayarlarından CPU_ISOLATION değerinin seçili olmasından emin olunması gerekir.
       

Bu kontrolden sonra cihaz açılma esnasında uboot adımında durdurulur. uboot adımında cihazı durdurduğumuz için gireceğimiz tüm komutlar uboot tarafından yorumlanır. 

Cpu2 izole etmemiz için tek yapmamız gerekn bootargs listesine “isolcpus=2” komutu eklemek. (imx8mm cpu2 izole edilecek). bootargs yüklenmiş eski değerlerin bozulmamasına dikkat etmek gerekir. Bunun için ilk olarak printenv koşularak var olan ayarlar okunur. IMX8MM-Evk donanımı için bootarg ilk hali:

mmcargs=setenv bootargs console=ttymxc1,115200 earlycon=ec_imx6q,0x30890000,115200 root=/dev/mmcblk2p2 rootwait rw

Yukarıdaki listenin sonuna “isolcpus=2” ekleyen komut aşağıdaki gibi olur.

setenv mmcargs “setenv bootargs   console=ttymxc1,115200 earlycon=ec_imx6q,0x30890000,115200 root=/dev/mmcblk2p2 rootwait rw isolcpus=2”

bootargs girerken hangi açılış için bootargs girildiği önemlidir. Eğer sisteminiz net üzerinden açılıyor ise dolayısı ile netboot çalışacaktır. netboot için bootargs yine setenv ile güncellenir.

netargs=setenv bootargs console=ttymxc1,115200 earlycon=ec_imx6q,0x30890000,115200 root=/dev/nfs ip=dhcp nfsroot=:,v3,tcp isolcpus=2;netboot=echo Booting from net …; run netargs;

bootargs güncellemesi yaptıktan sonra yapılan değişikliğin kaydedilmesi için saveenv komutunu koşmamız gerekir.
       
Artık cpu2 izolasyonu tamamlanmış bulunmakta. Cihaz yeniden başlatılır yada kernel açılması adımına geçilir. reset yada boot komutlarından birini koşmak yeterli.

cpu2 isolasyonu işlemini kontrol etmek için tıpkı PC-Ubunt cihazında yaptığımız kontoller yapabiliriz. 
$cat /proc/cmdline

    $nproc
     
4 çekirdekli cihazda nproc geri dönüş değeri 3 oldu.

$ cat /proc/$$/status | tail -6 komutunu koştuğumuzda Cpus_allowed_list açıklamasında 2. cpu olmadığını görürüz. (Bu komut ile açıklama daha öncesinde detaylı verildi.)
           

$ ps -eo psr,command | tr -s ” ” | grep “^ [2]”

5. İzole Edilen Çekirdeğin Üzerinde Uygulama Koşmak

Çekirdeğin izole edilme nedenleri genelde zaman yada görev kritik işlerin gecikme olmadan doğru çalışması gereksinimlerinden doğar. Çekirdek izolasyonunu adımı tamamlandıktan sonra istenilen uygulamayı izole ettiğimiz çekirdek üzerinde çalıştırabiliriz. 

taskset komutu bir uygulamayı istenilen çekirdek yada çekirdekler üzerinde koşulmasını sağlayan komuttur.
        taskset [options] mask command [argument…]
        taskset [options] -p [mask] pid

Örnek olması için MyTest.sh script IMX8MM donanımı üzerinde ayırdığımız cpu2 üzerinde koşturalım. Daha sonra cpu2 üzerinde yapılan işlere bakalım.

$ taskset -c 2 ./MyTest.sh &

$ ps -eo psr,command | tr -s ” ” | grep “^ [2]”

Diğer çekirdekler üzerindeki işleri kontrol edersek ./MyTest.sh listelerinde olmadığını görürüz.