7.3. Файлова система: inodes

Този раздел ще покрие системните извиквания, които се занимават с файловата система и по-специално с информация за файлове като размер, дати, разрешения и т.н. Тези системни извиквания дават достъп до цялата информация, която обсъдихме в Глава 2.

Нека сега се обърнем към действителните inodes. Някои от тях са описани от структурата на stat, дефинирана в sys/stat.h:

struct stat /* структура, върната от stat */

/* inode устройство */

/* номер на inode */

/* брой връзки към файла */

/* идентификатор на група собственици */

/* за специални файлове */

/* размер на файла в символи */

/* последно време на четене от файл */

7.3. Файлова система: inodes

последно записване или създаване на файл */

последно модифициран файл или индекс

#define S_IFMT 0170000

/* при стартиране, задайте ефективния

потребителско име като собственик */

/* при стартиране, задайте ефективния

идентификатор на групата като собственик */

/* запазва изпомпвания текст дори след това

/* разрешения за четене, собственик */

/* разрешения за запис, собственик */

/* изпълнение/права за търсене, собственик */

Inode на файл се осъществява чрез две системни извиквания: stat и fstat. Извикването stat взема име на файл и връща информацията за inode за този файл (или 1 при грешка), fstat прави същото от файловия дескриптор за отворения файл (не от указателя FILE). По този начин,

char*име; int fd;

struct stat stbuf;

Структурата stbuf се попълва с информация от inode за файла с имеили дескриптор fd.

След като имаме всички тези факти, нека се опитаме да напишем полезен код. Нека започнем с написването на програмата за проверка на C, която ще следи пощенската кутия на потребителя. Ако файлът се увеличи по размер, програмата пише Имате поща и издава звуков сигнал. (Ако файлът става по-малък, вероятно е защото току-що сте прочели и изтрили имейл, не са необходими съобщения.) За първата стъпка това е напълно достатъчно; след като тази програма започне да работи, ще бъде възможно да се подобри.

/* checkmail: наблюдава пощенската кутия на потребителя */ #include

Глава 7 UNIX системни извиквания

char *maildir = "/usr/spool/mail";

/* зависи от системата */

main(argc, argv) int argc; char *argv[];

struct stat buf;

char *име, *getlogin(); int lastsize = 0;

ако ((име = getlogin()) == NULL)

грешка ("не може да получи име за влизане", (char *) 0); if (chdir(maildir) == 1)

грешка ("не може да cd към %s", maildir); за(;;)

ако (buf.st_size > lastsize)

fprintf(stderr, "\nИмате поща\007\n"); lastsize = buf.st_size;

Функцията getlogin(3) връща потребителското име, с което потребителят е влязъл, или NULL, ако не е получено име. Системното извикване chdir поставя checkmail в пощенската директория, така че последващите извиквания на stat не трябва да преминават през всяка директория от root до поща. Във вашата система може да се наложи да промените стойността на maildir. Програмата за проверка на пощата е написана да се опитва дори ако пощенската кутия не съществува, тъй като повечето пощенски програми изтриват пощенската кутия, ако е празна.

Илюстрация за обработка на грешки: sv

Сега да пишемпрограма, наречена sv (подобна на cp), която копира набор от файлове в директория, но презаписва целевия файл само ако не съществува или е по-дълъг от

7.3. Файлова система: inodes

по-стар от оригиналния файл. Името sv идва от английската дума save (запазване) - идеята е sv да не презаписва по-новите версии на файловете. Командата sv зачита повече информация за inode, отколкото checkmail.

Ще стартираме sv така:

$ sv файл1 файл2. реж

Тази команда трябва да копира file1 в dir/file1, file2 в dir/file2 и т.н., освен ако целевият файл не е по-нов от съответния изходен файл, в който случай не се прави копие и се издава предупреждение. За да се избегне многократно копиране на препратки, програмата sv не позволява / в имената на изходните файлове.

/* sv: съхранява нови файлове */ #include

#include #include #include char *име на програмата;

main(argc, argv) int argc; char *argv[];

struct stat stbuf;

char *dir = argv[argc 1];

