internet pencereler Android

Linux çekirdeğini yapılandırma ve derleme. Kategori Arşivleri: Sonlandırma ve Başlatma Fonksiyonlarının Açıkça Tanımlanması Sürücü Programlama

Çekirdeği neden kendiniz derleyesiniz?
Belki de çekirdeğin derlenmesiyle ilgili sorulan asıl soru şudur: "Bunu neden yapmalıyım?"
Birçoğu, kendilerini akıllı ve gelişmiş bir "Linuxoid" olarak kanıtlamak için anlamsız bir zaman kaybı olarak görüyor. Aslında, çekirdeği derlemek çok önemlidir. Diyelim ki yeni bir dizüstü bilgisayar aldınız ve web kameranız çalışmıyor. Senin eylemlerin? Bir arama motoruna bakıyorsunuz ve bu konudaki bir soruna çözüm arıyorsunuz. Çoğu zaman, web kameranızın sizinkinden daha yeni bir çekirdek üzerinde çalıştığı ortaya çıkabilir. Hangi sürüme sahip olduğunuzu bilmiyorsanız, terminale uname -r yazın, sonuç olarak çekirdek sürümünü alacaksınız (örneğin, linux-2.6.31-10). Çekirdek derlemesi de performansı artırmak için yaygın olarak kullanılır: Gerçek şu ki, dağıtımlarda çekirdekler varsayılan olarak "herkes için" derlenir, bu yüzden ihtiyaç duymayabileceğiniz çok sayıda sürücü içerir. Yani kullanılan donanımı iyi biliyorsanız konfigürasyon aşamasında gereksiz sürücüleri devre dışı bırakabilirsiniz. Sistemin bit derinliğini değiştirmeden 4 Gigabayttan fazla RAM desteğini etkinleştirmek de mümkündür. Öyleyse, hala kendi çekirdeğinize ihtiyacınız varsa, derlemeye başlayalım!

Çekirdek kaynağının alınması.
Yapılacak ilk şey, doğru çekirdek sürümünün kaynak kodunu almaktır. Genellikle en son kararlı sürümü edinmeniz gerekir. Çekirdeğin tüm resmi sürümleri kernel.org'da mevcuttur. Halihazırda kurulu bir X sunucunuz (ev bilgisayarı) varsa, favori tarayıcınızda siteye gidebilir ve gerekli sürümü tar.gz arşivinden (sıkıştırılmış gzip) indirebilirsiniz. Konsolda çalışıyorsanız (örneğin, henüz X sunucusunu yüklemediyseniz veya sunucuyu yapılandırıyorsanız), bir metin tarayıcısı (örneğin, elinks) kullanabilirsiniz. Standart wget indirme yöneticisini de kullanabilirsiniz:
wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.33.1.tar.gz
Ancak, istediğiniz tam sürüm numarasını bilmeniz gerektiğini lütfen unutmayın.

Kaynak arşivi açma.
Kaynak koduyla birlikte arşivi aldıktan sonra, arşivi bir klasöre açmanız gerekir. Bu, grafik dosya yöneticilerinden (yunus, nautilus, vb.) veya mc aracılığıyla yapılabilir. Alternatif olarak, geleneksel tar komutunu kullanın:
tar -zxvf path_to_archive
Artık bir klasörünüz ve kaynak kodunuz var, komutu kullanarak ona gidin cd kernel_source_code_dizini(bir klasördeki dizinleri listelemek için ls komutunu kullanın).

Çekirdek yapılandırması.
Çekirdek kaynak dizinine geçtikten sonra, "20 dakikalık" bir çekirdek yapılandırması gerçekleştirmeniz gerekir. Amacı, yalnızca gerekli sürücüleri ve işlevleri bırakmaktır. Tüm komutların zaten süper kullanıcı adına yürütülmesi gerekir.

make config - yapılandırıcının konsol modu.

menuconfig - liste olarak konsol modu yapın.

xconfig yapın - grafik modu.

Gerekli değişiklikleri yaptıktan sonra ayarları kaydedin ve yapılandırıcıdan çıkın.

Derleme.
Montajın son aşamasının zamanı geldi - derleme. Bu iki komutla yapılır:
yap && yükle
İlk komut tüm dosyaları makine koduna derleyecek ve ikincisi yeni çekirdeği sisteminize yükleyecektir.
20 dakikadan birkaç saate kadar bekliyoruz (bilgisayarın gücüne bağlı olarak). Çekirdek kurulur. Grup (2) listesinde görünmesini sağlamak için (süper kullanıcıdan) yazın.
güncelleme grubu
Şimdi yeniden başlattıktan sonra "Escape" tuşuna basın ve yeni çekirdeğin listelendiğini göreceksiniz. Çekirdek açılmazsa, eski çekirdekle önyükleme yapın ve daha dikkatli yapılandırın.

KernelCheck - çekirdeği konsola girmeden derlemek.
çekirdeği tamamen grafik modunda oluşturmanıza olanak tanır Debian ve buna dayalı dağıtımlar için... Başlattıktan sonra, KernelCheck, çekirdeğin ve yamaların en son sürümlerini sunacak ve onayınızın ardından kaynak kodunu indirecek, grafik yapılandırıcıyı başlatacak. Program, çekirdeği .deb paketlerinde derleyecek ve kuracaktır. Sadece yeniden başlatmanız gerekiyor.

Hakkında: "Çeviriye dayalı" Linux Aygıt Sürücüsü 2. baskı. Tercüme: Knyazev Alexey [e-posta korumalı] Son değişiklik tarihi: 03.08.2004 Yerleştirme: http://lug.kmv.ru/index.php?page=knz_ldd2

Şimdi programlamaya başlayalım! Bu bölüm, modüllere ve çekirdek programlamaya genel bir bakış sağlar.
Burada, yapısı herhangi bir gerçek modül sürücüsüne karşılık gelen tam teşekküllü bir modül oluşturup çalıştıracağız.
Aynı zamanda, gerçek cihazların özelliklerini dikkate almadan ana pozisyonlara odaklanacağız.

Burada bahsedilen fonksiyonlar, değişkenler, başlıklar ve makrolar gibi çekirdeğin tüm parçaları
bölümün sonunda detaylandırılmıştır.

Selam Dünya!

Alessndro Rubini & Jonathan Corbet tarafından yazılan orijinal materyali incelerken, biraz talihsiz bir örnek buldum Merhaba dünya! Bu nedenle okuyucuya bence ilk modülün daha iyi bir versiyonunu sunmak istiyorum. Umarım 2.4.x çekirdeği altında derleyip kurarken herhangi bir sorun çıkmaz. Önerilen modül ve derlenme şekli, sürüm kontrolünü destekleyen ve desteklemeyen çekirdeklerde kullanılmasına izin verir. Daha sonra tüm detaylar ve terminoloji ile tanışacaksınız, şimdi vim'i açıyorum ve çalışmaya başlıyorum!

================================================= // hello_knz.c dosyası #include #Dahil etmek <1>Merhaba, dünya \ n "); 0 döndür;); void cleanup_module (void) (printk ("<1>Güle güle zalim dünya \ n ");) MODULE_LICENSE (“ GPL ”); ================================== ===============

Aşağıdaki Makefile, böyle bir modülü derlemek için kullanılabilir. $ (CC) ile başlayan satırın önüne bir sekme karakteri koymayı unutmayın….

================================================= BAYRAKLAR = -c -Duvar -D__KERNEL__ -DMODULE PARAM = -I / lib / modüller / $ (kabuk uname -r) / yapı / merhaba_knz.o içerir: merhaba_knz.c $ (CC) $ (BAYRAKLAR) $ (PARAM) - Ö [e-posta korumalı] $^ =================================================

Rubini & Corbet tarafından önerilen orijinal Merhaba dünya koduna kıyasla burada iki özellik kullanılır. İlk olarak, modül çekirdek ile aynı sürüme sahip olacaktır. Bu, derleme komut dosyasında PARAM değişkenini ayarlayarak elde edilir. İkinci olarak, modül artık GPL altında (MODULE_LICENSE () makrosu kullanılarak) lisanslanacaktır. Bu yapılmazsa, modülü çekirdeğe kurarken yaklaşık olarak aşağıdaki uyarıyı görebilirsiniz:

# insmod hello_knz.o Uyarı: hello_knz.o'nun yüklenmesi çekirdeği bozar: lisans yok Hatalı modüller hakkında bilgi için bkz. http://www.tux.org/lkml/#export-tainted Modül hello_knz yüklendi, uyarılarla birlikte

Şimdi modül derleme seçeneklerini açıklayalım (makrolar daha sonra açıklanacaktır):

-ile birlikte- bu seçenek mevcutsa, gcc derleyicisi, yürütülebilir bir ikili dosya oluşturmaya çalışmadan nesne dosyası oluşturulduktan hemen sonra dosya derleme işlemini durdurur.

-Duvar- gcc çalışırken maksimum uyarı çıkışı seviyesi.

-NS- makro sembollerin tanımları. Derlenmiş dosyadaki #define yönergesiyle aynı. Bu modülde kullanılan makroları kaynak dosyada #define kullanarak veya derleyici için -D seçeneğini kullanarak nasıl tanımladığınızın bir önemi yoktur.

-BEN- dahil edilen dosyalar için ek arama yolları. Size şu anda kullanılan çekirdek sürümünün tam adını verecek olan “uname -r” ikamesinin kullanımına dikkat edin.

Bir sonraki bölüm, bir modülün başka bir örneğini sağlar. Ayrıca çekirdekten nasıl kurulacağını ve kaldırılacağını da ayrıntılı olarak açıklar.

Orijinal Merhaba dünya!

Şimdi, Rubini & Corbet tarafından sunulan basit bir "Merhaba, Dünya" modülünün orijinal kodu burada. Bu kod, 2.0 ile 2.4 arasındaki çekirdekler altında derlenebilir. Bu örnek, kitaptaki diğerleri gibi, O'Reilly FTP sitesinde mevcuttur (bkz. Bölüm 1).

// merhaba.c dosyası #define MODULE #include int init_module (geçersiz) (baskı ("<1>Merhaba, dünya \ n "); 0 döndür;) void cleanup_module (void) (printk ("<1>Güle güle zalim dünya \ n ");)

İşlev yazdır () Linux çekirdeğinde tanımlanmıştır ve standart bir kitaplık işlevi olarak çalışır yazdır () C dilinde. Çekirdeğin, kullanıcı düzeyindeki kitaplıklarda değil, doğrudan çekirdekte bulunan kendi, tercihen küçük çıktı işlevine ihtiyacı vardır. Bir modül bir işlevi çağırabilir yazdır ()çünkü modülü komutla yükledikten sonra insmod modül, çekirdek ile iletişim kurar ve yayınlanmış (dışa aktarılan) çekirdek işlevlerine ve değişkenlerine erişime sahiptir.

Dize parametresi "<1>”printk() işlevine geçirilen mesajın önceliğidir. Orijinal İngilizce kaynaklar, mesajların günlüğe kaydedilme düzeyi anlamına gelen loglevel terimini kullanır. Burada orijinal “loglevel” yerine öncelik terimini kullanacağız. Bu örnekte, mesaj için düşük sayıya karşılık gelen yüksek öncelik kullanıyoruz. Mesajın yüksek önceliği kasıtlı olarak ayarlanır, çünkü varsayılan önceliğe sahip mesaj, modülün kurulduğu konsolda görüntülenmeyebilir. Varsayılan önceliğe sahip çekirdek mesajlarının çıkış yönü, çalışan çekirdeğin sürümüne, arka plan programının sürümüne bağlıdır. klogd, ve yapılandırmanız. Daha ayrıntılı olarak, bir işlevle çalışma yazdır () 4. Bölüm "Hata Ayıklama Teknikleri"nde açıklayacağız.

Komutu kullanarak modülü test edebilirsiniz. insmodçekirdeğe ve komutlara bir modül yüklemek için rmmodçekirdekten bir modülü kaldırmak için. Aşağıda bunun nasıl yapılabileceğini göstereceğiz. Bu durumda, init_module() giriş noktası, çekirdeğe bir modül kurulduğunda ve cleanup_module(), çekirdekten kaldırıldığında yürütülür. Modülleri yalnızca bir süper kullanıcının yükleyebileceğini ve kaldırabileceğini unutmayın.

Yukarıdaki örnek modül, yalnızca "modül sürüm desteği" bayrağı kapalı olarak oluşturulmuş bir çekirdek ile kullanılabilir. Ne yazık ki, çoğu dağıtım sürümlü çekirdekler kullanır (Bölüm 11, "kmod ve Gelişmiş Modülerleştirme", "Modüllerde Sürüm Kontrolü" bölümünde tartışılmıştır). Paketin eski sürümleri olmasına rağmen modüller bu tür modüllerin sürüm kontrolü ile derlenmiş çekirdeklere yüklenmesine izin verin, şimdi bu mümkün değil. Modutils paketinin insmod ve rmmod programlarını içeren bir dizi program içerdiğini hatırlayın.

Görev: Dağıtımınızdan modutils paketinin sürüm numarasını ve içeriğini belirleyin.

Sürüm kontrolünü destekleyen bir çekirdeğe böyle bir modül eklemeye çalıştığınızda aşağıdakine benzer bir hata mesajı görebilirsiniz:

# insmod merhaba.o merhaba.o: çekirdek modülü sürümü uyumsuzluğu merhaba.o, çekirdek sürümü 2.4.20 için derlenmiştir, bu çekirdek ise 2.4.20-9asp sürümüdür.

katalogda çeşitli modüller ftp.oreilly.com'daki örnekler için, biraz daha fazla satır uzunluğunda olan ve sürüm kontrolü olan veya olmayan çekirdeklere kurulabilen orijinal merhaba.c örnek programını bulacaksınız. Ancak, sürüm kontrol desteği olmadan kendi çekirdeğinizi oluşturmanızı şiddetle tavsiye ederiz. Aynı zamanda www.kernel.org sitesindeki orijinal kernel kaynaklarının alınması tavsiye edilir.

Çekirdek oluşturma konusunda yeniyseniz, Alessandro Rubini'nin (orijinal kitabın yazarlarından biri) http://www.linux.it/kerneldocs/kconf adresinde yayınladığı ve bu süreçte ustalaşmanıza yardımcı olacak makaleyi okumayı deneyin.

Yukarıdaki orijinal örnek modülü derlemek ve test etmek için aşağıdaki komutları bir metin konsolunda çalıştırın.

Root # gcc -c merhaba.c root # insmod ./hello.o Merhaba dünya kökü # rmmod merhaba Güle güle zalim dünya kökü #

Sisteminizin mesaj dizileri göndermek için kullandığı mekanizmaya bağlı olarak, fonksiyon tarafından gönderilen mesajların çıkış yönü yazdır () farklılık gösterebilir. Modülün derlenmesi ve test edilmesine ilişkin verilen örnekte, printk () işlevinden iletilen mesajlar, modülleri kurmak ve çalıştırmak için komutların verildiği aynı konsolda görüntülendi. Bu örnek bir metin konsolundan çekildi. komutları çalıştırırsanız insmod ve rmmod programın altından xterm o zaman büyük olasılıkla terminalinizde hiçbir şey görmeyeceksiniz. Bunun yerine, mesaj sistem günlüklerinden birinde sona erebilir, örneğin / var / günlük / mesajlar. Dosyanın tam adı dağıtıma bağlıdır. Günlük dosyalarının değiştirilme zamanına bakın. printk () işlevinden mesajları iletmek için kullanılan mekanizma, Bölüm 4, "Teknik", "Mesajlar Nasıl Günlüğe Alınır" bölümünde açıklanmıştır.
hata ayıklama ".

