C Programlama

C ile Linux Sistem Çağrı Eğitimi

C ile Linux Sistem Çağrı Eğitimi
Linux Sistem Çağrıları hakkındaki son makalemizde, bir sistem çağrısı tanımladım, bir programda kullanılmasının nedenlerini tartıştım ve avantajlarını ve dezavantajlarını araştırdım. Montajda C içinde kısa bir örnek bile verdim. Konuyu gösterdi ve aramanın nasıl yapılacağını açıkladı, ancak üretken hiçbir şey yapmadı. Tam olarak heyecan verici bir geliştirme alıştırması değil, ama noktayı gösterdi.

Bu yazıda, C programımızda gerçek iş yapmak için gerçek sistem çağrılarını kullanacağız. İlk olarak, bir sistem çağrısı kullanmanız gerekip gerekmediğini inceleyeceğiz, ardından dosya kopyalama performansını önemli ölçüde artırabilecek sendfile() çağrısını kullanarak bir örnek sunacağız. Son olarak, Linux sistem çağrılarını kullanırken hatırlanması gereken bazı noktaların üzerinden geçeceğiz.

Sistem Çağrısına İhtiyacınız Var mı??

Yüksek performansı veya belirli bir tür işlevselliği hedeflemediğiniz sürece, C geliştirme kariyerinizin bir noktasında bir sistem çağrısı kullanmanız kaçınılmaz olsa da, büyük Linux dağıtımlarında bulunan glibc kitaplığı ve diğer temel kitaplıklar, bunların çoğunu halledecektir. ihtiyaçlarınız.

glibc standart kitaplığı, aksi takdirde sisteme özel sistem çağrıları gerektirecek işlevleri yürütmek için platformlar arası, iyi test edilmiş bir çerçeve sağlar. Örneğin, fscanf(), fread(), getc(), vb. ile bir dosyayı okuyabilirsiniz., veya read() Linux sistem çağrısını kullanabilirsiniz. glibc işlevleri daha fazla özellik sağlar (i.e. daha iyi hata işleme, biçimlendirilmiş IO vb.) ve herhangi bir sistem glibc desteği üzerinde çalışacak.

Öte yandan, tavizsiz performansın ve kesin uygulamanın kritik olduğu zamanlar vardır. fread()'in sağladığı sarmalayıcı ek yük ekleyecek ve küçük olmasına rağmen tamamen şeffaf değil. Ek olarak, sarıcının sağladığı ekstra özellikleri istemeyebilir veya bunlara ihtiyaç duymayabilirsiniz. Bu durumda, en iyi şekilde bir sistem çağrısı ile hizmet alırsınız.

Henüz glibc tarafından desteklenmeyen işlevleri gerçekleştirmek için sistem çağrılarını da kullanabilirsiniz. Glibc kopyanız güncelse, bu pek sorun olmaz, ancak daha yeni çekirdeklerle eski dağıtımlarda geliştirme yapmak bu tekniği gerektirebilir.

Artık sorumluluk reddi beyanlarını, uyarıları ve olası sapmaları okudunuz, şimdi bazı pratik örnekleri inceleyelim.

Hangi CPU'dayız?

Çoğu programın muhtemelen sormayı düşünmediği, ancak yine de geçerli bir soru. Bu, glibc ile kopyalanamayan ve bir glibc sarmalayıcısı ile kapsanmayan bir sistem çağrısı örneğidir. Bu kodda, getcpu() çağrısını doğrudan syscall() işlevi aracılığıyla çağıracağız. Sistem çağrısı işlevi aşağıdaki gibi çalışır:

sistem çağrısı(SYS_call, arg1, arg2,… ​​);

İlk argüman, SYS_call, sistem çağrısının numarasını temsil eden bir tanımdır. sys/syscall'ı eklediğinizde.h, bunlar dahil. İlk kısım SYS_ ve ikinci kısım sistem çağrısının adıdır.

Çağrı için argümanlar yukarıdaki arg1, arg2'ye girer. Bazı çağrılar daha fazla argüman gerektirir ve man sayfalarından sırayla devam ederler. Çoğu argümanın, özellikle de dönüşler için, işaretçilerin dizileri veya malloc işlevi aracılığıyla tahsis edilen belleği gerektirdiğini unutmayın.

örnek 1.c

#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
 
int ana()
 
imzasız işlemci, düğüm;
 
// Sistem çağrısı yoluyla mevcut CPU çekirdeğini ve NUMA düğümünü alın
// Bunun glibc sarmalayıcısı olmadığına dikkat edin, bu yüzden onu doğrudan çağırmalıyız
sistem çağrısı(SYS_getcpu, &cpu, &düğüm, NULL);
 