име на програма = argv[0]; if (argc 254 Глава 7. UNIX системни извиквания

char target [BUFSIZ], buf [BUFSIZ], *index();

sprintf(цел, "%s/%s", директория, файл);

if (index(file, '/') != NULL) /* strchr() на някои системи */ error("няма да обработва / в %s", файл);

if (stat(file, &sti) == 1)

грешка ("не може да покаже %s",

if (stat(target, &sto) ==

1) /* целевият файл не съществува */

нека е по-старо */

if (sti.st_mtime 0) if (write(fout, buf, n) != n)

грешка ("грешка при писане на %s", цел);

Вместо стандартните I/O функции, creat беше използван, за да накара sv да имавъзможност за запазване на кода за права за достъп на изходния файл. (Имайте предвид, че index и strchr са различни имена за една и съща процедура; вижте ръководството за описанието на string(3), за да видите кое име използва вашата система.)

Въпреки че sv е специализирана програма, тя илюстрира няколко важни идеи. Има много програми, които, въпреки че не са "системни" програми, все пак могат да използват информация, съхранявана от операционната система, която може да бъде достъпна чрез системни повиквания. За такива програми ключът е, че системните типове данни са дефинирани само в стандартни заглавни файлове като stat.h и dir.h и програмата включва тези файлове, вместо да използва свои собствени дефиниции на типове. Такъв код е много по-вероятно да бъде преносим от една система в друга.

Не се изненадвайте, че поне две трети от кода в програмата sv е проверка на грешки. В началните етапи на писане на програма е изкушаващо да се спести от обработката на грешки, тъй като това не е част от основната постановка на проблема. И след като програмата е готова и работи, няма достатъчно ентусиазъм да се върнете и да поставите проверките, които ще превърнат частната програма в стабилен продукт.

Програмата sv не е имунизирана срещу всяко възможно нещастие, например тя не обработва прекъсвания в неудобни моменти, но е по-

7.3. Файлова система: inodes

по-голяма от повечето програми. Нека спрем буквално в един момент - разгледайте последното изявление write. Рядко се случва записът да не успее, така че много програми пренебрегват тази възможност. Но дисковете нямат свободно място; потребителите надхвърлят квотите; се случватпрекъсвания на комуникационните линии. Всички тези обстоятелства могат да причинят грешки при запис и е много по-добре, ако потребителят знае за това, вместо програмата мълчаливо да се преструва, че всичко е наред.

Упражнение 7.10. Променете контролната поща, за да идентифицирате подателя на имейла в съобщението Имате имейл. Подсказка: sscanf, lseek.

Упражнение 7.11. Променете checkmail, така че директорията да не се променя на поща, преди да влезете в цикъла. Ще има ли значение за представянето? (По-трудно.) Можете ли да напишете версия на checkmail, която се нуждае само от един процес, за да уведоми всички потребители?

Упражнение 7.12. Напишете програма за гледане на файлове, която проверява файл и го отпечатва отначало при всяка промяна. Къде може да се използва такава програма?

Упражнение 7.13. Програмата sv няма гъвкавост при обработката на грешки. Променете го така, че да продължи, дори ако не може да обработи някои файлове.

Упражнение 7.15. Напишете произволна програма:

който извежда един ред, произволно избран от файла. Ако случайно обработва файл с имена, той може да се използва в програмата за изкупителна жертва, която помага да се намери виновникът:

echo "За всичко са виновни случайни хора!" $изкупителна жертва

За всичко е виновен Кен!

Глава 7 UNIX системни извиквания

Уверете се, че произволното работи правилно, независимо от разпределението на дължините на низовете.

Този раздел описва изпълнението на една програма от друга. Най-лесният начин да направите това е да използвате стандартната библиотечна функционална система, описана, но отхвърлена в Глава 6. Системната команда приема един аргумент, командния ред точно както е въведен.на терминала (с изключение на знака за нов ред в края) и го изпълнява в подобвивка. Ако командният ред трябва да се състои от няколко части, тогава възможностите за форматиране в паметта, които има sprintf, могат да бъдат полезни. В края на този раздел ще бъде представена по-стабилна версия на система за използване в интерактивни програми, но първо трябва да разгледаме частите, които я съставят.

Създаване на процес на ниско ниво - execlp и execvp

Основната операция е изпълнението на друга програма, без да се чака системното извикване execlp да завърши. Например, за да отпечатате датата като последно действие на работеща програма, използвайте

execlp("дата", "дата", (char *) 0);

Първият аргумент на execlp е името на файла на командата; execlp получава пътя за търсене (т.е. $PATH) от средата и извършва същото търсене като обвивката. Вторият и следващите аргументи са името на командата и нейните опции; те стават argv масив за новата програма. Краят на списъка е маркиран с нулева стойност (за да разберете конструкцията execlp, вижте exec(2)).

Извикването на execlp заменя съществуващата програма с новата, стартира я и излиза. Основната програма връща контрола само ако има грешка, например ако файлът не е намерен или не е изпълним:

execlp("дата", "дата", (char *) 0);

fprintf(stderr, "Не може да се изпълни 'дата'\n"), изход(l);

Вариант на execlp, наречен execvp, се използва, когато броят на аргументите не е известен предварително. Обаждането изглежда така:

където argp е масив от указатели към аргументи (като argv); последният указател в масива трябва да е NULL, така че execvp да може да определи къде завършва списъкът. Както при execlp, името на файла е файлът, в койтопрограмата се намира и argp е argv масивът за новата програма; argp[0] е името на програмата.

Нито една от тези програми не позволява метасимволи, *, кавички и т.н. в списъка с аргументи. Ако е необходимо, изпълнете обвивката /bin/sh, която ще свърши цялата работа. Създайте команден ред, който ще съдържа цялата команда, сякаш е въведена в терминал, след което кажете:

execlp("/bin/sh", "sh", "c", команден ред, (char *) 0);

Аргументът c указва, че следващият аргумент трябва да се третира като целия команден ред, а не като отделен аргумент.

Разгледайте програмата waitfile като илюстрация. Екип

$ waitfile име на файл5 [команда]

периодично проверява посочения файл. Ако не се е променило от последната проверка, тогава командата се изпълнява. Ако командата не е зададена, тогава файлът се копира в стандартен изход. За да наблюдаваме работата на troff, използваме файл за чакане:

$ waitfile troff.out ехо troff done &

Изпълнението на waitfile извлича времето за модификация на файл с помощта на fstat.

/* waitfile: изчаква докато файлът спре да се променя */ #include