Modül mesajlarını sistem günlük dosyası / val / günlük / mesajlarda görüntülemek için, varsayılan olarak kendisine aktarılan dosyanın son 10 satırını görüntüleyen sistem kuyruk yardımcı programını kullanmak uygundur. Bu yardımcı programın ilginç bir seçeneği, yardımcı programı dosyanın son satırlarını izleme modunda başlatan -f seçeneğidir, yani. dosyada yeni satırlar göründüğünde, bunlar otomatik olarak görüntülenecektir. Bu durumda komutun yürütülmesini durdurmak için Ctrl + C tuşlarına basın. Bu nedenle, sistem günlük dosyasının son on satırını görüntülemek için komut satırına aşağıdakini girin:

Kök # kuyruk / var / günlük / mesajlar

Gördüğünüz gibi modül yazmak göründüğü kadar zor değil. En zor kısım, cihazınızın nasıl çalıştığını ve modülün performansını nasıl artıracağınızı anlamaktır. Bu bölüm boyunca, aygıt özelliklerini sonraki bölümlere bırakarak basit modüller yazma hakkında daha fazla şey öğreneceğiz.

Çekirdek modülleri ve uygulamalar arasındaki farklar

Bir uygulamanın, başlatılan uygulama bilgisayarın RAM'ine yerleştirildikten hemen sonra çalışmaya başlayan bir giriş noktası vardır. Bu giriş noktası, C'de ana () işlevi olarak tanımlanır. Ana () işlevinin sona ermesi, uygulamanın sona ermesi anlamına gelir. Modülün, modülü çekirdekten kurarken ve kaldırırken ve ayrıca kullanıcıdan gelen istekleri işlerken yürütülen birkaç giriş noktası vardır. Örneğin, init_module () giriş noktası, çekirdeğe bir modül yüklendiğinde yürütülür. Cleanup_module () işlevi, modül kaldırıldığında yürütülür. Gelecekte, modüle çeşitli isteklerde bulunulurken yürütülen modüle diğer giriş noktaları hakkında bilgi sahibi olacağız.

Modülleri yükleme ve boşaltma yeteneği, modülerleştirme mekanizmasının iki direğidir. Farklı anahtarlarda derecelendirilebilirler. Bir geliştirici için bu, her şeyden önce, geliştirme süresinde bir azalma anlamına gelir, çünkü sürücü işlevlerini uzun bir yeniden başlatma işlemi olmadan test edebilirsiniz.

Bir programcı olarak, bir uygulamanın, uygulamada bildirilmemiş bir işlevi çağırabileceğini bilirsiniz. Statik veya dinamik bağlantı aşamalarında, ilgili kitaplıklardan bu tür işlevlerin adresleri belirlenir. İşlev yazdır () kütüphanede tanımlanan bu çağrılabilir işlevlerden biri libc... Bir modül ise yalnızca çekirdekle ilişkilendirilir ve yalnızca çekirdek tarafından dışa aktarılan işlevleri çağırabilir. Çekirdek yürütülebilir kodu, harici kitaplıkları kullanamaz. Yani, örneğin, fonksiyon yazdır ()örnekte kullanılan Merhaba C, iyi bilinen işlevin bir analogudur yazdır () kullanıcı düzeyindeki uygulamalarda mevcuttur. İşlev yazdır ()çekirdekte bulunur ve mümkün olduğunca küçük olmalıdır. Bu nedenle, printf ()'den farklı olarak, veri türleri için çok sınırlı bir desteğe sahiptir ve örneğin, kayan noktalı sayıları hiç desteklemez.

Çekirdek 2.0 ve 2.2'nin uygulanması, tür belirteçlerini desteklemedi L ve Z... Sadece 2.4 çekirdekte tanıtıldılar.

Şekil 2-1, bir modüle giriş noktaları olan fonksiyonları çağırmak için mekanizmanın uygulamasını göstermektedir. Ayrıca, bu şekil kurulu veya kurulu bir modülün çekirdek ile etkileşim mekanizmasını göstermektedir.

Pirinç. 2-1. Modülü çekirdeğe bağlama

Unix / Linux işletim sistemlerinin özelliklerinden biri, çekirdek modüllerine bağlanabilecek kütüphanelerin olmamasıdır. Bildiğiniz gibi, modüller yüklendiğinde çekirdeğe bağlanır, bu nedenle modülünüzün dışındaki tüm işlevler çekirdek başlık dosyalarında bildirilmeli ve çekirdekte bulunmalıdır. Modül kaynakları asla kullanıcı alanı kitaplıklarından normal başlık dosyalarını içermemelidir. Çekirdek modüllerinde, yalnızca çekirdeğin bir parçası olan işlevleri kullanabilirsiniz.

Tüm çekirdek arayüzü, dizinlerde bulunan başlık dosyalarında açıklanmıştır. dahil / linux ve dahil / asmçekirdek kaynaklarının içinde (genellikle /usr/src/linux-x.y.z(x.y.z sizin çekirdek sürümünüzdür)). Daha eski dağıtımlar (temel olarak libc sürüm 5 veya daha az) kullanılan sembolik bağlantılar / usr / dahil / linux ve / usr / dahil / asmçekirdek kaynaklarındaki ilgili dizinlere. Bu sembolik bağlantılar, gerektiğinde özel uygulamalarda çekirdek arabirimlerini kullanma yeteneği sağlar.

Kullanıcı alanı kitaplığı arabirimi artık çekirdek arabiriminden ayrı olmasına rağmen, bazen kullanıcı işlemlerinin çekirdek arabirimlerini kullanması gerekli hale gelir. Bununla birlikte, çekirdek başlıklarındaki bağlantıların çoğu, çekirdeğe özgüdür ve kullanıcı uygulamalarına açık olmamalıdır. Bu nedenle, bu reklamlar korunur. #ifdef __KERNEL__ bloklar. Bu nedenle sürücünüz, diğer çekirdek kodları gibi, belirtilen makro sembolü ile derlenmelidir. __ÇEKİRDEK__.

Tek tek çekirdek başlıklarının rolü kitap boyunca gerektiği şekilde tartışılacaktır.

Herhangi bir büyük yazılım projesinde (çekirdek gibi) çalışan geliştiriciler bunu dikkate almalı ve bundan kaçınmalıdır. "ad alanı kirliliği"... Bu sorun, adları yeterince açıklayıcı olmayan (ayırt edilebilir) çok sayıda işlev ve global değişken olduğunda ortaya çıkar. Daha sonra bu tür uygulamalarla uğraşmak zorunda kalan programcı, "ayrılmış" adları ezberlemek ve yeni öğeler için benzersiz adlar bulmak için çok daha fazla zaman harcamak zorunda kalır. Ad çakışmaları (belirsizlikler), bir modül yüklenirken oluşan hatalardan, farklı bir yapılandırmada yerleşik bir çekirdek kullanan kullanıcıların başına gelebilecek kararsız veya açıklanamayan program davranışlarına kadar çok çeşitli sorunlara neden olabilir.

Geliştiriciler, çekirdek kodu yazarken bu tür hatalar yapmayı göze alamazlar, çünkü en küçük modül bile tüm çekirdeğe bağlı olacaktır. Ad çakışmalarını önlemenin en iyi çözümü, öncelikle program nesnelerinizi aşağıdaki gibi bildirmektir. statik ve ikinci olarak, global nesneleri adlandırmak için sistem çapında benzersiz bir önek kullanımı. Ayrıca, bir modül geliştiricisi olarak, daha sonra "Çekirdek Bağlama Tablosu" bölümünde açıklandığı gibi kodunuzdaki nesnelerin kapsamlarını kontrol edebilirsiniz.

Komutun çoğu (hepsi değil) sürümü insmod olarak bildirilmeyen tüm modül nesnelerini dışa aktarın statik, varsayılan olarak, yani modülde özel talimatlar tanımlanmadıkça. Bu nedenle, dışa aktarmayacağınız modül nesnelerini şu şekilde bildirmek son derece mantıklıdır. statik.

Bir modül içindeki yerel nesneler için benzersiz bir önek kullanmak, hata ayıklamayı kolaylaştırdığından iyi bir uygulama olabilir. Sürücünüzü test ederken, çekirdeğe ek nesneler aktarmanız gerekebilir. Adları belirtmek için benzersiz bir önek kullanarak, çekirdek ad alanına çarpışmalar getirme riskini üstlenmezsiniz. Çekirdek önekleri, kurala göre küçük harfli karakterler kullanır ve biz de bu kurala bağlı kalacağız.

Çekirdek ve kullanıcı süreçleri arasındaki bir diğer önemli fark, hata işleme mekanizmasıdır. Çekirdek, kullanıcı sürecinin yürütülmesini kontrol eder, bu nedenle kullanıcı sürecindeki bir hata, sisteme zararsız bir mesajla sonuçlanır: segmentasyon hatası. Bu durumda, bir kullanıcı uygulamasının kaynak kodundaki bir hatayı izlemek için her zaman bir hata ayıklayıcı kullanılabilir. Çekirdekte meydana gelen hatalar ölümcüldür - tüm sistem için değilse de en azından mevcut süreç için. Bölüm 4, “Hata Ayıklama Teknikleri”ndeki “Sistem Hatalarında Hata Ayıklama” bölümünde, çekirdek hatalarını izlemenin yollarına bakacağız.

Kullanıcı alanı ve çekirdek alanı

Modül sözde yürütülür çekirdek alanı uygulamalar çalışırken. Bu kavram, işletim sistemleri teorisinin temelidir.

İşletim sisteminin temel amaçlarından biri, kullanıcı ve kullanıcı programlarına, çoğu harici aygıtlar tarafından temsil edilen bilgisayar kaynakları sağlamaktır. İşletim sistemi yalnızca kaynaklara erişim sağlamakla kalmamalı, aynı zamanda bunların tahsisini ve kullanımını kontrol ederek çakışmaları ve yetkisiz erişimi engellemelidir. Buna ek olarak, işletim sistemi programlar için bağımsız işlemler oluşturabilir ve kaynaklara yetkisiz erişime karşı koruma sağlayabilir. Bu önemsiz görevin çözümü, yalnızca işlemcinin sistem programlarını kullanıcı uygulamalarından koruması durumunda mümkündür.

Hemen hemen her modern işlemci, yürütülebilir kod için farklı ayrıcalık seviyeleri uygulayarak bu ayrımı sağlayabilir (en az iki seviye gereklidir). Örneğin, I32 işlemciler 0'dan 3'e kadar dört ayrıcalık düzeyine sahiptir. Ayrıca, düzey 0 en yüksek ayrıcalıklara sahiptir. Bu tür işlemciler için, yalnızca ayrıcalıklı düzeylerde yürütülebilen bir ayrıcalıklı komutlar sınıfı vardır. Unix sistemleri iki düzeyde işlemci ayrıcalığı kullanır. İşlemcinin ikiden fazla ayrıcalık düzeyi varsa, en düşük ve en yüksek olan kullanılır. Unix çekirdeği, kullanıcının donanımı ve süreçleri üzerinde kontrol sağlayarak en yüksek ayrıcalık düzeyinde çalışır.

hakkında konuştuğumuzda çekirdek alanı ve kullanıcı işlem alanı Yürütülebilir kodun yalnızca farklı ayrıcalık düzeylerini değil, aynı zamanda farklı adres alanlarını da kastediyorum.

Unix, yürütmeyi iki durumda kullanıcı işlem alanından çekirdek alanına aktarır. İlk olarak, kullanıcı uygulaması çekirdeğe bir çağrı yaptığında (sistem çağrısı) ve ikincisi, donanıma hizmet verirken kesintiler. Bir sistem çağrısında yürütülen çekirdek kodu, bir süreç bağlamında çalışır, yani Çağrı sürecinin çıkarları doğrultusunda çalışan, sürecin adres alanındaki verilere erişime sahiptir. Öte yandan, donanım kesintisine hizmet verirken yürütülen kod, sürece göre eşzamansızdır ve herhangi bir özel işleme ait değildir.

Modüllerin amacı, çekirdeğin işlevselliğini genişletmektir. Modül kodu, çekirdek alanında yürütülür. Tipik olarak, bir modül daha önce belirtilen görevlerin her ikisini de gerçekleştirir: modülün işlevlerinden bazıları sistem çağrılarının bir parçası olarak yürütülür ve bazıları kesintileri yönetmekten sorumludur.

Çekirdekte paralelleştirme

Aygıt sürücülerini programlarken, uygulama programlamasının aksine, yürütülebilir kodu paralelleştirme sorunu özellikle akuttur. Tipik olarak, bir uygulama, ortamını değiştirme konusunda endişelenmeden baştan sona sıralı bir şekilde çalışır. Çekirdek kodu, aynı anda birkaç kez erişilebileceği varsayımıyla çalışmalıdır.

Çekirdek kodunu paralelleştirmenin birçok nedeni vardır. Genellikle Linux üzerinde çalışan birçok işlem vardır ve bunlardan bazıları aynı anda modül kodunuza erişmeye çalışabilir. Birçok cihaz, işlemcide donanım kesintilerini tetikleyebilir. Kesme işleyicileri eşzamansız olarak çağrılır ve sürücünüz başka bir istek yürütmekle meşgulken çağrılabilir. Bazı yazılım soyutlamaları (Bölüm 6, “Zamanın Akışı”nda açıklanan çekirdek zamanlayıcıları gibi) de eşzamansız olarak çalışır. Ayrıca Linux, simetrik çok işlemcili (SMP) bir sistemde çalışabilir, bu sayede sürücü kodunuz aynı anda birden çok işlemcide paralel olarak çalışabilir.

Bu nedenlerle, sürücü kodları da dahil olmak üzere Linux çekirdek kodu, yeniden girişli olmalıdır, yani. aynı anda birden fazla veri bağlamıyla çalışabilmelidir. Veri yapıları, paralel olarak çalışan birden çok iş parçacığı ile tasarlanmalıdır. Buna karşılık, çekirdek kodunun birden çok paralel veri akışını onlara zarar vermeden işleyebilmesi gerekir. Paralel olarak yürütülebilen ve farklı bir yürütme sırasının istenmeyen sistem davranışına yol açabileceği durumlardan kaçınabilen kod yazmak zaman alıcı ve muhtemelen yanıltıcıdır. Bu kitaptaki her sürücü örneği, eşzamanlılık göz önünde bulundurularak yazılmıştır. Gerekirse, böyle bir kod yazma tekniğinin özelliklerini açıklayacağız.

Programcıların yaptığı en yaygın hata, bazı kod bölümleri uyuyamadığı için eşzamanlılığın bir sorun olmadığını varsaymalarıdır. Aslında, kritik çekirdek kodu yürütülürken işlemci tarafından alınamayan kesme işleyicileriyle ilgili önemli bir istisna dışında, Linux çekirdeği disk belleğine alınmamıştır. Son zamanlarda, çoğu durumda istenmeyen paralelliği önlemek için yüklenemezlik yeterli olmuştur. Ancak SMP sistemlerinde, paralel hesaplama nedeniyle kod boşaltma gerekli değildir.