// Bilgileri göster
printf("Bu program CPU çekirdeği %u ve NUMA düğümü %u üzerinde çalışıyor.\n\n", işlemci, düğüm);
 
0 döndür;
 

 
Derlemek ve çalıştırmak için:
 
gcc örneği1.c -o örnek1
./örnek 1

Daha ilginç sonuçlar için, dizileri pthreads kitaplığı aracılığıyla döndürebilir ve ardından dizinizin hangi işlemcide çalıştığını görmek için bu işlevi çağırabilirsiniz.

Sendfile: Üstün Performans

Sendfile, sistem çağrıları yoluyla performansı artırmanın mükemmel bir örneğini sunar. sendfile() işlevi, verileri bir dosya tanıtıcısından diğerine kopyalar. Birden fazla fread() ve fwrite() işlevi kullanmak yerine sendfile, aktarımı çekirdek alanında gerçekleştirir, ek yükü azaltır ve böylece performansı artırır.

Bu örnekte, 64 MB veriyi bir dosyadan diğerine kopyalayacağız. Bir testte, standart kitaplıktaki standart okuma/yazma yöntemlerini kullanacağız. Diğerinde, bu verileri bir konumdan diğerine göndermek için sistem çağrılarını ve sendfile() çağrısını kullanacağız.

test1.c (glibc)

#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
 
#define BUFFER_SIZE 67108864
#define BUFFER_1 "buffer1"
#define BUFFER_2 "buffer2"
 
int ana()
 
DOSYA *fOut, *fIn;
 
printf("\nGeleneksel glibc işlevleriyle G/Ç testi.\n\n");
 
// BUFFER_SIZE arabelleği alın.
// Tamponun içinde rastgele veriler olacak ama bu umurumuzda değil.
printf("64 MB arabellek ayırma:                    ");
char *tampon = (char *) malloc(BUFFER_SIZE);
printf("BİTTİ\n");
 
// Tamponu fOut'a yaz
printf("İlk ara belleğe veri yazma:                ");
fOut = fopen(BUFFER_1, "wb");
fwrite(buffer, sizeof(char), BUFFER_SIZE, fOut);
fclose(fOut);
printf("BİTTİ\n");
 
printf("Birinci dosyadan ikinci dosyaya veri kopyalama:      ");
fIn = fopen(BUFFER_1, "rb");
fOut = fopen(BUFFER_2, "wb");
fread(buffer, sizeof(char), BUFFER_SIZE, fIn);
fwrite(buffer, sizeof(char), BUFFER_SIZE, fOut);
fclose(fIn);
fclose(fOut);
printf("BİTTİ\n");
 
printf("Arabelleği boşaltma:                           ");
ücretsiz(tampon);
printf("BİTTİ\n");
 
printf("Dosyalar siliniyor:                           ");
kaldır(BUFFER_1);
kaldır(BUFFER_2);
printf("BİTTİ\n");
 
0 döndür;
 

test2.c (sistem çağrıları)

#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
#Dahil etmek
 
#define BUFFER_SIZE 67108864
 
int ana()
 
int fOut, fIn;
 
printf("\nSendfile() ve ilgili sistem çağrıları ile I/O testi.\n\n");
 
// BUFFER_SIZE arabelleği alın.
// Tamponun içinde rastgele veriler olacak ama bu umurumuzda değil.
printf("64 MB arabellek ayırma:                    ");
char *tampon = (char *) malloc(BUFFER_SIZE);
printf("BİTTİ\n");
 
// Tamponu fOut'a yaz
printf("İlk ara belleğe veri yazma:                ");
fOut = open("buffer1", O_RDONLY);
write(fOut, &buffer, BUFFER_SIZE);
kapat(fÇıkış);
printf("BİTTİ\n");
 
printf("Birinci dosyadan ikinci dosyaya veri kopyalama:      ");
fIn = open("buffer1", O_RDONLY);
fOut = open("buffer2", O_RDONLY);
sendfile(fOut, fIn, 0, BUFFER_SIZE);
kapat(fIn);
kapat(fÇıkış);
printf("BİTTİ\n");
 
printf("Arabelleği boşaltma:                            ");
ücretsiz(tampon);
printf("BİTTİ\n");
 
printf("Dosyalar siliniyor:                           ");
unlink("buffer1");
unlink("buffer2");
printf("BİTTİ\n");
 
0 döndür;
 

1. ve 2. Testleri Derleme ve Çalıştırma

