36
•более точную семантику других полей следует изучать по руководству man.
Встандарте POSIX предусмотрены макросы, которые проверяют, является ли файл:
•S_ISLNK(m) — символьной ссылкой;
•S_ISREG(m) — обычным файлом;
•S_ISDIR(m) — каталогом;
•S_ISCHR(m) — символьным устройством;
•S_ISBLK(m) — блочным устройством;
•S_ISFIFO(m) — каналом FIFO;
•S_ISSOCK(m) — сокетом.
Для поля st_mode, в стандарте POSIX для программирования на языке С предусмотрены также флаги, представленные в таблице 1.3.
Таблица 1.3 — Флаги для поля st_mode
S_IFMT |
0170000 |
битовая маска для полей типа файла |
S_IFSOCK |
0140000 |
сокет |
S_IFLNK |
0120000 |
символьная ссылка |
S_IFREG |
0100000 |
обычный файл |
S_IFBLK |
0060000 |
блочное устройство |
S_IFDIR |
0040000 |
каталог |
S_IFCHR |
0020000 |
символьное устройство |
S_IFIFO |
0010000 |
канал FIFO |
S_ISUID |
0004000 |
бит setuid |
S_ISGID |
0002000 |
бит setgid |
S_ISVTX |
0001000 |
бит принадлежности |
S_IRWXU |
00700 |
маска для прав доступа пользователя |
S_IRUSR |
00400 |
пользователь имеет право чтения |
S_IWUSR |
00200 |
пользователь имеет право записи |
S_IXUSR |
00100 |
пользователь имеет право выполнения |
S_IRWXG |
00070 |
маска для прав доступа группы |
S_IRGRP |
00040 |
группа имеет права чтения |
S_IWGRP |
00020 |
группа имеет права записи |
S_IXGRP |
00010 |
группа имеет права выполнения |
S_IRWXO |
00007 |
маска прав доступа всех прочих (не находящихся в |
|
|
группе) |
S_IROTH |
00004 |
все прочие имеют права чтения |
S_IWOTH |
00002 |
все прочие имеют права записи |
S_IXOTH |
00001 |
все прочие имеют права выполнения |
37
Замечание
Обратите внимание на соответствие данной таблицы для поля st_mode и таблицы 4.10 для i_mode, приведенной в [4]. Учтите, что значения констант в этих таблицах приведены в разных представлениях.
В случае успеха, все функции возвращают ноль.
При обнаружении ошибки возвращается -1, а переменной errno присваивается номер ошибки:
•EBADF - Неверный файловый описатель filedes;
•ENOENT - Компонент полного имени файла file_name не существует или полное имя является пустой строкой;
•ENOTDIR - Компонент пути не является каталогом;
•ELOOP - При поиске файла встретилось слишком много символьных ссылок;
•EFAULT - Некорректный адрес;
•EACCES - Запрещен доступ;
•ENOMEM - Недостаточно памяти в системе;
•ENAMETOOLONG - Слишком длинное название файла.
Поскольку поля st_dev и st_rdev имеют тип dev_t, который может отличаться для разных аппаратных платформ, следует пользоваться макросами, присутсвующими в система разработки большинства ОС:
#define _BSD_SOURCE |
/* See feature_test_macros(7) */ |
#include <sys/types.h> |
|
dev_t makedev(unsigned int maj, unsigned int min); unsigned int major(dev_t dev);
unsigned int minor(dev_t dev);
Теперь рассмотрим конкретный пример, представленный на листинге 1.6 и демонстрирующий возможности изученных системных вызовов.
Листинг 1.6 — Запрос информации о статусе файлов
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h>
int main(int argc, char *argv[], char *envp[]) {
#define NO 0; #define YES 1;
// Переменные программы |
|
|
|
struct stat |
sb; |
// Структура информации о файле |
|
int res = NO; |
// Состояние определения типа файла |
||
char buf[255]; |
// |
Буфер чтения данных имени файла |
|
ssize_t n = |
0L; |
// |
Чило прочитанных симвосов имени файла |
38
puts("Программа lab1.5: Введи имя файла> "); n = read(0, buf, sizeof(buf));
if(n < 2) { // Нет аргумента - завершаем работу puts("\nОшибка: необходимо ввести имя_файла\n"); return 1;
}
buf[n - 1] = '\0';
printf("Исследуем |
статус файла: |
%s\n", buf); |
printf("Длина |
имени файла: |
%lu\n", n - 1); |
if (stat(buf, &sb) != 0){
printf("Ошибка получения статуса файла: %s", buf); return 1;
}
//Пример использования макросов определения типа файла res = S_ISDIR(sb.st_mode);
printf("%i\n", res);
//Другой вариант
printf("Файл %s является: ", buf); |
|
||||
switch (sb.st_mode & |
S_IFMT) { |
|
|||
case S_IFBLK: |
printf("блочным устройством\n"); |
break; |
|||
case S_IFCHR: |
printf("символьным устройством\n"); |
break; |
|||
case S_IFDIR: |
printf("директорией\n"); |
break; |
|||
case S_IFIFO: |
printf("FIFO/pipe\n"); |
break; |
|||
case S_IFLNK: |
printf("ссылкой на файл\n"); |
break; |
|||
case S_IFREG: |
printf("обычным (регулярным) файлом\n"); break; |
||||
case S_IFSOCK: |
printf("сокетом\n"); |
break; |
|||
default: |
printf("неизвестным типом файла\n"); break; |
||||
} |
|
|
|
|
|
printf("st_dev |
= |
0x%lx ( major = %u minor = %u )\n", |
|||
sb.st_dev, major(sb.st_dev), minor(sb.st_dev)); |
|||||
printf("st_rdev |
= |
0x%lx ( major = %u minor = %u )\n", |
|||
sb.st_rdev, major(sb.st_rdev), minor(sb.st_rdev)); |
|||||
printf("st_ino |
= |
%lu\n", sb.st_ino); |
|
||
printf("st_mode |
= |
0%o\n", sb.st_mode); |
|
||
printf("st_nlink |
= |
%lu\n", sb.st_nlink); |
|
||
printf("st_size |
= |
%lu\n", sb.st_size); |
|
||
printf("st_blksize = |
%lu\n", sb.st_blksize); |
|
|||
printf("st_blocks |
= |
%lu\n", sb. |
st_blocks |
); |
|
return EXIT_SUCCESS;
}
Вариант запуска данной программы представлен на рисунке 1.13.
Замечание
Программа, представленная на листинге 1.6, осуществляет ввод имени файла с клавиатуры, что позволяет легко исследовать статусы множества файлов, не выходя из системы разработки.
ОС УПК АСУ имеет утилиту с именем stat. Студенту следует самостоятельно изучить ее применение и сравнить полученные результаты с разработанной программой.
39
Рисунок 1.13 — Вариант запуска программы листинга 1.6
Операционные системы предоставляют программисту два типа каналов:
•полудуплексные каналы UNIX;
•именованные каналы FIFO.
Изучая командный интерпретатор shell, мы уже сталкивались с понятием ка-налов. Например, если в командной строке запустить: ls -l | grep src, то интерпретатор sh или bash будут выполнять следующую последовательность действий:
•будут одновременно запускатся утилиты ls, обеспечивающая вывод подроб-
ного списка файлов текущей директории, и утилита grep, фильтрующая входной поток данных по наличию слова src, в каждой строке;
•перед вызовом утилит, shell создаст полудуплексный каканал, обеспечив возможность записи в него для утилиты ls и чтение из него для утилиты grep.
Каналы - старейший из инструментов IPC, существующий приблизительно со времени появления самых ранних версий оперативной системы UNIX. Они предоставляют метод односторонних коммуникаций между процессами: отсюда появился термин half-duplex channel.
Сам канал создается в ядре ОС. Когда процесс создает канал, ядро устанавливает два файловых дескриптора для пользования этим каналом. Один такой дескриптор используется, чтобы открыть путь ввода в канал (запись), в то время как другой применяется для получения данных из канала (чтение), как показано ниже:
|
40 |
in |
<----- |
Process |
Kernel |
out |
-----> |
Если открыт массив дескрипторов int fd[2] и процесс посылает данные через канал fd[1], то он имеет возможность получить эту информацию из дескрип-тора fd[0].
Хотя подобное применение канала не имеет практической ценности, для учебных целей, показанных на листинге 1.7, оно вполне подходит.
Создание неименованного канала выполняется с помощью функции pipe(...):
#include <unistd.h>
int pipe(int pipefd[2]);
где pipefd — целочисленный массив открываемых дескрипторов канала.
Листинг 1.7 — Запись строки в ядро ОС и чтение из канала
#include <stdio.h> #include <stdlib.h> #include <unistd.h>
int main(void) { int fd[2];
char out[] = "Строка, записанная в канал...\n"; char buf[BUFSIZ];
if (pipe(fd) == -1) { perror("pipe"); exit(EXIT_FAILURE);
}
write(fd[1], out, sizeof(out)); // Пишем в канал close(fd[1]);
while(read(fd[0], buf, 1) > 0) write(1, buf, 1);
close(fd[0]);
return EXIT_SUCCESS;
}
Основное назначение неименованных полудуплексных каналов — взаимодействие между родительским и дочерними процессами. В этом случае используется то свойство, что дочерний процесс наследует все ресурсы, открытые родительским процессом, в том числе и открытые каналы.
Если предположить, что родительский процесс долже передать некоторое сообщение дочернему процессу, а потом дождаться его завершения, то в этом случае:
•родителскому процессу не нужет дескриптор чтения из канала;
•дочернему процессу не нужен дескриптор записи в канал.