Kodunuz atılmayacağını varsayarsa, SMP sistemlerinde düzgün çalışmayacaktır. Böyle bir sisteminiz olmasa bile, başkası sizin kodunuzu kullanarak sahip olabilir. Çekirdeğin gelecekte sayfalamayı kullanması da mümkündür, bu nedenle tek işlemcili sistemler bile her yerde paralellik ile uğraşmak zorunda kalacaktır. Bu tür çekirdekleri uygulamak için zaten seçenekler var. Böylece, ihtiyatlı bir programcı, bir SMP sisteminde çalışacağı varsayımıyla çekirdek kodu yazacaktır.

Yaklaşık. çevirmen:Üzgünüm, ama son iki paragraf benim için net değil. Belki de bu yanlış bir çevirinin sonucudur. Bu nedenle orijinal metni veriyorum.

Sürücü programcıları tarafından yapılan yaygın bir hata, belirli bir kod segmenti olduğu sürece eşzamanlılığın bir sorun olmadığını varsaymaktır.
uyumaz (veya "bloke"). Linux çekirdeğinin önleyici olmadığı doğrudur; önemli istisna dışında
servis kesintileri, isteyerek vermeyen çekirdek kodundan işlemciyi uzaklaştırmaz. Geçmiş zamanlarda, bu önleyici olmayan
davranış, çoğu zaman istenmeyen eşzamanlılığı önlemek için yeterliydi. Bununla birlikte, SMP sistemlerinde, neden olmak için önceden alma gerekli değildir.
eşzamanlı yürütme

Kodunuz öncelikli olmayacağını varsayarsa, SMP sistemlerinde düzgün çalışmayacaktır. Böyle bir sisteminiz olmasa bile,
kodunuzu çalıştıran diğerlerinde bir tane olabilir. Gelecekte, çekirdeğin önleyici bir çalışma moduna geçmesi de mümkündür.
bu noktada tek işlemcili sistemler bile her yerde eşzamanlılık ile uğraşmak zorunda kalacak (çekirdeğin bazı varyantları zaten uygulanıyor)
o).

Mevcut süreç hakkında bilgi

Çekirdek modül kodu, uygulamalar gibi sırayla yürütülmese de, çekirdeğe yapılan çoğu çağrı, çağrı sürecine göre yapılır. Çekirdek kodu, yapıya işaret eden genel işaretçiye başvurarak kendisini çağıran süreci tanımlayabilir. struct task_struct dosyada 2.4 çekirdek için tanımlanmış dahil ... Işaretçi akımşu anda çalışan kullanıcı sürecini gösterir. gibi sistem çağrıları yürütürken açık () veya kapat (), bunlara neden olan bir süreç olmalı. Çekirdek kodu, gerekirse, bir işaretçi aracılığıyla çağrı işlemiyle ilgili belirli bilgileri arayabilir akım... Bu işaretçiyi kullanma örnekleri için, Bölüm 5, "Gelişmiş Karakter Sürücüsü İşlemleri" içindeki "Aygıt Dosya Erişimini Kontrol Etme" konusuna bakın.

Bugüne kadar, işaretçi akımçekirdeğin önceki sürümlerinde olduğu gibi daha genel bir değişken değildir. Geliştiriciler, yığın sayfasına aktararak mevcut süreci tanımlayan yapıya erişimi optimize etti. Akımın uygulama detaylarına dosyadan bakabilirsiniz. ... Orada göreceğiniz kod size basit gelmeyebilir. Linux'un SMP odaklı bir sistem olduğunu ve birden fazla CPU ile uğraşırken global bir değişkenin çalışmayacağını unutmayın. Uygulama ayrıntıları diğer çekirdek alt sistemlerinde gizli kalır ve aygıt sürücüsü işaretçiye erişebilir akım sadece arayüz üzerinden .

Modül açısından, akım harici bir bağlantı gibi görünüyor yazdır ()... Modül kullanabilir akım ihtiyacın olan her yerde. Örneğin, aşağıdaki kod parçası, modülü çağıran işlemin tanımlayıcısını (işlem kimliği - PID) ve komut adını yazdırır ve bunları yapının ilgili alanlarından geçirir. struct task_struct:

Printk ("İşlem \"% s \ "(pid% i) \ n", akım-> iletişim, akım-> pid);

current->comm alanı, geçerli işlemi oluşturan komutun dosya adıdır.

Modüllerin derlenmesi ve yüklenmesi

Bu bölümün geri kalanı, atipik de olsa eksiksiz bir modül yazmaya ayrılmıştır. Onlar. modül, Bölüm 1, Aygıt Sürücülerine Giriş bölümündeki Aygıt ve Modül Sınıfları bölümünde açıklanan sınıfların hiçbirine ait değildir. Bu bölümde gösterilen örnek sürücü kafatası olarak adlandırılacaktır (Yerleri Yüklemek için Basit Çekirdek Yardımcı Programı). Kendi yerel kodunuzu yazmak için scull modülünü şablon olarak kullanabilirsiniz.

Eski güzel Unix geleneğinde (/ usr / local) kişisel kod değişikliklerinizi vurgulamak için “yerel kod” terimini kullanıyoruz.

Ancak, init_module() ve cleanup_module() fonksiyonlarını içerikle doldurmadan önce, modül nesne kodunu oluşturmak için kullanılacak makefile betiği yazacağız.

Önişlemci herhangi bir başlık dosyasının eklenmesini işlemeden önce, makro sembolü __KERNEL__ #define yönergesi tarafından tanımlanmalıdır. Daha önce bahsedildiği gibi, çekirdek arabirim dosyalarında çekirdeğe özgü bir bağlam tanımlanabilir, bu yalnızca __KERNEL__ sembolü ön işleme sırasında önceden tanımlanmışsa görünür.

#define yönergesi ile tanımlanan bir diğer önemli sembol ise MODULE sembolüdür. Arabirimi etkinleştirmek için from tanımlanmalıdır (çekirdekle derlenecek sürücüler hariç). Çekirdeğe derlenen sürücüler bu kitapta anlatılmayacaktır, bu nedenle tüm örneklerimizde MODULE sembolü bulunacaktır.

Bir SMP sistemi için bir modül oluşturuyorsanız, çekirdek arayüzlerini etkinleştirmeden önce __SMP__ makrosunu da tanımlamanız gerekir. 2.2 çekirdeğinde, tek işlemcili ve çok işlemcili sistem arasındaki seçim, çekirdek yapılandırmasında ayrı bir öğe olarak tanıtıldı. Bu nedenle, aşağıdaki satırları modülünüzün ilk satırları olarak dahil etmek, çoklu işlemci desteği ile sonuçlanacaktır.

#Dahil etmek #ifdef CONFIG_SMP # tanımla __SMP__ #endif

Modül geliştiricileri ayrıca derleyici için -O optimizasyon bayrağını tanımlamalıdır, çünkü birçok işlev çekirdek başlıklarında satır içi olarak bildirilir. gcc derleyicisi, optimizasyon etkinleştirilene kadar işlevlerde satır içi genişletme gerçekleştirmez. -g ve -O seçenekleriyle satır içi değiştirmelerin uzantısını etkinleştirmek, daha sonra hata ayıklayıcıdaki satır içi işlevleri kullanarak kodda hata ayıklamanıza olanak tanır. Çekirdek, satır içi işlevleri kapsamlı bir şekilde kullandığından, doğru şekilde genişletilmeleri çok önemlidir.

Bununla birlikte, derleyici satır içi olarak tanımlanmayan işlevleri genişletebileceğinden, -O2 seviyesinin üzerindeki herhangi bir optimizasyonun riskli olduğunu unutmayın. Bu sorunlara yol açabilir. bazı işlev kodları, standart çağrı yığınını bulmayı bekler. Satır içi genişletme, ilgili işlev çağrısı talimatı yerine çağrılma noktasında işlev kodunun eklenmesi olarak anlaşılır. Buna göre, bu durumda, fonksiyon çağrısı olmadığı için çağrı yığını da yoktur.

Modülün kurulması gereken yerde çekirdeği oluşturmak için kullanılan modülleri derlemek için aynı derleyiciyi kullandığınızı kontrol etmeniz gerekebilir. Ayrıntılar için dosyadaki orijinal belgeye bakın Belgeler / Değişikliklerçekirdek kaynakları dizininde bulunur. Çekirdek ve derleyici geliştirmeleri genellikle geliştirme ekipleri arasında senkronize edilir. Bu öğelerden birinin güncellenmesinin diğerinde hatalara neden olduğu durumlar olabilir. Bazı dağıtım satıcıları, derleyicinin kullandıkları çekirdeğe uymayan ultra yeni sürümlerini gönderir. Bu durumda, genellikle ayrı bir paket sağlarlar (genellikle kgcc) için özel olarak tasarlanmış bir derleyici ile
çekirdeği derlemek.

Son olarak can sıkıcı hataları önlemek için derleme seçeneğini kullanmanızı öneririz. -Duvar(tüm uyarılar - tüm uyarılar). Tüm bu uyarıları karşılamak için normal programlama stilinizi değiştirmeniz gerekebilir. Çekirdek kodu yazarken Linus Torvalds tarafından önerilen kodlama stilinin kullanılması tercih edilir. Yani, belge Belgeleme / KodlamaStil, yeterince ilgi çekicidir ve çekirdek düzeyinde programlamayla ilgilenen herkese önerilir.

Son zamanlarda gördüğümüz modülün derleme bayrakları setini değişkene yerleştirmeniz önerilir. FLAG'LAR senin Makefile'n. make yardımcı programı için bu, kullanımı aşağıdaki açıklamadan netleşecek olan özel bir değişkendir.

Değişkendeki bayrakların dışında FLAG'LAR, Makefile'nizin farklı nesne dosyalarını birleştirmek için bir hedefe ihtiyacı olabilir. Bu hedef, yalnızca modül kodu, genel olarak nadir olmayan birden çok kaynak dosyaya bölündüğünde gereklidir. Nesne dosyaları komutla birleştirilir ld -r Bağlayıcının kullanılmasına rağmen, geleneksel anlamda bir bağlama işlemi olmayan , ( ld). Komut yürütmenin sonucu ld -r bağlayıcı giriş dosyalarının nesne kodlarını birleştiren başka bir nesne dosyasıdır. Seçenek -r anlamına geliyor " yer değiştirebilir - yer değiştirme”, yani komutun çıktı dosyasını adres alanına taşırız, çünkü henüz mutlak işlev çağrısı adreslerine sahip değil.

Aşağıdaki örnek, bir modülü iki kaynak dosyayla derlemek için gereken minimum Makefile'ı gösterir. Modülünüz bir kaynak dosyadan oluşuyorsa, verilen örnekten komutu içeren hedefi kaldırmanız gerekir. ld -r.

# Çekirdek kaynakları dizininize giden yol buradan değiştirilebilir, # veya “make” çağırırken onu parametre olarak iletebilirsiniz KERNELDIR = / usr / src / linux include $ (KERNELDIR) /. Config CFLAGS = -D__KERNEL__ -DMODULE - I $ (KERNELDIR) / include \ -O -Wall ifdef CONFIG_SMP CFLAGS + = -D__SMP__ -DSMP endif all: skull.o skull.o: skull_init.o skull_clean.o $ (LD) -r $ ^ -o [e-posta korumalı] temiz: rm -f * .o * ~ çekirdek

make yardımcı programında yeniyseniz, * .c dosyalarını nesne * .o dosyalarında derlemek için hiçbir kural olmamasına şaşırabilirsiniz. Bu tür kuralların tanımı gerekli değildir, çünkü make yardımcı programı, gerekirse, varsayılan derleyiciyi veya değişken tarafından belirtilen derleyiciyi kullanarak * .c dosyalarını * .o dosyalarına dönüştürür $ (CC)... Bu durumda değişkenin içeriği $ (CFLAGS) derleme bayraklarını belirtmek için kullanılır.

Modülü oluşturduktan sonraki adım onu ​​çekirdeğe yüklemektir. Bunun için modülün tüm tanımsız sembollerini (fonksiyon çağrıları vb.) çalışan çekirdeğin sembol tablosuna bağlayan insmod yardımcı programını kullanacağımızı söylemiştik. Ancak, bir bağlayıcıdan (ld gibi) farklı olarak, modülün disk dosyasını değiştirmez, çekirdeğe bağlı modül nesnesini RAM'e yükler. insmod yardımcı programı birkaç komut satırı seçeneğini kabul eder. Detaylar üzerinden görüntülenebilir adam insmod... Bu seçenekleri kullanarak, örneğin modülü çekirdeğe bağlamadan önce modülünüzdeki belirli tamsayı ve dize değişkenlerini belirli değerlere atayabilirsiniz. Bu nedenle, bir modül uygun şekilde tasarlanmışsa, önyükleme sırasında yapılandırılabilir. Modülün bu şekilde yapılandırılması, kullanıcıya derleme zamanında yapılandırmaya göre daha fazla esneklik sağlar. İndirme aşaması konfigürasyonu, bu bölümün ilerisindeki “Manuel ve Otomatik Konfigürasyon” bölümünde açıklanmıştır.

Bazı okuyucular, insmod yardımcı programının nasıl çalıştığının ayrıntılarıyla ilgilenecektir. insmod uygulaması, kernel / module.c'de tanımlanan birkaç sistem çağrısına dayanır. sys_create_module() işlevi, bir modülü yüklemek için çekirdek adres alanında gerekli miktarda bellek ayırır. Bu bellek, vmalloc () işlevi kullanılarak tahsis edilir (Bölüm 7 “Hafızayı Elde Etme” bölümündeki “vmalloc ve Arkadaşları” bölümüne bakın). get_kernel_sysms () sistem çağrısı, bağlantı sırasında nesnelerin gerçek adreslerini belirlemek için kullanılacak bir çekirdek sembol tablosu döndürür. sys_init_module () işlevi, modülün nesne kodunu çekirdek adres alanına kopyalar ve modülün başlatma işlevini çağırır.

Çekirdek kodunun kaynaklarına bakarsanız, orada sys_ öneki ile başlayan sistem çağrı adları bulacaksınız. Bu önek yalnızca sistem çağrıları için kullanılır. Başka hiçbir işlev kullanmamalıdır. Grep ile çekirdek kaynaklarını işlerken bunu aklınızda bulundurun.

Sürüm bağımlılıkları

Burada söylenenlerden daha fazlasını bilmiyorsanız, oluşturduğunuz modüllerin bağlanacakları çekirdeğin her sürümü için büyük olasılıkla yeniden derlenmesi gerekecektir. Her modül adı verilen bir sembol tanımlamalıdır. __module_kernel_version kimin değeri
insmod yardımcı programı ile mevcut çekirdeğin sürümüyle karşılaştırıldığında. Bu sembol bölümde yer almaktadır. .modinfo ELF (Yürütülebilir ve Bağlama Formatı) dosyaları. Bu, Bölüm 11, “kmod ve Gelişmiş Modülerleştirme”de daha ayrıntılı olarak açıklanmaktadır. Lütfen bu sürüm kontrol yönteminin yalnızca 2.2 ve 2.4 çekirdekleri için geçerli olduğunu unutmayın. 2.0 çekirdeğinde bu biraz farklı bir şekilde yapılır.

Derleyici, başlık dosyasının dahil edildiği her yerde bu sembolü tanımlayacaktır. ... Bu nedenle yukarıdaki merhaba.c örneğinde bu sembolü açıklamadık. Bu ayrıca, modülünüz birçok kaynak dosyadan oluşuyorsa, dosyayı dahil etmeniz gerektiği anlamına gelir. kodunuza yalnızca bir kez girin. Bir istisna, tanımın kullanılması durumudur. __HAYIR_VERSION__, ki daha sonra buluşacağız.

Aşağıda, 2.4.25 çekirdek kodundan çıkarılan module.h dosyasındaki açıklanan sembolün tanımı verilmiştir.

Statik const char __module_kernel_versio / PRE__attribute __ ((bölüm (". Modinfo"))) = "kernel_version =" UTS_RELEASE;

Modül, sürüm uyuşmazlığı nedeniyle yüklenemiyorsa, anahtarı insmod yardımcı programının parametre dizesine ileterek bu modülü yüklemeyi deneyebilirsiniz. -F(Kuvvet). Modülün bu şekilde yüklenmesi güvenli değildir ve her zaman başarılı değildir. Olası başarısızlıkların nedenlerini açıklamak zordur. Bağlantı sırasında semboller çözülemediğinden modül yüklenmeyebilir. Bu durumda, ilgili bir hata mesajı alacaksınız. Başarısızlığın nedenleri, çekirdeğin çalışmasındaki veya yapısındaki değişikliklerde de olabilir. Bu durumda modülün yüklenmesi ciddi çalışma zamanı hatalarına ve sistem paniğine neden olabilir. İkincisi, bir sürüm kontrol sistemi kullanmak için iyi bir teşvik olmalıdır. Sürüm uyuşmazlığı, çekirdekte sürüm kontrolü kullanılarak daha zarif bir şekilde yönetilebilir. Bunu, Bölüm 11 “kmod ve Gelişmiş Modülerleştirme” “Modüllerde Versiyon Kontrolü” bölümünde ayrıntılı olarak konuşacağız.

Modülünüzü belirli bir çekirdek sürümü için derlemek istiyorsanız, o belirli çekirdek sürümünden başlık dosyalarını eklemelisiniz. Yukarıdaki örnek Makefile'de, bu dosyaların bulunduğu dizini belirlemek için değişken kullanılmıştır. KERNELDIR... Bu özel derleme, çekirdek kaynakları mevcut olduğunda nadir değildir. Ayrıca, dizin ağacında çekirdeğin farklı sürümlerinin olması nadir görülen bir durum değildir. Bu kitaptaki tüm modül örnekleri değişkeni kullanır. KERNELDIR birleştirilmiş modülü bağlaması gereken çekirdek sürümünün kaynak dizininin konumunu belirtmek için. Bu dizini belirtmek için bir sistem değişkeni kullanabilir veya yapmak için komut satırı parametreleri aracılığıyla konumunu iletebilirsiniz.

Bir modül yüklendiğinde, insmod yardımcı programı modülün nesne dosyaları için kendi arama yollarını kullanır ve şu andan itibaren sürüme bağlı dizinlere bakar. / lib / modüller... Yardımcı programın eski sürümleri geçerli dizini arama yollarına dahil etse de, bu davranış artık güvenlik nedenleriyle kabul edilemez olarak kabul edilmektedir (sistem değişkenini kullanmakla aynı sorunlar). YOL). Bu nedenle, geçerli dizinden bir modül yüklemek istiyorsanız, bunu stilde belirtebilirsiniz. ./module.o... Modülün konumunun bu göstergesi, insmod yardımcı programının herhangi bir sürümü için çalışacaktır.