Bu örnekleri oluşturmak için dağıtımınızda kurulu geliştirme araçlarına ihtiyacınız olacak. Debian ve Ubuntu'da bunu şu şekilde yükleyebilirsiniz:

apt kurulum temelleri

Ardından şununla derleyin:

gcc testi1.c -o testi1 && gcc testi2.c -o testi2

Her ikisini de çalıştırmak ve performansı test etmek için şunu çalıştırın:

zaman ./test1 && zaman ./test2

Bunun gibi sonuçlar almalısınız:

Geleneksel glibc işlevleriyle I/O testi.

64 MB arabellek ayırma:                    TAMAMLANDI
İlk ara belleğe veri yazma:                TAMAMLANDI
İlk dosyadan ikinci dosyaya veri kopyalama:      TAMAMLANDI
Boş arabellek:                           TAMAMLANDI
Dosyaları silme:                            BİTTİ
gerçek    0m0.397'ler
kullanıcı    0m0.000'ler
sistem     0m0.203'ler
sendfile() ve ilgili sistem çağrıları ile I/O testi.
64 MB arabellek ayırma:                    TAMAMLANDI
İlk ara belleğe veri yazma:                TAMAMLANDI
İlk dosyadan ikinci dosyaya veri kopyalama:      BİTTİ
Boş arabellek:                           TAMAMLANDI
Dosyaları silme:                           TAMAMLANDI
gerçek    0m0.019'lar
kullanıcı    0m0.000'ler
sistem     0m0.016'lar

Gördüğünüz gibi, sistem çağrılarını kullanan kod, glibc eşdeğerinden çok daha hızlı çalışır.

Hatırlanacak şeyler

Sistem çağrıları performansı artırabilir ve ek işlevsellik sağlayabilir, ancak dezavantajları da vardır. Sistem çağrılarının sağladığı faydaları, platform taşınabilirliği eksikliğine ve bazen kütüphane işlevlerine kıyasla azaltılmış işlevselliğe karşı tartmanız gerekecektir.

Bazı sistem çağrılarını kullanırken kütüphane fonksiyonları yerine sistem çağrılarından dönen kaynakları kullanmaya özen göstermelisiniz. Örneğin, glibc'nin fopen(), fread(), fwrite() ve fclose() işlevleri için kullanılan FILE yapısı, open() sistem çağrısındaki dosya tanımlayıcı numarasıyla aynı değildir (tam sayı olarak döndürülür). Bunları karıştırmak sorunlara yol açabilir.

Genel olarak, Linux sistem çağrıları, glibc işlevlerinden daha az tampon şeridine sahiptir. Sistem çağrılarının bazı hata işleme ve raporlamaya sahip olduğu doğru olsa da, bir glibc işlevinden daha ayrıntılı işlevsellik elde edeceksiniz.

Ve son olarak, güvenlik üzerine bir kelime. Sistem çağrıları doğrudan çekirdekle arayüz oluşturur. Linux çekirdeği, kullanıcı topraklarından gelen maskaralıklara karşı kapsamlı korumaya sahiptir, ancak keşfedilmemiş hatalar var. Bir sistem çağrısının girişinizi doğrulayacağına veya sizi güvenlik sorunlarından yalıtacağına güvenmeyin. Bir sistem çağrısına verdiğiniz verilerin sterilize edilmesini sağlamak akıllıca olacaktır. Doğal olarak, bu herhangi bir API çağrısı için iyi bir tavsiyedir, ancak çekirdekle çalışırken dikkatli olamazsınız.

Umarım Linux sistem çağrıları diyarına bu derin dalıştan keyif almışsınızdır. Linux Sistem Çağrılarının tam listesi için ana listemize bakın.

Wesnoth 1 Savaşı.13.6 Geliştirme Yayınlandı
Wesnoth 1 Savaşı.13.Geçen ay yayınlanan 6. sürüm, 1. sürümdeki altıncı geliştirme sürümüdür.13.x serisi ve özellikle kullanıcı arayüzü için bir dizi i...
Ubuntu 14'te League Of Legends Nasıl Kurulur.04
League of Legends hayranıysanız, bu, League of Legends koşusunu test etmeniz için bir fırsattır. Linux kullanıcısıysanız LOL'nin PlayOnLinux'ta destek...
En son OpenRA Strateji Oyununu Ubuntu Linux'a yükleyin
OpenRA, klasik Command & Conquer: Red Alert gibi erken Westwood oyunlarını yeniden yaratan bir Libre/Free Gerçek Zamanlı Strateji oyun motorudur. Dağı...