C Програмиране

Урок за системно повикване на Linux с C

Урок за системно повикване на Linux с C
В последната ни статия за системните повиквания на Linux определих системно обаждане, обсъдих причините, поради които може да ги използваме в програма, и се задълбочих в техните предимства и недостатъци. Даже дадох кратък пример при сглобяването в C. Той илюстрира въпроса и описва как да се осъществи разговорът, но не направи нищо продуктивно. Не е точно вълнуващо упражнение за развитие, но илюстрира въпроса.

В тази статия ще използваме действителни системни повиквания, за да извършим реална работа в нашата програма C. Първо ще прегледаме дали трябва да използвате системно повикване, след което ще предоставим пример с помощта на повикването sendfile (), което може драстично да подобри производителността на копиране на файлове. И накрая, ще разгледаме някои точки, които трябва да запомните, докато използвате системни повиквания на Linux.

Имате ли нужда от системно обаждане?

Въпреки че е неизбежно, ще използвате системно обаждане в даден момент от вашата кариера за развитие на C, освен ако не се насочите към висока производителност или функционалност от определен тип, библиотеката glibc и други основни библиотеки, включени в основните дистрибуции на Linux, ще се погрижат за по-голямата част вашите нужди.

Стандартната библиотека на glibc осигурява междуплатформена, добре тествана рамка за изпълнение на функции, които иначе изискват специфични за системата системни повиквания. Например можете да прочетете файл с fscanf (), fread (), getc () и т.н., или можете да използвате системното повикване read () Linux. Функциите glibc предоставят повече функции (т.е.д. по-добра обработка на грешки, форматиран IO и т.н.) и ще работи на всяка поддръжка на glibc система.

От друга страна, има моменти, в които безкомпромисната производителност и точното изпълнение са от решаващо значение. Опаковката, която предоставя fread (), ще добави режийни и макар и незначителна, не е напълно прозрачна. Освен това може да не искате или да се нуждаете от допълнителните функции, които опаковката предоставя. В такъв случай е най-добре да получите системен разговор.

Можете също да използвате системни повиквания, за да изпълнявате функции, които все още не се поддържат от glibc. Ако вашето копие на glibc е актуално, това едва ли ще бъде проблем, но разработването на по-стари дистрибуции с по-нови ядра може да изисква тази техника.

След като прочетохте отказ от отговорност, предупреждения и потенциални отклонения, сега нека разгледаме някои практически примери.

На какъв процесор сме?

Въпрос, който повечето програми вероятно не мислят да зададат, но все пак валиден. Това е пример за системно повикване, което не може да се дублира с glibc и не е покрито с glibc обвивка. В този код ще извикаме извикването getcpu () директно чрез функцията syscall (). Функцията syscall работи по следния начин:

syscall (SYS_call, arg1, arg2, ...);

Първият аргумент, SYS_call, е дефиниция, която представлява номера на системното повикване. Когато включите sys / syscall.з, те са включени. Първата част е SYS_, а втората част е името на системното повикване.

Аргументите за обаждането влизат в arg1, arg2 по-горе. Някои обаждания изискват повече аргументи и те ще продължат в ред от ръководството си. Не забравяйте, че повечето аргументи, особено за връщанията, ще изискват указатели за маркиране на масиви или памет, разпределени чрез функцията malloc.

пример1.° С

#include
#include
#include
#include
 
int main ()
 
неподписан процесор, възел;
 
// Вземете текущо ядро ​​на процесора и NUMA възел чрез системно обаждане
// Забележете, че няма glibc обвивка, така че трябва да го извикаме директно
syscall (SYS_getcpu, & cpu, & node, NULL);
 
// Показване на информация
printf ("Тази програма работи на CPU ядро% u и NUMA възел% u.\ n \ n ", процесор, възел);
 
връщане 0;
 

 
За да компилирате и стартирате:
 
gcc пример1.c -o пример1
./ пример1

За по-интересни резултати можете да завъртите нишки чрез библиотеката pthreads и след това да извикате тази функция, за да видите на кой процесор работи вашата нишка.

Sendfile: Превъзходно изпълнение

Sendfile предоставя отличен пример за подобряване на производителността чрез системни обаждания. Функцията sendfile () копира данни от един дескриптор на файл в друг. Вместо да използва множество функции fread () и fwrite (), sendfile извършва трансфера в пространството на ядрото, намалявайки режийните разходи и по този начин увеличавайки производителността.

В този пример ще копираме 64 MB данни от един файл в друг. В един тест ще използваме стандартните методи за четене / запис в стандартната библиотека. В другата ще използваме системни повиквания и повикването sendfile (), за да прехвърлим тези данни от едно място на друго.

тест1.c (glibc)

#include
#include
#include
#include
 
#define BUFFER_SIZE 67108864
#define BUFFER_1 "buffer1"
#define BUFFER_2 "buffer2"
 
int main ()
 
ФАЙЛ * fOut, * fIn;
 
printf ("\ n I / O тест с традиционни glibc функции.\ n \ n ");
 
// Вземете буфер BUFFER_SIZE.
// Буферът ще съдържа случайни данни, но това не ни интересува.
printf ("Разпределяне на 64 MB буфер:");
char * буфер = (char *) malloc (BUFFER_SIZE);
printf ("ГОТОВО \ n");
 
// Записваме буфера във fOut
printf ("Записване на данни в първия буфер:");
fOut = fopen (BUFFER_1, "wb");
fwrite (буфер, sizeof (char), BUFFER_SIZE, fOut);
fclose (fOut);
printf ("ГОТОВО \ n");
 