Bazen 2.0.x ve 2.4.x sürümleri arasında farklılık gösteren çekirdek arayüzleriyle karşılaşabilirsiniz. Bu durumda, çekirdeğin mevcut sürümünü belirleyen bir makro kullanmaya başvurmanız gerekecektir. Bu makro başlık dosyasında bulunur ... Bunları kullanırken arayüzlerdeki farklılık durumlarına işaret edeceğiz. Bu, ya yol boyunca ya da bölümün sonunda, sürüm bağımlılıklarıyla ilgili özel bir bölümde yapılabilir. Ayrıntıları ayrı bir bölüme taşımak, bazı durumlarda, bu kitap için profil oluşturan 2.4.x çekirdek sürümüne göre açıklamayı karmaşıklaştırmamaya izin verecektir.

Başlık dosyasında linux / sürüm.h aşağıdaki makrolar, çekirdek sürümünün tanımıyla ilgili olarak tanımlanmıştır.

UTS_RELEASE Geçerli dizinin çekirdek sürümünü tanımlayan bir dizeye genişleyen bir makro
kaynak ağaç. Örneğin, bir makro buna genişleyebilir
hat: "2.3.48" . LINUX_VERSION_CODE Bu makro, çekirdek sürümünün ikili gösterimine genişler.
numaranın her bölümü için bir bayt. Örneğin, ikili
2.3.48 sürümünün gösterimi 131888 olacaktır (ondalık
hex 0x020330 için temsil). muhtemelen ikili
görünüm size dizeden daha uygun görünecektir. ne olduğunu not et
görünüm, her birinde 256'dan fazla seçeneği tanımlamanıza izin vermez.
odanın bölümleri. KERNEL_VERSION (majör, minör, sürüm) Bu makro, kernel_version_code oluşturmanıza izin verir.
çekirdek sürümünü oluşturan bireysel öğelerden. Örneğin,
sonraki makro KERNEL_VERSION (2, 3, 48)
131888'e genişler. Bu makro,
çekirdeğin mevcut sürümünün gerekli olanla karşılaştırılması. tekrar tekrar olacağız
kitap boyunca bu makroyu kullanın.

Dosyanın içeriğini verelim linux / sürüm.hçekirdek 2.4.25 için (başlık dosyasının metni tam olarak verilmiştir).

#define UTS_RELEASE "2.4.25" #define LINUX_VERSION_CODE 132121 #define KERNEL_VERSION (a, b, c) (((a)<< 16) + ((b) << 8) + (c))

version.h başlık dosyası module.h dosyasına dahil edilmiştir, bu nedenle genellikle version.h'yi modül kodunuza açıkça eklemeniz gerekmez. Alternatif olarak, bir makro bildirerek version.h başlık dosyasının module.h dosyasına eklenmesini önleyebilirsiniz. __HAYIR_VERSION__... kullanacaksın __HAYIR_VERSION__, örneğin, etkinleştirmeniz gerektiğinde daha sonra tek bir modüle bağlanacak olan birkaç kaynak dosyaya. Duyuru __HAYIR_VERSION__ modül.h başlık dosyasını eklemeden önce engeller
otomatik hat açıklaması __module_kernel_version veya kaynak dosyalardaki eşdeğeri. Bağlayıcı şikayetlerini gidermek için buna ihtiyacınız olabilir: ld -r bağlantı tablolarındaki çoklu sembol açıklamalarını kim sevmez. Genellikle modül kodu, başlık dosyası dahil olmak üzere birden çok kaynak dosyaya bölünürse sonra duyuru __HAYIR_VERSION__ bu dosyalardan biri hariç hepsinde yapılır. Kitabın sonunda, kullanılan bir modül örneği vardır. __HAYIR_VERSION__.

Çekirdek sürümüyle ilişkili bağımlılıkların çoğu, makrolar kullanılarak önişlemci yönergeleri üzerine kurulu mantık kullanılarak ele alınabilir. ÇEKİRDEK SÜRÜMÜ ve LINUX_VERSION_CODE... Ancak, sürüm bağımlılıklarını kontrol etmek, rengarenk yönergeler nedeniyle modül kodunun okunabilirliğini büyük ölçüde karmaşıklaştırabilir. #ifdef... Bu nedenle, belki de en iyi çözüm, bağımlılık kontrolünü ayrı bir başlık dosyasına koymaktır. Bu nedenle örneğimiz başlık dosyasını içerir. sysdep.h sürüm bağımlılığı kontrolleriyle ilişkili tüm makroları içermek için kullanılır.

Sunmak istediğimiz ilk sürüm bağımlılığı hedef bildirimde " kurulum yap"Sürücü derleme betiğimiz. Tahmin edebileceğiniz gibi, kullanılan çekirdek sürümüne göre değişen kurulum dizini, version.h dosyasının görüntülenmesine göre seçilir. İşte dosyadan bir kod parçası. Kurallar.make tüm çekirdek Makefiles tarafından kullanılır.

VERSIONFILE = $ (INCLUDEDIR) /linux/version.h VREION = $ (shell awk -F \ "" / REL / ($$ 2 yazdırın) "$ (VERSIONFILE)) INSTALLDIR = / lib / modüller / $ (VERSION) / çeşitli

Tüm sürücülerimizi yüklemek için misc dizinini kullandığımızı unutmayın (yukarıdaki Makefile örneğindeki INSTALLDIR bildirimi). Çekirdek 2.4'ten başlayarak, bu dizin, özel sürücüleri yerleştirmek için önerilen dizindir. Ayrıca modutils paketinin hem eski hem de yeni sürümleri, arama yollarında bir misc dizini içerir.

Yukarıda verilen INSTALLDIR tanımını kullanarak Makefile içindeki yükleme hedefi şöyle görünebilir:

Kurulum: kurulum -d $ (INSTALLDIR) kurulum -c $ (OBJS) $ (INSTALLDIR)

Platform Bağımlılığı

Her bilgi işlem platformunun, en yüksek performansı elde etmek için çekirdek geliştiricileri tarafından dikkate alınması gereken kendi özellikleri vardır.

Çekirdek geliştiricileri, / PCLASS = "batılı" ve uygulama geliştiricilerinden çok daha fazla seçim ve karar verme özgürlüğüne sahiptir. Her belirli platformdan maksimumu sıkarak kodunuzu optimize etmenize izin veren bu özgürlüktür.

Modül kodu, çekirdek derlenirken kullanılan aynı derleyici seçenekleri kullanılarak derlenmelidir. Bu, hem aynı işlemci kaydı kullanım modellerini kullanmak hem de aynı düzeyde optimizasyon gerçekleştirmek için geçerlidir. Dosya Kurallar.makeçekirdek kaynak ağacının kökünde bulunan , tüm derleme Makefiles'e dahil edilmesi gereken platforma özel tanımları içerir. Tüm platforma özel derleme komut dosyaları Makefile olarak adlandırılır. platform ve geçerli çekirdek yapılandırmasına göre make yardımcı programı için değişkenlerin değerlerini içerir.

Makefile'ın bir başka ilginç özelliği, platformlar arası veya sadece çapraz derleme desteğidir. Bu terim, farklı bir platform için kod derlemeniz gerektiğinde kullanılır. Örneğin, i86 platformunu kullanarak M68000 platformu için kod üreteceksiniz. Çapraz derleme kullanacaksanız, derleme araçlarınızı değiştirmeniz gerekir ( gcc, ld, vb.) başka bir uygun araç seti ile
(Örneğin, m68k-linux-gcc, m68k-linux-ld). Kullanılan önek, $ (CROSS_COMPILE) Makefile değişkeni, make yardımcı programına bir komut satırı parametresi veya bir sistem ortam değişkeni tarafından belirtilebilir.

SPARC mimarisi, Makefile'de uygun şekilde ele alınması gereken özel bir durumdur. SPARC64 (SPARC V9) platformunda çalışan kullanıcı programları, genellikle SPARC32 (SPARC V8) platformu için tasarlanmış ikili dosyalardır. Bu nedenle, SPARC64 platformundaki (gcc) varsayılan derleyici, SPARC32 için nesne kodunu oluşturur. Öte yandan, SPARC V9 üzerinde çalışacak şekilde tasarlanmış bir çekirdek, SPARC V9 için nesne kodunu içermelidir, bu nedenle bile, bir çapraz derleyici gereklidir. SPARC64'ü hedefleyen tüm GNU / Linux dağıtımları, çekirdek derleme betiğinin Makefile'ında seçilmesi gereken uygun bir çapraz derleyici içerir.

Sürüm ve platform bağımlılıklarının tam listesi burada açıklanandan biraz daha karmaşık olsa da, çapraz derleme için yine de yeterlidir. Daha fazla bilgi için Makefile derleme betiklerine ve çekirdek kaynak dosyalarına bakabilirsiniz.

Çekirdek 2.6 özellikleri

Zaman durmuyor. Ve şimdi 2.6 çekirdeğin yeni neslinin ortaya çıkışına tanık oluyoruz. Ne yazık ki, bu kitabın orijinali yeni çekirdeği kapsamamaktadır, bu nedenle çevirmen, çeviriye yeni bilgiler ekleme özgürlüğünü alacaktır.

Gerekli çekirdek sürümüne bağlı olarak modülünüzü doğru şekilde çerçeveleyecek ve derleyecek TimeSys 'TimeStorm gibi IDE'leri kullanabilirsiniz. Tüm bunları kendiniz yazacaksanız, yeni çekirdeğin getirdiği temel farklılıklar hakkında bazı ek bilgilere ihtiyacınız olacak.

2.6 çekirdeğinin özelliklerinden biri, başlatma ve sonlandırma işlevlerinin adlarını açıkça kaydetmek için module_init () ve module_exit () makrolarını kullanma ihtiyacıdır.

2.4 çekirdeğinde tanıtılan MODULE_LISENCE () makrosu, bir modül yüklerken ilgili uyarıları görmek istemiyorsanız yine de gereklidir. Makroya aktarım için aşağıdaki lisans dizelerini seçebilirsiniz: “GPL”, “GPL v2”, “GPL ve ek haklar”, “Dual BSD / GPL” (BSD veya GPL lisansları arasında seçim yapın), “Dual MPL / GPL "( Mozilla veya GPL lisansları arasında seçim) ve
"Tescilli".

Yeni çekirdek için daha önemli olan, yalnızca modülün kodunda değil, aynı zamanda derleme betiğinin Makefile'sinde de değişiklikler gerektiren modülleri derlemek için yeni bir şemadır.

Bu nedenle, MODULE makro sembolünün tanımı artık ne modül kodunda ne de Makefile'de gerekli değildir. Gerekirse, yeni derleme şeması bu makro sembolünü kendisi tanımlayacaktır. Ayrıca, __KERNEL__ makrolarını veya KBUILD_BASENAME ve KBUILD_MODNAME gibi daha yenilerini açıkça tanımlamanız gerekmez.

Ayrıca, derleme zamanı optimizasyon düzeyini (-O2 veya diğerleri) belirtmemelisiniz, çünkü modülünüz, çekirdeğinizdeki diğer tüm modüllerin derlendiği optimizasyon bayrakları da dahil olmak üzere, tüm bu bayrak setleriyle derlenecektir - make yardımcı programı, gerekli tüm bayrak setlerini otomatik olarak kullanacaktır.

Bu nedenlerden dolayı, 2.6 çekirdeği için bir modül derlemek için Makefile çok daha kolaydır. Merhaba.c modülü için Makefile şöyle görünecektir:

Obj-m: = merhaba.o

Ancak modülü derlemek için, geçici dosya ve dizinlerin oluşturulacağı çekirdek kaynak ağacına yazma erişimine ihtiyacınız olacak. Bu nedenle, modülün kaynak kodunu içeren geçerli dizinden verilen modülü 2.6 çekirdeğe derleme komutu şöyle görünmelidir:

# make -C /usr/src/linux-2.6.1 SUBDIRS = `pwd` modülleri

Yani, modülün kaynağına sahibiz merhaba-2.6.c, 2.6 çekirdekte derlemek için:

//hello-2.6.c #include #Dahil etmek #Dahil etmek MODULE_LICENSE ("GPL"); static int __init my_init (void) (printk ("Merhaba dünya \ n"); 0 döndür;); statik void __exit my_cleanup (void) (printk ("Güle güle \ n");); module_init (my_init); module_exit (my_cleanup);

Buna göre, bir Makefile'ımız var:

Obj-m: = merhaba-2.6.o

Makefile'ımızı aşağıdaki parametrelerle işlemek için make yardımcı programını çağırıyoruz:

# make -C / usr / src / linux-2.6.3 SUBDIRS = `pwd` modülleri

Normal derleme işlemi aşağıdaki standart çıktı ile devam edecektir:

Yapın: `/usr/src/linux-2.6.3" dizinini girin *** Uyarı: Komut satırında SUBDIRS'in geçersiz kılınması *** tutarsızlıklarına neden olabilir make: `arch / i386 / kernel / asm-offsets.s" güncelleme gerektiriyor . CHK şunları içerir / asm-i386 / asm_offsets.h CC [M] /home/knz/j.kernel/3/hello-2.6.o Modül oluşturma, 2. aşama /usr/src/linux-2.6.3/scripts/Makefile .modpost: 17: *** Ah-oh, eski modül girişleriniz var. SUBDIRS ile uğraştınız, /usr/src/linux-2.6.3/scripts/Makefile.modpost:18: bir şeyler ters giderse şikayet etmeyin. MODPOST CC /home/knz/j.kernel/3/hello-2.6.mod.o LD [M] /home/knz/j.kernel/3/hello-2.6.ko make: Çıkış dizini `/ usr / src / linux-2.6.3"

Derlemenin sonucu, çekirdeğe kurulabilecek bir hello-2.6.ko modül dosyası olacaktır.

2.6 çekirdeğinde modül dosyalarının son ekinin 2.4 çekirdeğinde olduğu gibi .o değil, .ko olduğunu unutmayın.

Çekirdek Sembol Tablosu

Bir modülü çekirdeğe bağlarken insmod yardımcı programının çekirdeğin genel sembol tablosunu nasıl kullandığından zaten bahsetmiştik. Bu tablo, modüler sürücü değişkenlerini uygulamak için gerekli olan global çekirdek nesnelerinin (fonksiyonlar ve değişkenler) adreslerini içerir. Çekirdeğinizin / proc dosya sistemini desteklemesi koşuluyla, çekirdek genel sembol tablosu / proc / ksyms dosyasından metin biçiminde okunabilir.

2.6 çekirdeğinde / proc / ksyms dosyası / proc / modüller olarak yeniden adlandırılmıştır.

Bir modül yüklendiğinde, modül tarafından dışa aktarılan semboller, çekirdeğin sembol tablosunun bir parçası haline gelir ve / proc / ksyms'den görüntüleyebilirsiniz.

Yeni modüller, modülünüz tarafından dışa aktarılan sembolleri kullanabilir. Bu nedenle, örneğin, msdos modülü, fat modülü tarafından dışa aktarılan sembollere dayanır ve okuma modunda kullanılan her USB cihazı, usbcore ve giriş modüllerinden gelen sembolleri kullanır. Modüllerin sıralı olarak yüklenmesiyle uygulanan bu ilişkiye modül yığını denir.

Modül yığını, karmaşık modül projeleri oluştururken kullanışlıdır. Bu soyutlama, aygıt sürücüsü kodunu donanıma bağlı ve donanımdan bağımsız parçalara ayırmak için kullanışlıdır. Örneğin, linux için video sürücü seti, kullanılan donanımın özelliklerini dikkate alan düşük seviyeli bir sürücü için sembolleri dışa aktaran bir ana modülden oluşur. Yapılandırmanıza göre ana video modülünü ve donanımınıza özel modülü yüklersiniz. Aynı şekilde, paralel bağlantı noktaları ve USB aygıtları gibi çok çeşitli eklenti aygıtları için destek sağlanır. Paralel bağlantı noktası sistem yığını Şekil 2'de gösterilmektedir. 2-2. Oklar, modüller ve çekirdek API arasındaki etkileşimleri gösterir. Etkileşim, hem işlevler düzeyinde hem de işlevler tarafından kontrol edilen veri yapıları düzeyinde gerçekleşebilir.

Şekil 2-2. Paralel bağlantı noktası modülü yığını

Yığınlama modüllerini kullanırken modprobe yardımcı programını kullanmak uygundur. modprobe yardımcı programının işlevselliği birçok yönden insmod yardımcı programına benzer, ancak bir modül yüklendiğinde, temel bağımlılıklarını kontrol eder ve gerekirse, gerekli modül yığını dolana kadar gerekli modülleri yükler. Bu nedenle, tek bir modprobe komutu, insmod komutuna birden fazla çağrı yapılmasına neden olabilir. Modprobe'un insmod üzerinde akıllı bir sarmalayıcı olduğunu söyleyebiliriz. Geçerli dizinden özel modüller yüklemek dışında her yerde insmod yerine modprobe kullanabilirsiniz. modprobe yalnızca özel modül konumlarına bakar ve olası bağımlılıkları karşılayamaz.

Modülleri parçalara bölmek, problem ifadesini basitleştirerek geliştirme süresini azaltmaya yardımcı olur. Bu, Bölüm 1, Aygıt Sürücülerine Giriş'te açıklanan uygulama mekanizması ile kontrol ilkesi arasındaki ayrıma benzer.

Genellikle bir modül, işlevselliğini sembolleri dışa aktarmaya gerek kalmadan uygular. Diğer modüller bundan faydalanabiliyorsa, sembolleri dışa aktarmanız gerekecektir. Statik olmayan simgelerin dışa aktarılmasını önlemek için özel bir yönerge eklemeniz gerekebilir. modutils'in çoğu uygulaması, hepsini varsayılan olarak dışa aktarır.

Linux çekirdek başlıkları, sembollerinizin görünürlüğünü kontrol etmek için uygun bir yol sağlar, böylece çekirdek sembol tablosu ad alanının kirlenmesini önler. Bu bölümde açıklanan mekanizma, 2.1.18 sürümünden beri çekirdeklerde çalışır. 2.0 çekirdeğin tamamen farklı bir kontrol mekanizması vardı
Bu bölümün sonunda açıklanacak olan sembollerin görünürlüğü.

Modülünüzün sembolleri hiç dışa aktarmaması gerekiyorsa, modül kaynak dosyasına aşağıdaki makro çağrısını açıkça yerleştirebilirsiniz:

EXPORT_NO_SYMBOLS;

Linux / module.h dosyasında tanımlanan bu makro çağrısı, bir assembler yönergesine genişletilir ve modülün herhangi bir yerinde belirtilebilir. Ancak farklı kernellere taşınabilir kod oluştururken bu makro çağrısını modülün başlatma fonksiyonuna (init_module) yerleştirmek gerekir, çünkü bu makro tanımının eski sürümler için sysdep.h dosyamızda tanımladığımız versiyonu çekirdeğin sürümleri yalnızca burada çalışacaktır.

Öte yandan, modülünüzden sembollerin belirli bir kısmını dışa aktarmanız gerekiyorsa, o zaman makro sembolünü kullanmanız gerekir.
EXPORT_SYMTAB... Bu makro tanımlanmalıdır önüstbilgi dosyası module.h dahil. Genel kabul görmüş bir uygulamadır
bayrak aracılığıyla bu makro sembolünün tanımı -NS Makefile'da.

makro karakteri ise EXPORT_SYMTAB tanımlandığında, bireysel semboller birkaç makro kullanılarak dışa aktarılabilir:

EXPORT_SYMBOL (isim); EXPORT_SYMBOL_NOVERS (isim);

Bu iki makrodan herhangi biri, verilen sembolü modülün dışında kullanılabilir hale getirecektir. Aradaki fark, makro EXPORT_SYMBOL_NOVERS sürüm bilgisi olmadan bir sembolü dışa aktarır (bkz. bölüm 11 “kmod ve Gelişmiş Modülerleştirme”). Daha fazla bilgi için
başlık dosyasına göz atın , yukarıdakiler pratik kullanım için oldukça yeterli olsa da
makrolar.

Modülleri Başlatma ve Tamamlama

Belirtildiği gibi, init_module () işlevi, bir modülün işlevsel bileşenlerini çekirdeğe kaydeder. Böyle bir kayıttan sonra, modülü kullanan uygulama için, modüle giriş noktaları, çekirdek tarafından sağlanan arayüz üzerinden erişilebilir olacaktır.

Modüller, kaydedildiğinde modül işlevlerinin adları olan birçok farklı bileşeni kaydedebilir. Önerilen işlevselliği uygulayan işlevlere yönelik işaretçiler içeren bir veri yapısına yönelik bir işaretçi, çekirdek kayıt işlevine geçirilir.

Bölüm 1, "Aygıt Sürücülerine Giriş", ana aygıt türlerinin sınıflandırmasından söz etti. Yalnızca burada bahsedilen aygıt türlerini değil, örneğin / proc dosya sistemi dosyaları vb. gibi yazılım soyutlamalarına kadar diğerlerini de kaydedebilirsiniz. Sürücü API'si aracılığıyla çekirdekte çalışabilen herhangi bir şey kaydedilebilir. sürücü olarak kayıtlı.

Çekirdek örneğiniz için kayıtlı sürücü türleri hakkında daha fazla bilgi edinmek isterseniz, çekirdek kaynaklarında EXPORT_SYMBOL alt dizisi için bir arama yapabilir ve çeşitli sürücüler tarafından sunulan giriş noktalarını bulabilirsiniz. Kural olarak, kayıt işlevleri adlarında öneki kullanır. Kayıt ol_,
bu yüzden onları bulmanın bir başka olası yolu da bir alt dizi aramaktır. Kayıt ol_ grep yardımcı programını kullanarak / proc / ksyms dosyasında. Bahsedildiği gibi, 2.6.x çekirdeğinde / proc / ksyms dosyası / proc / modülleri ile değiştirilir.

init_module'de hata işleme

Modülün başlatılması sırasında herhangi bir hata meydana gelirse, modülün yüklenmesini durdurmadan önce yapılmış olan başlatma işlemini geri almalısınız. Örneğin, veri yapıları tahsis edilirken sistemdeki yetersiz bellek nedeniyle bir hata meydana gelebilir. Ne yazık ki, bu olabilir ve iyi kod bu durumları halledebilmelidir.

init_module () başlatma işlevinde hata oluşmadan önce kaydedilen veya tahsis edilen her şey kendi başına iptal edilmeli veya serbest bırakılmalıdır, çünkü Linux çekirdeği başlatma hatalarını izlemez ve modül kodu tarafından halihazırda yürütülen ödünç alma ve kaynak tahsisini geri almaz. Geri almadıysanız veya kaydı geri almadıysanız, çekirdek kararsız bir durumda kalacaktır ve modül yeniden yüklendiğinde
zaten kayıtlı öğeleri yeniden kaydettiremeyeceksiniz ve daha önce yapılmış bir kaydı iptal edemeyeceksiniz, çünkü init_module () işlevinin yeni bir örneğinde, kayıtlı işlevlerin adresleri için doğru değere sahip olmayacaksınız. Sistemi önceki durumuna geri yüklemek, çeşitli karmaşık hileler gerektirecektir ve bu genellikle sistemin basit bir yeniden başlatılmasıyla yapılır.

Modül başlatma hataları durumunda sistemin önceki durumunu geri yükleme uygulaması en iyi şekilde goto ifadesi kullanılarak uygulanır. Genellikle, bu operatöre son derece olumsuz ve hatta nefretle davranılır, ancak bu durumda çok faydalı olduğu ortaya çıkar. Bu nedenle, çekirdekte, goto ifadesi genellikle modül başlatma hatalarını işlemek için kullanılır.

Aşağıdaki basit kod, örnek olarak hayali kayıt ve iptal işlevlerini kullanarak hataları ele almanın bu yolunu gösterir.

Int init_module (void) (int err; / * kayıt bir işaretçi ve bir isim alır * / err = register_this (ptr1, "skull"); if (err) fail_this'e gider; err = register_that (ptr2, "skull"); ise (err) fail_that'a git; err = register_those (ptr3, "skull"); if (err) fail_those'a git; 0 döndür; / * başarı * / fail_those: unregister_that (ptr2, "skull"); fail_that: unregister_this (ptr1, " kafatası "); fail_this: return err; / * hatayı yay * /)

Bu örnek, üç modül bileşenini kaydetmeye çalışır. Goto ifadesi, bir kayıt hatası oluştuğunda kullanılır ve modül yüklenmeyi durdurmadan önce kayıtlı bileşenlerin kaydının silinmesine neden olur.

Goto ifadesinin okunması kolay kod için kullanılmasına başka bir örnek, bir modülün başarıyla tamamlanan kayıt işlemlerini "hatırlama" ve bir hata oluştuğunda bu bilgi iletilerek cleanup_module () öğesini çağırma hilesidir. cleanup_module () işlevi, yürütülen başlatma işlemlerini geri almak için tasarlanmıştır ve modül kaldırıldığında otomatik olarak çağrılır. init_module() işlevi tarafından döndürülen değer,
modül başlatma hata kodunu temsil eder. Linux çekirdeğinde hata kodu, başlık dosyasında yapılan birçok tanımdan negatif bir sayıdır. ... -ENODEV, -ENOMEM, vb. gibi ayrılmış hata kodlarının sembolik anımsatıcılarını kullanmak için bu başlık dosyasını modülünüze ekleyin. Bu tür anımsatıcıları kullanmak iyi bir programlama stili olarak kabul edilir. Ancak, modutils yardımcı programlarının bazı sürümlerinin döndürülen hata kodlarını doğru bir şekilde işlemediği ve “Aygıt meşgul” mesajını görüntülediğine dikkat edilmelidir.
init_modules () tarafından döndürülen bir dizi tamamen farklı hataya yanıt olarak. Paketin son sürümlerinde bu
can sıkıcı hata düzeltildi.

Yukarıdaki durum için cleanup_module () işlev kodu, örneğin şöyle olabilir:

Void cleanup_module (void) (unregister_those (ptr3, "skull"); unregister_that (ptr2, "skull"); unregister_this (ptr1, "skull"); dönüş;)

Başlatma ve sonlandırma kodunuz burada açıklanandan daha karmaşıksa, o zaman goto ifadesinin kullanılması, program kodunun okunması zor olabilir, çünkü sonlandırma kodunun, goto atlamaları için birden çok etiket kullanılarak init_module () işlevinde tekrarlanması gerekir. . Bu nedenle, init_module () işlevinde cleanup_module () işlevine yapılan çağrıyı kullanarak, bir modül yükleme hatası oluştuğunda başarılı başlatma miktarı hakkında bilgi vererek daha zor bir teknik kullanılır.

Aşağıda init_module() ve cleanup_module() öğelerinin bu şekilde nasıl yazıldığına dair bir örnek verilmiştir. Bu örnek, başarılı başlatma miktarı hakkında bilgi sağlamak için genel olarak tanımlanmış işaretçiler kullanır.

Bir şey oluşturun * item1; başka bir şey yap * item2; int malzeme_ok; void cleanup_module (void) (if (item1) release_thing (item1); if (item2) release_thing2 (item2); if (stuff_ok) unregister_stuff(); return;) int init_module (void) (int err = -ENOMEM; item1 = allocate_thing (argümanlar); item2 = allocate_thing2 (argümanlar2); if (! item2 ||! item2) goto fail; err = register_stuff (item1, item2); if (! err) stuff_ok = 1; else goto fail; 0 döndür; / * başarı * / başarısız: cleanup_module (); dönüş hatası;)

Modülünüzün başlatma işlemlerinin karmaşıklığına bağlı olarak, modül başlatma hatalarını kontrol etmek için burada açıklanan yöntemlerden birini kullanabilirsiniz.

Modül kullanım sayacı

Modülün güvenli bir şekilde boşaltılıp boşaltılamayacağını belirlemek için sistem her modül için bir kullanım sayacı içerir. Sistem bu bilgiye ihtiyaç duyar, çünkü modül biri veya bir şeyle meşgulse kaldırılamaz - bu dosya sistemi takılıysa dosya sistemi sürücüsünü kaldıramazsınız veya bazı işlemler bu aygıtı kullanıyorsa karakter aygıtı modülünü kaldıramazsınız. Aksi halde,
bu, sistem çökmesine - segmentasyon hatasına veya çekirdek paniğine neden olabilir.

Modern çekirdeklerde sistem, bir sonraki bölümde inceleyeceğimiz bir mekanizma kullanarak size modül kullanımı için otomatik bir sayaç sağlayabilir. Çekirdek sürümü ne olursa olsun, bu sayacın manuel kontrolünü kullanabilirsiniz. Bu nedenle, çekirdeğin eski sürümlerinde kullanılması gereken kod, aşağıdaki üç makro üzerine kurulu modül kullanım hesaplama modelini kullanmalıdır:

MOD_INC_USE_COUNT Mevcut modülün kullanım sayacını artırır MOD_DEC_USE_COUNT Mevcut modülün kullanım sayacını azaltır MOD_IN_USE Bu modülün kullanım sayacı sıfır ise true döndürür

Bu makrolar şurada tanımlanmıştır: ve doğrudan erişilmesi arzu edilmeyen özel bir dahili veri yapısını manipüle ederler. Gerçek şu ki, bu makroları kullanmak için harici arayüz değişmeden kalırken, bu verilerin iç yapısı ve yönetim şekli sürümden sürüme değişebilir.

kontrol etmeniz gerekmediğini unutmayın MOD_IN_USE cleanup_module() işlevi kodunda, çünkü bu kontrol, kernel / module.c'de tanımlanan sys_delete_module() sistem çağrısında cleanup_module() çağrılmadan önce otomatik olarak yapılır.

Modül kullanım sayacını doğru şekilde yönetmek, sistem kararlılığı için kritik öneme sahiptir. Çekirdeğin kullanılmayan bir modülü herhangi bir zamanda otomatik olarak boşaltmaya karar verebileceğini unutmayın. Modüllerin programlanmasındaki yaygın bir hata, bu sayacın yanlış yönetilmesidir. Örneğin, bir isteğe yanıt olarak, modül kodu bazı eylemler gerçekleştirir ve işlem tamamlandığında modül kullanım sayacını artırır. Onlar. Böyle bir programcı, bu sayacın modül kullanımına ilişkin istatistikleri toplamaya yönelik olduğunu varsayar, oysa aslında, aslında, mevcut modül doluluk için bir sayaçtır, yani. şu anda modül kodunu kullanan işlemlerin sayısını takip eder. Bu nedenle, bir modüle bir istek işlerken, aramanız gerekir MOD_INC_USE_COUNT herhangi bir işlem yapmadan önce ve MOD_DEC_USE_COUNT onları tamamladıktan sonra.

Kullanım sayacının kontrolünü kaybederseniz, bariz nedenlerden dolayı bir modülü boşaltamayacağınız durumlar olabilir. Bu durumla genellikle bir modülün geliştirme aşamasında karşılaşılır. Örneğin, bir NULL işaretçisinin başvurusunu kaldırmaya çalıştığında bir işlem kesintiye uğrayabilir ve kullanım sayacını sıfırlayana kadar böyle bir modülü kaldıramazsınız. Bir modülün hata ayıklama aşamasında bu sorunun olası çözümlerinden biri, geçersiz kılarak modül kullanım sayacının yönetiminden tamamen vazgeçmektir. MOD_INC_USE_COUNT ve MOD_DEC_USE_COUNT boş koda. Başka bir çözüm, modül kullanım sayacını sıfıra zorlamak için bir ioctl() çağrısı yapmaktır. Bunu Bölüm 5, “Gelişmiş Karakter Sürücü İşlemleri”ndeki “ioctl Argümanını Kullanma” bölümünde ele alacağız. Tabii ki, kullanıma hazır bir sürücüde, sayaçla bu tür hileli manipülasyonlar hariç tutulmalıdır, ancak hata ayıklama aşamasında geliştiricinin zamanını korur ve oldukça kabul edilebilir.

Her modül için sistem kullanım sayacının güncel değerini / proc / modüller dosyasındaki her girişin üçüncü alanında bulabilirsiniz. Bu dosya, şu anda yüklü olan modüller hakkında bilgi içerir - her modül için bir satır. Satırın ilk alanı modülün adını içerir, ikinci alan modülün bellekte kapladığı boyut ve üçüncü alan kullanım sayacının mevcut değeridir. Bu bilgiler, biçimlendirilmiş biçimde,
lsmod yardımcı programı çağrılarak elde edilebilir. Aşağıda bir örnek / proc / modül dosyası verilmiştir:

Parport_pc 7604 1 (autoclean) lp 4800 0 (kullanılmayan) parport 8084 1 kilitli 33256 1 (autoclean) sunrpc 56612 1 (autoclean) ds 6252 1 i82365 22304 1 pcmcia_core 41280 0

Burada sisteme yüklenmiş birkaç modül görüyoruz. Bayraklar alanında (satırın son alanı), modül bağımlılık yığını köşeli parantez içinde görüntülenir. Diğer şeylerin yanı sıra, paralel bağlantı noktası modüllerinin, Şekil 2'de gösterildiği gibi modül yığını aracılığıyla iletişim kurduğunu fark edebilirsiniz. 2-2. (Autoclean) bayrağı, kmod veya kerneld tarafından yönetilen modülleri işaretler. Bu, Bölüm 11, “kmod ve Gelişmiş Modülerleştirme”) kapsamında ele alınacaktır. (Kullanılmayan) bayrağı, modülün şu anda kullanılmadığı anlamına gelir. 2.0 çekirdeğinde, boyut alanı bilgileri bayt cinsinden değil, çoğu platform için boyutu 4KB olan sayfalarda görüntülüyordu.

Modül boşaltma

Bir modülü boşaltmak için rmmod yardımcı programını kullanın. Bir modülü boşaltmak, onu dinamik olarak çekirdeğe bağlayan yüklemekten daha basit bir iştir. Bir modül kaldırıldığında, kullanım sayacı sıfırsa, kaldırılan modülün cleanup_module () işlevini çağıran veya bir hatayla sonlanan delete_module () sistem çağrısı yürütülür.

Daha önce de belirtildiği gibi, cleanup_module() işlevinde, modül yüklendiğinde gerçekleştirilen başlatma işlemleri, cleanup_module() işlevi tarafından geri alınır. Ayrıca, modülün dışa aktarılan sembolleri otomatik olarak silinir.

Sonlandırma ve Başlatma İşlevlerinin Açıkça Tanımlanması

Daha önce de belirtildiği gibi, bir modül yüklenirken, çekirdek init_module() işlevini çağırır ve onu boşaltırken cleanup_module() işlevini çağırır. Ancak, çekirdeğin modern sürümlerinde bu işlevlerin genellikle farklı adları vardır. Çekirdek 2.3.23 ile başlayarak, modül yükleme ve boşaltma işlevi için açıkça bir ad tanımlamak mümkün hale geldi. Artık bu işlevleri açıkça adlandırmak için programlama stili önerilir.

Bir örnek verelim. Modülünüzün başlatma işlevi olarak my_init() işlevini ve sırasıyla init_module() ve cleanup_module() yerine my_cleanup() işlevini son işlev olarak bildirmek istiyorsanız, aşağıdaki ikisini eklemeniz gerekir. modül metnine sahip makrolar (genellikle sonuna eklenirler
modül kaynak dosyası):

Module_init (my_init); module_exit (my_cleanup);

Bu makroları kullanmak için modülünüze başlık dosyasını eklemeniz gerekeceğini unutmayın. .

Bu stili kullanmanın rahatlığı, çekirdekteki her modül başlatma ve tamamlama işlevinin hata ayıklamaya büyük ölçüde yardımcı olan kendi benzersiz adına sahip olabilmesidir. Ayrıca, bu işlevlerin kullanımı, sürücü kodunuzu bir modül olarak mı yoksa doğrudan çekirdeğe mi gömeceğinizden bağımsız olarak hata ayıklamayı basitleştirir. Elbette, başlatma ve sonlandırma işlevlerinizin ayrılmış adları varsa, module_init ve module_exit makrolarını kullanmanız gerekmez, ör. sırasıyla init_module() ve cleanup_module().

Çekirdek 2.2 veya sonraki sürümleri için kaynaklara aşina iseniz, başlatma ve tamamlama işlevi için biraz farklı bir açıklama biçimi görebilirsiniz. Örneğin:

Statik int __init my_init (void) (....) static void __exit my_cleanup (void) (....)

Özellik kullanımı __içinde başlatma tamamlandıktan sonra başlatma işlevinin bellekten kaldırılacağı gerçeğine yol açacaktır. Ancak, bu yalnızca yerleşik çekirdek sürücüleri için çalışır ve modüller için yoksayılır. Ayrıca, çekirdeğe yerleşik sürücüler için, öznitelik __çıkış bu öznitelikle işaretlenmiş tüm işlevin yok sayılmasına neden olur. Modüller için bu bayrak da yok sayılır.

Nitelikleri kullanma __içinde(ve __initveri verileri tanımlamak için) çekirdek tarafından kullanılan bellek miktarını azaltabilir. İşaretleme __içinde modülün başlatma işlevi hiçbir fayda veya zarar vermeyecektir. Bu başlatma yolunun kontrolü, gelecekte yapılabilmesine rağmen modüller için henüz uygulanmadı.

Özetleme

Bu nedenle, sunulan materyalin bir sonucu olarak, “Merhaba dünya” modülünün aşağıdaki versiyonunu sunabiliriz:

Modül kaynak dosya kodu =========================================== # Dahil etmek #Dahil etmek #Dahil etmek static int __init my_init_module (void) (EXPORT_NO_SYMBOLS; printk ("<1>Merhaba dünya \ n "); 0 döndür;); statik void __exit my_cleanup_module (void) (printk ("<1>Hoşçakal \ n ");); module_init (my_init_module); module_exit (my_cleanup_module); MODULE_LICENSE (" GPL "); ======================== ======================= Modülü derlemek için Makefile ======================= ====================== CFLAGS = -Wall -D__KERNEL__ -DMODULE -I / lib / modüller / $ (shell uname -r) / build / dahil merhaba. o: ============================================== ===============================

Makefile yazarken, GNU'nun CFLAGS değişkenine ve sistemdeki mevcut derleyiciye dayalı olarak bir nesne dosyasının nasıl oluşturulacağını bağımsız olarak belirleyebileceği kuralı kullandık.

Kaynak kullanımı

Bir modül, bellek, G/Ç bağlantı noktaları, G/Ç belleği, kesme hatları ve DMA kanalları gibi sistem kaynaklarını kullanmadan görevini tamamlayamaz.

Bir programcı olarak yığın yönetimine zaten aşina olmalısınız. Çekirdekteki yığın yönetimi temelde farklı değildir. Programınız işlevi kullanarak bellek alabilir kmalloc () ve onu özgür bırak ücretsiz ()... Bu işlevler, kmalloc () işlevine ek bir öncelik argümanı iletilmesi dışında, tanıdık malloc () ve free () işlevlerine çok benzer. Genellikle öncelik GFP_KERNEL veya GFP_USER'dir. GFP, "ücretsiz sayfa al"ın kısaltmasıdır - ücretsiz sayfa alın. Çekirdek yığın yönetimi Bölüm 7, “Bellek Elde Etme”de ayrıntılı olarak açıklanmıştır.

Acemi bir sürücü geliştiricisi, G / Ç bağlantı noktalarını, G / Ç belleğini ve kesme hatlarını açıkça tahsis etme ihtiyacına şaşırabilir. Ancak o zaman çekirdek modülü bu kaynaklara kolayca erişebilir. Sistem belleği herhangi bir yerden tahsis edilebilmesine rağmen, G/Ç belleği, portlar ve kesme hatları özel bir rol oynar ve farklı şekilde tahsis edilir. Örneğin, bir sürücünün belirli bağlantı noktalarını tahsis etmesi gerekir,
her şey, ama cihazı kontrol etmesi gerekenler. Ancak sürücü, başka biri tarafından kullanılmadıklarından emin olana kadar bu kaynakları kullanamaz.

Bir çevre birimine ait bellek alanına, basitçe bellek olarak adlandırılan sistem RAM'inden (RAM) ayırt etmek için genellikle G / Ç belleği denir.

G / Ç bağlantı noktaları ve bellek

Tipik bir sürücünün işi çoğunlukla okuma ve yazma portları ve G/Ç belleğinden oluşur. Bağlantı noktaları ve G/Ç belleği topluca G/Ç bölgesi (veya alanı) olarak adlandırılır.

Ne yazık ki her bus mimarisi, her cihaza ait G/Ç bölgesini net olarak tanımlayamıyor ve sürücünün kendisine ait bölgenin konumunu tahmin etmesi, hatta olası adres boşluklarının okuma/yazma işlemlerini denemesi gerekebiliyor. . Bu sorun özellikle
Kişisel bir bilgisayara basit aygıtları kurmak için hala kullanılan ve endüstriyel dünyada PC / 104'ün uygulanmasında çok popüler olan ISA veri yolunu ifade eder (Bölüm 15'teki “PC / 104 ve PC / 104 +” bölümüne bakın) “Çevresel veri yollarına genel bakış”).