printf ("Копиране на данни от първи файл във втори:");
fIn = fopen (BUFFER_1, "rb");
fOut = fopen (BUFFER_2, "wb");
fread (буфер, sizeof (char), BUFFER_SIZE, fIn);
fwrite (буфер, sizeof (char), BUFFER_SIZE, fOut);
fclose (fIn);
fclose (fOut);
printf ("ГОТОВО \ n");
 
printf ("Освобождаване на буфер:");
безплатно (буфер);
printf ("ГОТОВО \ n");
 
printf ("Изтриване на файлове:");
премахване (BUFFER_1);
премахване (BUFFER_2);
printf ("ГОТОВО \ n");
 
връщане 0;
 

тест2.c (системни обаждания)

#include
#include
#include
#include
#include
#include
#include
#include
#include
 
#define BUFFER_SIZE 67108864
 
int main ()
 
int fOut, fIn;
 
printf ("\ nI / O тест с sendfile () и свързани системни повиквания.\ n \ n ");
 
// Вземете буфер BUFFER_SIZE.
// Буферът ще съдържа случайни данни, но това не ни интересува.
printf ("Разпределяне на 64 MB буфер:");
char * буфер = (char *) malloc (BUFFER_SIZE);
printf ("ГОТОВО \ n");
 
// Записваме буфера във fOut
printf ("Записване на данни в първия буфер:");
fOut = отворен ("буфер1", O_RDONLY);
запис (fOut, & буфер, BUFFER_SIZE);
затваряне (fOut);
printf ("ГОТОВО \ n");
 
printf ("Копиране на данни от първи файл във втори:");
fIn = отворен ("буфер1", O_RDONLY);
fOut = отворен ("buffer2", O_RDONLY);
sendfile (fOut, fIn, 0, BUFFER_SIZE);
затваряне (fIn);
затваряне (fOut);
printf ("ГОТОВО \ n");
 
printf ("Освобождаване на буфер:");
безплатно (буфер);
printf ("ГОТОВО \ n");
 
printf ("Изтриване на файлове:");
прекратяване на връзката ("буфер1");
прекратяване на връзката ("буфер2");
printf ("ГОТОВО \ n");
 
връщане 0;
 

Компилиране и провеждане на тестове 1 и 2

За да създадете тези примери, ще са ви необходими инструментите за разработка, инсталирани на вашата дистрибуция. На Debian и Ubuntu можете да инсталирате това с:

apt инсталиране на основите на изграждането

След това компилирайте с:

gcc тест1.c -o test1 && gcc test2.c -o тест2

За да стартирате и двете и да тествате производителността, изпълнете:

време ./ test1 && време ./ тест2

Трябва да получите резултати като този:

I / O тест с традиционни glibc функции.

Разпределение на 64 MB буфер: СЪСТАВЕНО
Записване на данни в първия буфер: ГОТОВО
Копиране на данни от първи файл във втори: ГОТОВО
Освобождаващ буфер: ГОТОВО
Изтриване на файлове: ГОТОВО
реално 0m0.397s
потребител 0m0.000s
sys 0m0.203s
I / O тест с sendfile () и свързани системни повиквания.
Разпределяне на 64 MB буфер: СЪСТАВЕНО
Записване на данни в първия буфер: ГОТОВО
Копиране на данни от първи файл във втори: ГОТОВО
Освобождаващ буфер: ГОТОВО
Изтриване на файлове: ГОТОВО
реално 0m0.019s
потребител 0m0.000s
sys 0m0.016s

Както можете да видите, кодът, който използва системните повиквания, работи много по-бързо от еквивалента на glibc.

Неща за запомняне

Системните разговори могат да повишат производителността и да осигурят допълнителна функционалност, но не са без недостатъците си. Ще трябва да прецените предимствата, които предлагат системните обаждания, срещу липсата на преносимост на платформата и понякога намалена функционалност в сравнение с библиотечните функции.

Когато използвате някои системни повиквания, трябва да внимавате да използвате ресурси, върнати от системни повиквания, а не библиотечни функции. Например структурата FILE, използвана за функциите fopen (), fread (), fwrite () и fclose () на glibc, не са същите като номера на дескриптора на файла от системното повикване open () (върнато като цяло число). Смесването им може да доведе до проблеми.

По принцип системните повиквания на Linux имат по-малко бронирани ленти, отколкото функциите на glibc. Макар че е вярно, че системните обаждания имат известна обработка на грешки и отчитане, ще получите по-подробна функционалност от функция glibc.

И накрая, дума за сигурността. Системните обаждания директно се свързват с ядрото. Ядрото на Linux има широка защита срещу прониквания от страна на потребителите, но съществуват неоткрити грешки. Не вярвайте, че системното обаждане ще потвърди вашите данни или ще ви изолира от проблеми със сигурността. Разумно е да се уверите, че данните, които предавате на системно повикване, са санирани. Естествено, това е добър съвет за всяко извикване на API, но не можете да бъдете внимателни, когато работите с ядрото.

Надявам се да ви е харесало това по-дълбоко гмуркане в страната на системните разговори на Linux. За пълен списък на системните повиквания на Linux вижте нашия главен списък.

Най-добрите дистрибуции на Linux за игри през 2021 г
Операционната система Linux е изминала дълъг път от първоначалния си, прост, базиран на сървъра външен вид. Тази операционна система се подобри неимов...
Как да заснемете и поточно предадете игралната си сесия на Linux
В миналото играта на игри се смяташе само за хоби, но с течение на времето игралната индустрия отбеляза огромен ръст по отношение на технологиите и бр...
Най-добрите игри за игра с ръчно проследяване
Oculus Quest наскоро представи страхотната идея за ръчно проследяване без контролери. С непрекъснато нарастващия брой игри и дейности, които изпълнява...