Bir donanım aygıtını bağlamak için hangi veri yolu kullanılırsa kullanılsın, sürücüler arasındaki çarpışmaları önlemek için aygıt sürücüsünün G / Ç bölgesine özel erişimi garanti edilmelidir. Bir modül, kendi cihazına atıfta bulunarak kendisine ait olmayan bir cihaza yazarsa, bu ölümcül sonuçlara yol açabilir.

Linux geliştiricileri, temel olarak farklı cihazlar arasındaki çarpışmaları önlemek için G/Ç bölgeleri için bir istek/bırakma mekanizması uygulamışlardır. Bu mekanizma uzun süredir I/O portları için kullanılmaktadır ve son zamanlarda genel olarak bir kaynak yönetim mekanizmasına genelleştirilmiştir. Bu mekanizmanın bir yazılım soyutlaması olduğunu ve donanım özelliklerini kapsamadığını unutmayın. Örneğin donanım seviyesinde I/O portlarına yetkisiz erişim, donanım kaynaklarını tahsis etmediği ve yetkilendirmediği için “segmentasyon hatası” benzeri bir hataya neden olmaz.

Kayıtlı kaynaklarla ilgili bilgiler / proc / ioports ve / proc / iomem dosyalarında metin biçiminde bulunur. Bu bilgi Linux'ta çekirdek 2.3'ten beri sağlanmaktadır. Bu kitabın öncelikle 2.4 çekirdeğe odaklandığını ve bölümün sonunda uyumluluk notlarının verileceğini hatırlayın.

Limanlar

/ proc / ioports dosyasının tipik içeriği aşağıdadır:

0000-001f: dma1 0020-003f: pic1 0040-005f: zamanlayıcı 0060-006f: klavye 0080-008f: dma sayfa kaydı 00a0-00bf: pic2 00c0-00df: dma2 00f0-00ff: fpu 0170-0177: ide1 01f0-01f7 : ide0 02f8-02ff: seri (set) 0300-031f: NE2000 0376-0376: ide1 03c0-03df: vga + 03f6-03f6: ide0 03f8-03ff: seri (set) 1000-103f: Intel Corporation 82371AB PIIX4 ACPI 1000- 1003 : acpi 1004-1005: acpi 1008-100b: acpi 100c-100f: acpi 1100-110f: Intel Corporation 82371AB PIIX4 IDE 1300-131f: pcnet_cs 1400-141f: Intel Corporation 82371AB PIIX4 ACPI 1800-18ff: PCI CardBus # 02 1c00 - 1cff: PCI CardBus # 04 5800-581f: Intel Corporation 82371AB PIIX4 USB d000-dff: PCI Bus # 01 d000-d0ff: ATI Technologies Inc 3D Rage LT Pro AGP-133

Bu dosyanın her satırı, bir sürücü veya aygıt sahibiyle ilişkili bağlantı noktası aralığını onaltılı olarak görüntüler. Çekirdeğin önceki sürümlerinde, bağlantı noktası hiyerarşisinin görüntülenmemesi dışında dosya aynı biçime sahiptir.

Dosya, sisteme yeni bir cihaz eklerken bağlantı noktası çarpışmalarını önlemek için kullanılabilir. Bu, özellikle jumper'ları (jamper'lar) değiştirerek kurulu ekipmanı manuel olarak yapılandırırken kullanışlıdır. Bu durumda kullanıcı, kullanılan bağlantı noktalarının listesini kolayca görüntüleyebilir ve kurulu cihaz için boş bir aralık seçebilir. Ve çoğu modern cihaz manuel jumper kullanmasa da, yine de küçük ölçekli bileşenlerin imalatında kullanılmaktadır.

Daha da önemlisi, / proc / ioports dosyasıyla ilişkili programlı olarak erişilebilir bir veri yapısı vardır. Bu nedenle, aygıt sürücüsü başlatıldığında, işgal edilen G / Ç bağlantı noktası aralığını bulabilir. Bu, yeni bir cihaz aramak için portları taramak gerekirse, sürücünün yabancı cihazlar tarafından işgal edilen portlara yazma durumundan kaçınabileceği anlamına gelir.

ISA veri yolunu taramanın riskli bir görev olduğu bilinmektedir. Bu nedenle, resmi Linux çekirdeği ile dağıtılan bazı sürücüler, modülü yüklerken bu taramadan kaçınır. Böylece diğer ekipmanların kullandığı portlara yazarak çalışan sisteme zarar verme riskini ortadan kaldırırlar. Neyse ki, modern otobüs mimarileri bu problemlere karşı bağışıktır.

G/Ç kayıtlarına erişmek için kullanılan programlama arabirimi aşağıdaki üç işlevden oluşur:

Int check_region (imzasız uzun başlangıç, imzasız uzun len); struct source * request_region (imzasız uzun başlangıç, imzasız uzun len, char * ad); void release_region (imzasız uzun başlangıç, imzasız uzun len);

İşlev kontrol_bölgesi () belirtilen port aralığının meşgul olup olmadığını kontrol etmek için çağrılabilir. Yanıt olumsuzsa, olumsuz bir hata kodu (-EBUSY veya -EINVAL gibi) döndürür.

İşlev request_region () belirtilen adres aralığının tahsisini gerçekleştirir ve başarı durumunda boş olmayan bir işaretçi döndürür. Sürücünün döndürülen işaretçiyi saklaması veya kullanması gerekmez. Tek yapmanız gereken NULL olup olmadığını kontrol etmek. Yalnızca çekirdek 2.4 (veya üstü) ile çalışması gereken kodun check_region () öğesini çağırması gerekmez. Hiç şüphe yok ki bu dağıtım yönteminin avantajı,
check_region() ve request_region() çağrıları arasında ne olabileceği bilinmiyor. Çekirdeğin eski sürümleriyle uyumluluğu korumak istiyorsanız, check_region() öğesini request_region() öğesinden önce çağırmak gereklidir.

İşlev yayın_bölgesi () sürücü daha önce kullanılan bağlantı noktalarını serbest bıraktığında çağrılmalıdır.

request_region () tarafından döndürülen gerçek işaretçi değeri, yalnızca çekirdekte çalışan kaynak ayırıcı tarafından kullanılır.

Bu üç işlev aslında içinde tanımlanan makrolardır. .

Aşağıda, bağlantı noktalarını kaydetmek için kullanılan çağrı sırasını kullanma örneği verilmiştir. Örnek, kafatası sürücü kodundan alınmıştır. (Skull_probe_hw () işlevinin kodu, donanıma bağlı kod içerdiğinden burada gösterilmez.)

#Dahil etmek #Dahil etmek static int skull_detect (işaretsiz int bağlantı noktası, işaretsiz int aralığı) (int hata; if ((err = kontrol_bölgesi (bağlantı noktası, aralık))< 0) return err; /* busy */ if (skull_probe_hw(port,range) != 0) return -ENODEV; /* not found */ request_region(port,range,"skull"); /* "Can"t fail" */ return 0; }

Bu örnek, önce gerekli bağlantı noktası aralığının kullanılabilirliğini kontrol eder. Bağlantı noktaları mevcut değilse, donanıma erişmek mümkün değildir.
Cihazın gerçek port konumları taranarak doğrulanabilir. Bu örnekte request_region () işlevi,
başarısızlıkla sonuçlanacaktır. Çekirdek aynı anda birden fazla modül yükleyemez, bu nedenle bağlantı noktası çakışması olmaz.
zorunlu.

Sürücü tarafından tahsis edilen tüm G/Ç bağlantı noktaları daha sonra serbest bırakılmalıdır. Kafatası sürücümüz bunu cleanup_module () işlevinde yapar:

Statik void skull_release (imzasız int bağlantı noktası, imzasız int aralığı) (release_region (bağlantı noktası, aralık);)

Kaynak isteme/serbest bırakma mekanizması, modülleri kaydetme/kayıt silme mekanizmasına benzer ve yukarıda açıklanan goto ifadesine dayalı olarak mükemmel bir şekilde uygulanır.

Hafıza

G/Ç bellek bilgilerine /proc/iomem dosyası üzerinden ulaşılabilir. Aşağıda, kişisel bir bilgisayar için böyle bir dosyanın tipik bir örneği verilmiştir:

00000000-0009fbff: Sistem RAM'i 0009fc00-0009ffff: ayrılmış 000a0000-000bffff: Video RAM alanı 000c0000-000c7fff: Video ROM 000f0000-000fffff: Sistem ROM'u 00100000-03feffff: Sistem RAM'i 00100000-0022c557: Çekirdek kodu 00ff5522c:258-002f Çekirdek verisi 2000ff5522c:258-002 : Intel Corporation 440BX / ZX - 82443BX / ZX Ana bilgisayar köprüsü 68000000-68000fff: Texas Instruments PCI1225 68001000-68001fff: Texas Instruments PCI1225 (# 2) e0000000-e3ffffff: PCI Bus # 01 e4000000-e7ffffff: PCI Bus # 01 e4000000-eff : ATI Technologies Inc 3D Rage LT Pro AGP-133 e6000000-e6000fff: ATI Technologies Inc 3D Rage LT Pro AGP-133 fffc0000-ffffffff: ayrılmış

Adres aralıkları için değerler onaltılık gösterimde gösterilir. Her adres aralığı için sahibi gösterilir.

G/Ç bellek erişiminin kaydedilmesi, G/Ç bağlantı noktalarının kaydedilmesiyle benzerdir ve çekirdekte aynı mekanizma üzerine kuruludur.

Gerekli G / Ç bellek adresleri aralığını almak ve serbest bırakmak için sürücü aşağıdaki çağrıları kullanmalıdır:

Int check_mem_region (imzasız uzun başlangıç, imzasız uzun len); int request_mem_region (imzasız uzun başlangıç, imzasız uzun len, char * isim); int release_mem_region (imzasız uzun başlangıç, imzasız uzun len);

Genellikle sürücü, G / Ç bellek adresleri aralığını bilir, bu nedenle bu kaynak için ayırma kodu, bağlantı noktası aralığını ayırma örneğine kıyasla azaltılabilir:

if (check_mem_region (mem_addr, mem_size)) (printk ("sürücü adı: bellek zaten kullanımda \ n"); return -EBUSY;) request_mem_region (mem_addr, mem_size, "sürücü adı");

Linux 2.4'te Kaynak Tahsisi

Mevcut kaynak tahsis mekanizması Linux 2.3.11 çekirdeğinde tanıtıldı ve sistem kaynaklarını yönetmek için esnek erişim sağlıyor. Bu bölüm kısaca bu mekanizmayı açıklamaktadır. Ancak, temel kaynak ayırma işlevleri (istek_bölgesi (), vb. gibi) hala makro olarak uygulanmaktadır ve çekirdeğin önceki sürümleriyle geriye dönük uyumluluk için kullanılır. Çoğu durumda, gerçek dağıtım mekanizması hakkında hiçbir şey bilmenize gerek yoktur, ancak daha karmaşık sürücüler oluştururken ilginç olabilir.

Linux'ta uygulanan kaynak yönetim sistemi, rastgele kaynakları tek tip hiyerarşik bir şekilde yönetebilir. Küresel sistem kaynakları (örneğin, G / Ç bağlantı noktaları), örneğin bir donanım veri yolu yuvasıyla ilişkili olanlar gibi alt kümelere bölünebilir. Bazı sürücüler, istenirse, mantıksal yapılarına göre yakalanan kaynakları da alt bölümlere ayırabilir.

Tahsis edilen kaynakların aralığı, başlık dosyasında bildirilen yapı kaynak yapısı aracılığıyla tanımlanır. :

Yapı kaynağı (const char * isim; imzasız uzun başlangıç, bitiş; imzasız uzun bayraklar; yapı kaynağı * ebeveyn, * kardeş, * çocuk;);

Küresel (kök) kaynak aralığı, önyükleme sırasında oluşturulur. Örneğin, G/Ç portlarını açıklayan bir kaynak yapısı aşağıdaki gibi oluşturulur:

Struct kaynağı ioport_resource = ("PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO);

Sıfırdan IO_SPACE_LIMIT'e kadar olan adres aralığını kapsayan PCI IO adlı bir kaynağı tanımlar. Bu değişkenin değeri kullanılan platforma bağlıdır ve 0xFFFF (x86, IA-64, Alpha, M68k ve MIPS mimarileri için 16 bit adres alanı), 0xFFFFFFFF (SPARC, PPC için 32 bit alan, SH) veya 0xFFFFFFFFFFFFFFFF (64-bit, SPARC64).

Bu kaynağın alt aralıkları, allocate_resource() çağrılarak oluşturulabilir. Örneğin, PCI veri yolunun başlatılması sırasında, bu veri yolunun adres bölgesi için fiziksel bir cihaza atanan yeni bir kaynak oluşturulur. PCI veri yolu yöneticisi çekirdek kodu, bağlantı noktası ve bellek atamalarını işlerken, yalnızca bu bölgeler için yeni bir kaynak oluşturur ve bunları ioport_resource() veya iomem_resource() çağrılarını kullanarak tahsis eder.

Sürücü daha sonra bir kaynağın alt kümesini (genellikle küresel bir kaynağın parçası) talep edebilir ve onu meşgul olarak işaretleyebilir. Kaynak, istenen kaynağı tanımlayan yeni bir yapı kaynağına bir işaretçi veya bir hata durumunda NULL döndüren request_region() çağrılarak yakalanır. Bu yapı, küresel kaynak ağacının bir parçasıdır. Daha önce de belirtildiği gibi, kaynağı elde ettikten sonra sürücünün bu işaretçinin değerine ihtiyacı yoktur.

İlgilenen okuyucu, çekirdek kaynakları dizininde bulunan kernel / resource.c dosyasında bu kaynak yönetimi şemasının ayrıntılarını görmekten keyif alabilir. Ancak, zaten belirtilen bilgiler çoğu geliştirici için yeterli olacaktır.

Kaynak tahsisinin katmanlama mekanizması çifte fayda sağlar. Bir yandan, çekirdek veri yapılarının görsel bir temsilini sağlar. Tekrar / proc / ioports dosyası örneğine bakalım:

E800-e8ff: Adaptec AHA-2940U2 / W / 7890 e800-e8be: aic7xxx

e800-e8ff serisi, kendisini bir PCI veri yolu sürücüsü olarak tanımlayan bir Adaptec adaptörüne ayrılmıştır. Bu aralığın çoğu aic7xxx sürücüsü tarafından istendi.

Bu tür kaynak yönetiminin bir başka avantajı, adres alanının, ekipmanın fiili ara bağlantısını yansıtan alt bantlara bölünmesidir. Kaynak yöneticisi, arızalı bir sürücünün yüklenmesini engelleyebilecek çakışan adres alt aralıklarını tahsis edemez.

Otomatik ve manuel konfigürasyon

Sürücünün ihtiyaç duyduğu bazı parametreler sistemden sisteme değişiklik gösterebilir. Örneğin, sürücünün geçerli G/Ç adresleri ve bellek aralıkları hakkında bilgi sahibi olması gerekir. Bu, iyi organize edilmiş veri yolu arabirimleri için bir sorun değildir. Ancak bazen, kendi cihazını bulmasına yardımcı olmak veya bazı işlevlerini etkinleştirmek/devre dışı bırakmak için sürücüye parametreler iletmeniz gerekir.

Sürücünün çalışmasını etkileyen bu parametreler cihaza bağlıdır. Örneğin, bu, kurulu bir cihazın sürüm numarası olabilir. Elbette bu bilgiler, sürücünün cihazla doğru bir şekilde çalışması için gereklidir. Bu tür parametrelerin tanımı (sürücü yapılandırması) yeterlidir
sürücü başlatıldığında gerçekleştirilen zor bir görev.

Genellikle, bu parametre için doğru değerleri almanın iki yolu vardır - ya kullanıcı bunları açıkça tanımlar ya da sürücü, ekipmanı sorgulamaya dayanarak bunları bağımsız olarak belirler. Bir aygıtı otomatik olarak algılamak, şüphesiz bir sürücüyü yapılandırmak için en iyi çözüm olsa da,
özel yapılandırmanın uygulanması çok daha kolaydır. Sürücü geliştirici, mümkün olan her yerde sürücü otomatik yapılandırmasını uygulamalıdır, ancak aynı zamanda kullanıcıya manuel bir yapılandırma mekanizması sağlamalıdır. Elbette, manuel konfigürasyon, otomatik konfigürasyona göre öncelikli olmalıdır. Geliştirmenin ilk aşamalarında, kural olarak, parametrelerin sürücüye yalnızca manuel aktarımı uygulanır. Otomatik yapılandırma, mümkünse daha sonra eklenir.

Birçok sürücü, yapılandırma parametreleri arasında, sürücünün işlemlerini kontrol eden parametrelere sahiptir. Örneğin, Integrated Device Electronics (IDE) arabirim sürücüleri, kullanıcının DMA işlemlerini kontrol etmesine olanak tanır. Bu nedenle, sürücünüz donanımı otomatik olarak algılama konusunda iyi bir iş çıkarıyorsa, sürücünün işlevselliği üzerinde kullanıcıya kontrol vermek isteyebilirsiniz.

Modül yükleme sırasında insmod veya modprobe komutları ile parametre değerleri geçilebilir. Son zamanlarda, bir konfigürasyon dosyasından (genellikle /etc/modules.conf) parametrelerin değerini okumak mümkün hale geldi. Tamsayı ve dize değerleri parametre olarak iletilebilir. Bu nedenle, skull_ival parametresi için bir tamsayı değeri ve skull_sval parametresi için bir dize değeri iletmeniz gerekiyorsa, bunları modül yüklemesi sırasında insmod komutunun ek parametreleriyle iletebilirsiniz:

Insmod skull skull_ival = 666 skull_sval = "canavar"

Ancak insmod, modül parametrelerinin değerlerini değiştirmeden önce modülün bu parametreleri kullanılabilir hale getirmesi gerekir. Parametreler, module.h başlık dosyasında tanımlanan MODULE_PARM makrosu kullanılarak bildirilir. MODULE_PARM makrosu iki parametre alır: bir değişken adı ve türünü tanımlayan bir dize. Bu makro tanımı, herhangi bir işlevin dışına yerleştirilmelidir ve genellikle dosyanın başında, değişkenlerin tanımından sonra bulunur. Dolayısıyla, yukarıda bahsedilen iki parametre aşağıdaki gibi bildirilebilir:

Int skull_ival = 0; karakter * kafatası_sval; MODULE_PARM (skull_ival, "i"); MODULE_PARM (skull_sval, "s");

Şu anda beş tür modül parametresi desteklenmektedir:

  • b - bir baytlık değer;
  • h - (kısa) iki baytlık değer;
  • ben - bütün;
  • l - uzun tam sayı;
  • s - dize (karakter *);

Dize parametreleri durumunda, modülde bir işaretçi (char *) bildirilmelidir. insmod komutu, geçirilen dize için bellek ayırır ve onu gerekli değerle başlatır. MODULE_PARM makrosunu kullanarak parametre dizilerini başlatabilirsiniz. Bu durumda, tür harfinden önceki tam sayı, dizinin uzunluğunu belirler. Bir tire ile ayrılmış iki tamsayı belirtirken, iletilecek minimum ve maksimum değer sayısını belirlerler. Bu makronun çalışmasının daha ayrıntılı anlaşılması için başlık dosyasına bakın. .

Örneğin, parametre dizisinin en az iki ve en az dört tam sayı değeriyle başlatılması gerektiğini varsayalım. O zaman şu şekilde tarif edilebilir:

Int kafatası_dizisi; MODULE_PARM (skull_array, "2-4i");

Ek olarak, programcının araç takımı, aktarılan modül parametreleri hakkında yorum yapmanıza olanak sağlayan MODULE_PARM_DESC makro tanımını içerir. Bu yorumlar modül nesne dosyasına kaydedilir ve örneğin objdump yardımcı programı veya otomatik sistem yönetim araçları kullanılarak görüntülenebilir. Bu makro tanımını kullanmanın bir örneği:

Int base_port = 0x300; MODULE_PARM (temel_port, "i"); MODULE_PARM_DESC (base_port, "Temel G / Ç bağlantı noktası (varsayılan 0x300)");

Modülün tüm parametrelerinin varsayılan değerlere sahip olması istenir. Bu değerlerin insmod ile değiştirilmesi sadece gerekli durumlarda istenmelidir. Modül, mevcut değerlerini varsayılan değerlerle kontrol ederek parametrelerin açık ayarını kontrol edebilir. Ardından, aşağıdaki şemaya dayalı olarak bir otomatik yapılandırma mekanizması uygulayabilirsiniz. Parametre değerleri varsayılan değerlere sahipse, otomatik yapılandırma gerçekleştirilir. Aksi takdirde mevcut değerler kullanılır. Bu şemanın çalışması için varsayılan değerlerin olası gerçek dünya sistem konfigürasyonlarından hiçbiriyle eşleşmemesi gerekir. Daha sonra bu değerlerin kullanıcı tarafından manuel konfigürasyonda ayarlanamayacağı varsayılabilir.

Aşağıdaki örnek, kafatası sürücüsünün aygıt bağlantı noktası adres alanını nasıl otomatik olarak algıladığını gösterir. Yukarıdaki örnekte, otomatik algılama, birden çok aygıta göz atmayı kullanırken, manuel yapılandırma, sürücüyü bir aygıtla sınırlar. G / Ç bağlantı noktalarını açıklayan bölümde daha önce skull_detect işlevini gördünüz. Skull_init_board () işlevinin uygulanması gösterilmemiştir, çünkü
donanıma bağlı başlatma gerçekleştirir.

/ * * bağlantı noktası aralıkları: cihaz, 0x10'luk adımlarla * 0x280 ve 0x300 arasında bulunabilir. 0x10 portları kullanır. * / #define SKULL_PORT_FLOOR 0x280 #define SKULL_PORT_CEIL 0x300 #define SKULL_PORT_RANGE 0x010 / * * insmod tarafından "skull_port_base"e belirli bir * değeri atanmadıysa, aşağıdaki işlev otomatik algılama gerçekleştirir * / static int skull_port_base = 0; / * 0 otomatik algılamayı zorlar * / MODULE_PARM (skull_port_base, "i"); MODULE_PARM_DESC (skull_port_base, "Kafatası için Temel G/Ç bağlantı noktası"); static int skull_find_hw (void) / * cihaz sayısını döndürür * / (/ * base, yükleme süresi değeri veya ilk denemedir * / int base = skull_port_base? skull_port_base: SKULL_PORT_FLOOR; int sonuç = 0; / * birinci döngü değer atanmışsa zaman; otomatik algılama varsa hepsini deneyin * / do (if (skull_detect (taban, SKULL_PORT_RANGE) == 0) (skull_init_board (taban); sonuç ++;) taban + = SKULL_PORT_RANGE; / * bir sonraki denemeye hazırlanın * / ) while (skull_port_base == 0 && base< SKULL_PORT_CEIL); return result; }

Konfigürasyon değişkenleri yalnızca sürücü içinde kullanılıyorsa (yani, çekirdek sembol tablosunda yayınlanmazlarsa), programcı değişken adlarında önekler kullanmayarak (bizim durumumuzda, kafatası_ öneki) kullanıcının hayatını biraz basitleştirebilir. . Kullanıcı için bu ön ekler çok az şey ifade eder ve bunların yokluğu klavyeden komut yazmayı kolaylaştırır.

Eksiksiz olması için, nesne dosyasına bazı yorumlar eklemenize izin veren üç makronun daha tanımını sağlayacağız.

MODULE_AUTHOR (isim) Nesne dosyasına yazarın adıyla bir dize yerleştirir. MODULE_DESCRIPTION (azalan) Bir nesne dosyasına bir modül için genel bir açıklama satırı yerleştirir. MODULE_SUPPORTED_DEVICE (geliştirici) Modül tarafından desteklenen aygıtı tanımlayan bir dize yerleştirir. V
Linux: Eksiksiz Kılavuz Kolisnichenko Denis Nikolaevich

28.2. Modül derleme

28.2. Modül derleme

Module.c dosyasını derleyeceğiz. Bunun için kurulu bir gcc derleyicisi, başlık dosyaları ve çekirdek kaynakları gerekir. Kitabı bu bölümden önce okuduysanız, paketleri zaten kurmuş olmalısınız:

1. cpp - cpp önişlemcisi;

2. binutils - bir dizi çeşitli yardımcı program (as, gprof, ld);

3.glibc-kerheaders - çekirdek başlıkları;

4. glibc-devel - standart C kitaplığını kullanarak uygulama geliştirmek için yardımcı dosyalar;

5.gcc, gcc derleyicisidir.

Linux çekirdeğinin kaynakları olan çekirdek kaynak paketini kurmaya devam ediyor. Ayrıca, çekirdeğinizin dinamik olarak yüklenebilir modülleri desteklediğinden emin olmanız gerekir (bölüm 20.3.2.3). Eğer seçenek Yüklenebilir modül desteğini etkinleştir devre dışı bırakıldı, etkinleştirmeniz, çekirdek yapılandırma dosyasını kaydetmeniz ve çekirdeği yeniden derlemeniz gerekir.

gcc derleyicisinin birçok seçenekle çağrılması gerekiyor, bu yüzden işimizi kolaylaştırmak için bir makefile yazacağız (bölüm 21.2):

Listeleme 28.5. Modülü oluşturmak için Makefile

YOL = / usr / dahil /usr/src/linux-2.4/include

MODFLAGS: = -O3 -Wall -DLINUX -D__KERNEL__ -I $ (YOL)

modül.o: modül.c

$ (CC) $ (MODFLAGS) -c modülü.c

Derleyici seçenekleri şu anlama gelir:

O3: üçüncü optimizasyon seviyesi kullanılacaktır (bunun ne olduğunu, yardım sisteminde öğreneceksiniz) gcc: adam gcc);

Duvar: tüm uyarıları açın;

DLINUX: Linux için kod oluşturma;

I $ (PATH): başlık dosyaları için arama yolunu tanımlar. Varsayılan olarak, derleyici /usr / include dizinindeki başlık dosyalarını arar, ancak orada olmayabilir. Örneğin, ALT Linux dağıtımı için (çekirdek 2.4.21), başlık dosyaları /usr/include/linux-2.4.21rel-std-up dizininde bulunur.

makefile dosyasını module.c ile aynı dizine yerleştirin ve make dosyasını çalıştırın. Tamamladıktan sonra, aynı dizinde bulunacak olan bir module.o dosyası alacaksınız.

# insmod modülü.o

Modülüm: Başlıyor ... mesajını göreceksiniz. Aynı mesaj log dosyasına / var / log / mesajlara yazılacaktır.

C++ kitabından tarafından Hill Murray

1.1.2 Derleme cout çıktı akışı ve "" çıktı işlemi için kod nereden geldi? Yürütülebilir kod almak için C++ ile yazılmış bir programın derlenmesi gerekir. Özünde, derleme işlemi C ile aynıdır ve girdilerin çoğu

Fedora 8 Kullanıcı Kılavuzundan yazar

3.4.3. Derleme Kural olarak, programların kaynak kodları "çift uzantılı" -.tar.gz ile bir arşiv şeklinde dağıtılır. Kaynak kodunu / usr / src dizinine açmak gelenekseldir. Bu nedenle arşivi açmak için aşağıdaki komutları çalıştırmanız gerekir: sucd / usr / srcgunzip arşivi.tar.gztar xvf

Kullanıcı için Linux kitabından yazar Kostromin Viktor Alekseevich

En İyi 200 Linux Yazılımından yazar Yaremchuk Sergey Akimoviç

17.5.6. Modülleri Derleme Bazı sürücüleri ayrı modüller olarak yapılandırdıysanız (bazı soruları cevaplarken "m" seçeneğini seçtiniz), şimdi ayrıca make modülleri komutunu ve ardından ayrıca make module_install komutunu da çalıştırmalısınız. Documentation / module.txt dosyasında şunları yapabilirsiniz:

C# 2005 Programlama Dili ve .NET 2.0 Platformu kitabından. yazar Troelsen Andrew

Programları Derleme Halihazırda derlenmiş programlar olan paketlerin ortaya çıkmasından sonra bile, derleme uzun bir süre kaldı ve bazıları için kurulumun birincil yolu olmaya devam ediyor. Not İlk önceden derlenmiş kitler,

Asterisk ™ kitabından: Telefonun Geleceği, İkinci Baskı yazar Meggelen Jim Wan

Koşullu Derleme Başka bir önişlemci yönergeleri paketi (#if, #elif, #else, #endif), önceden tanımlanmış sembollere dayalı olarak bir kod bloğunu koşullu olarak derlemenize izin verir. Bu direktiflerin klasik kullanım durumu blok tanımlamadır.

Linux Ağı kitabından yazar Smith Roderick W.

Kişisel Bilgisayar için C Programlama Dili kitabından yazar Bochkov S.O.

libpri derleme libpri kitaplıkları, gerekli olmadığı için yapı ortamını veya yapı bileşeni seçicisini kurmak için autoconf kullanmaz; böylece kurulum basitleştirilmiştir. libpri, çeşitli donanım üreticileri tarafından kullanılır

Linux kitabından: Tam Kılavuz yazar Kolisnichenko Denis Nikolaevich

Asterisk'in Derlenmesi Zaptel ve libpri paketlerini derleyip kurduktan sonra (gerekirse), Asterisk kurulumuna geçebilirsiniz. Bu bölüm, standart bir kurulumu kapsar ve bazı alternatif oluşturma argümanları sunar.

Örnekle Linux Programlama kitabından yazar Robbins Arnold

Çekirdeğin Derlenmesi Bu bölümün başındaki make xconfig veya diğer komutu çalıştırarak sisteminizin çekirdeğini yapılandırdıktan sonra, çekirdeği derlemeli ve modüllerini kurmalısınız. Bunu yapmak için aşağıdaki komutları çalıştırın: # make dep # make bzImage # make modülleri # make

C Dili - Yeni Başlayanlar Kılavuzu kitabından Prata Stephen tarafından

Koşullu Derleme Bu bölüm, koşullu derlemeyi kontrol eden yönergeleri açıklar. Bu yönergeler, koşulları (sürekli) kontrol ederek kaynak dosyanın herhangi bir bölümünü derleme işleminden çıkarmanıza izin verir.

Bir bilgisayar korsanının gözünden Linux kitabından yazar Flenov Mihail Evgenievich

20.5. Çekirdeğin Derlenmesi 20.5.1. Çekirdeği neden güncelleyelim? Linux, diğer tüm işletim sistemlerinden daha hızlı büyüyor. Çekirdeğin yeni işlevleri uygulayan yeni sürümleri düzenli olarak görünür. Örneğin, 2.6.11 çekirdeğindeki Fedora Core 4 dağıtımının çıkması için zar zor zaman vardı ve www.kernel.org'un zaten kararlı bir sürümü var.

UNIX İşletim Sistemi kitabından yazar Robaçevski Andrey M.

15.2. Hata Ayıklama için Derleme Kaynak hata ayıklayıcıyı kullanmak için, hata ayıklanan yürütülebilir dosyanın -g derleyici seçeneğiyle derlenmesi gerekir. Bu seçenek, derleyiciyi nesne koduna ek hata ayıklama tanımlayıcıları eklemeye zorlar; yani

Yazarın kitabından

Neden derlemek? BASIC okuyucuları, bir programı tamamlamak için neden bu kadar çok adım olduğunu merak edebilir. Görünüşe göre bu derleme yolu daha fazla zaman alıyor (ve bazı durumlarda gerçekten olabilir). Ama o zamandan beri

Yazarın kitabından

3.8.3. Çekirdeğin Derlenmesi Bir RPM paketinden kurulum yaparken, cihaz sürücülerinin çekirdek ile tek parça halinde derlenebileceği veya ayrı olarak yüklenebileceği modüler bir çekirdek elde ederiz. Böyle bir çekirdek operasyonda daha yavaştır, ancak sürücüleri basit bir değiştirme ile güncellemenize izin verir.

Yazarın kitabından

Derleme Çoğu uygulamayı oluşturma prosedürü geneldir ve Şek. 2.2. Pirinç. 2.2. Programın derleme şeması İlk aşama, başlık dosyaları da dahil olmak üzere programın kaynak dosyalarının derleyici tarafından işlendiği derleme aşamasıdır.