Include unistd h что это
НАЗВАНИЕ
unistd — набор символических констант
#include
ОПИСАНИЕ
Включаемый файл содержит символические константы и структуры, которые еще не были описаны в каких-либо других включаемых файлах.
Символические константы для доступа к файлам:
#define R_OK 4 /* Маска доступности для чтения */ #define W_OK 2 /* Маска доступности для записи */ #define X_OK 1 /* Маска доступности для выполнения */ #define F_OK 0 /* Маска проверки существования файла */ #define F_ULOCK 0 /* Разблокировать прежде заблокированную область */ #define F_LOCK 1 /* Заблокировать область для монопольного использования */ #define F_TLOCK 2 /* Проверить и заблокировать область для монопольного использования */ #define F_TEST 3 /* Проверить, заблокирована ли область другими процессами */
Символические константы для позиционирования в файле:
#define SEEK_SET 0 /* Установить указатель файла равным смещению */ #define SEEK_CUR 1 /* Установить указатель файла равным текущему значению плюс смещение */ #define SEEK_END 2 /* Установить указатель файла равным EOF плюс смещение */
Маршрутные имена:
#define GF_PATH "/etc/group" /* Маршрутное имя файла групп */ #define PF_PATH "/etc/passwd" /* Маршрутное имя файла паролей */
Include unistd h что это
В языке C для осуществления файлового ввода-вывода используются механизмы стандартной библиотеки языка, объявленные в заголовочном файле stdio.h. Как вы вскоре узнаете консольный ввод-вывод — это не более чем частный случай файлового ввода-вывода. В C++ для ввода-вывода чаще всего используются потоковые типы данных. Однако все эти механизмы являются всего лишь надстройками над низкоуровневыми механизмами ввода-вывода ядра операционной системы.
С точки зрения модели КИС (Клиент-Интерфейс-Сервер), сервером стандартных механизмов ввода вывода языка C (printf, scanf, FILE*, fprintf, fputc и т. д.) является библиотека языка. А сервером низкоуровневого ввода-вывода в Linux, которому посвящена эта глава книги, является само ядро операционной системы.
Пользовательские программы взаимодействуют с ядром операционной системы посредством специальных механизмов, называемых системными вызовами (system calls, syscalls). Внешне системные вызовы реализованы в виде обычных функций языка C, однако каждый раз вызывая такую функцию, мы обращаемся непосредственно к ядру операционной системы. Список всех системных вызовов Linux можно найти в файле /usr/include/asm/unistd.h. В этой главе мы рассмотрим основные системные вызовы, осуществляющие ввод-вывод: open(), close(), read(), write(), lseek() и некоторые другие.
5.2. Файловые дескрипторы
В языке C при осуществлении ввода-вывода мы используем указатель FILE*. Даже функция printf() в итоге сводится к вызову vfprintf(stdout. ), разновидности функции fprintf(); константа stdout имеет тип struct _IO_FILE*, синонимом которого является тип FILE*. Это я к тому, что консольный ввод-вывод — это файловый ввод-вывод. Стандартный поток ввода, стандартный поток вывода и поток ошибок (как в C, так и в C++) — это файлы. В Linux все, куда можно что-то записать или откуда можно что-то прочитать представлено (или может быть представлено) в виде файла. Экран, клавиатура, аппаратные и виртуальные устройства, каналы, сокеты — все это файлы. Это очень удобно, поскольку ко всему можно применять одни и те же механизмы ввода-вывода, с которыми мы и познакомимся в этой главе. Владение механизмами низкоуровневого ввода-вывода дает свободу перемещения данных в Linux. Работа с локальными файловыми системами, межсетевое взаимодействие, работа с аппаратными устройствами, — все это осуществляется в Linux посредством низкоуровневого ввода-вывода.
Вы уже знаете из предыдущей главы, что при запуске программы в системе создается новый процесс (здесь есть свои особенности, о которых пока говорить не будем). У каждого процесса (кроме init) есть свой родительский процесс (parent process или просто parent), для которого новоиспеченный процесс является дочерним (child process, child). Каждый процесс получает копию окружения (environment) родительского процесса. Оказывается, кроме окружения дочерний процесс получает в качестве багажа еще и копию таблицы файловых дескрипторов.
Файловый дескриптор (file descriptor) — это целое число (int), соответствующее открытому файлу. Дескриптор, соответствующий реально открытому файлу всегда больше или равен нулю. Копия таблицы дескрипторов (читай: таблицы открытых файлов внутри процесса) скрыта в ядре. Мы не можем получить прямой доступ к этой таблице, как при работе с окружением через environ. Можно, конечно, кое-что «вытянуть» через дерево /proc, но нам это не надо. Программист должен лишь понимать, что каждый процесс имеет свою копию таблицы дескрипторов. В пределах одного процесса все дескрипторы уникальны (даже если они соответствуют одному и тому же файлу или устройству). В разных процессах дескрипторы могут совпадать или не совпадать — это не имеет никакого значения, поскольку у каждого процесса свой собственный набор открытых файлов.
Возникает вопрос: сколько файлов может открыть процесс? В каждой системе есть свой лимит, зависящий от конфигурации. Если вы используете bash или ksh (Korn Shell), то можете воспользоваться внутренней командой оболочки ulimit, чтобы узнать это значение.
$ ulimit -n 1024 $
Если вы работаете с оболочкой C-shell (csh, tcsh), то в вашем распоряжении команда limit:
$ limit descriptors descriptors 1024 $
В командной оболочке, в которой вы работаете (bash, например), открыты три файла: стандартный ввод (дескриптор 0), стандартный вывод (дескриптор 1) и стандартный поток ошибок (дескриптор 2). Когда под оболочкой запускается программа, в системе создается новый процесс, который является для этой оболочки дочерним процессом, следовательно, получает копию таблицы дескрипторов своего родителя (то есть все открытые файлы родительского процесса). Таким образом программа может осуществлять консольный ввод-вывод через эти дескрипторы. На протяжении всей книги мы будем часто играть с этими дескрипторами.
Таблица дескрипторов, помимо всего прочего, содержит информацию о текущей позиции чтения-записи для каждого дескриптора. При открытии файла, позиция чтения-записи устанавливается в ноль. Каждый прочитанный или записанный байт увеличивает на единицу указатель текущей позиции. Мы вернемся к этой теме в разделе 5.7.
5.3. Открытие файла: системный вызов open()
Чтобы получить возможность прочитать что-то из файла или записать что-то в файл, его нужно открыть. Это делает системный вызов open(). Этот системный вызов не имеет постоянного списка аргументов (за счет использования механизма va_arg); в связи с этим существуют две «разновидности» open(). Не только в С++ есть перегрузка функций 😉 Если интересно, то о механизме va_arg можно прочитать на man-странице stdarg (man 3 stdarg) или в книге Б. Кернигана и Д. Ритчи «Язык программирования Си». Ниже приведены адаптированные прототипы системного вызова open().
int open (const char * filename, int flags, mode_t mode); int open (const char * filename, int flags);
Системный вызов open() объявлен в заголовочном файле fcntl.h. Ниже приведен общий адаптированный прототип open().
int open (const char * filename, int flags, . );
Начнем по порядку. Первый аргумент — имя файла в файловой системе в обычной форме: полный путь к файлу (если файл не находится в текущем каталоге) или сокращенное имя (если файл в текущем каталоге).
Второй аргумент — это режим открытия файла, представляющий собой один или несколько флагов открытия, объединенных оператором побитового ИЛИ. Список доступных флагов приведен в Таблице 4 Приложения 2.. Наиболее часто используют только первые семь флагов. Если вы хотите, например, открыть файл в режиме чтения и записи, и при этом автоматически создать файл, если такового не существует, то второй аргумент open() будет выглядеть примерно так: O_RDWR|O_CREAT. Константы-флаги открытия объявлены в заголовочном файле bits/fcntl.h, однако не стоит включать этот файл в свои программы, поскольку он уже включен в файл fcntl.h.
Третий аргумент используется в том случае, если open() создает новый файл. В этом случае файлу нужно задать права доступа (режим), с которыми он появится в файловой системе. Права доступа задаются перечислением флагов, объединенных побитовым ИЛИ. Вместо флагов можно использовать число (как правило восьмиричное), однако первый способ нагляднее и предпочтительнее. Список флагов приведен в Таблице 1 Приложения 2. Чтобы, например, созданный файл был доступен в режиме «чтение-запись» пользователем и группой и «только чтение» остальными пользователями, — в третьем аргументе open() надо указать примерно следующее: S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH или 0664. Флаги режима доступа реально объявлены в заголовочном файле bits/stat.h, но он не предназначен для включения в пользовательские программы, и вместо него мы должны включать файл sys/stat.h. Тип mode_t объявлен в заголовочном файле sys/types.h.
Если файл был успешно открыт, open() возвращает файловый дескриптор, по которому мы будем обращаться к файлу. Если произошла ошибка, то open() возвращает -1. Позже, в последующих главах книги мы научимся распознавать ошибки системных вызовов.
5.4. Закрытие файла: системный вызов close()
Системный вызов close() закрывает файл. Вообще говоря, по завершении процесса все открытые файлы (кроме файлов с дескрипторами 0, 1 и 2) автоматически закрываются. Тем не менее, это не освобождает нас от самостоятельного вызова close(), когда файл нужно закрыть. К тому же, если файлы не закрывать самостоятельно, то соответствующие дескрипторы не освобождаются, что может привести к превышению лимита открытых файлов. Простой пример: приложение может быть настроено так, чтобы каждую минуту открывать и перечитывать свой файл конфигурации для проверки обновлений. Если каждый раз файл не будет закрываться, то в моей системе, например, приложение может «накрыться медным тазом» примерно через 17 часов. Автоматически! Кроме того, файловая система Linux поддерживает механизм буферизации. Это означает, что данные, которые якобы записываются, реально записываются на носитель (синхронизируются) только через какое-то время, когда система сочтет это правильным и оптимальным. Это повышает производительность системы и даже продлевает ресурс жестких дисков. Системный вызов close() не форсирует запись данных на диск, однако дает больше гарантий того, что данные останутся в целости и сохранности.
Системный вызов close() объявлен в файле unistd.h. Ниже приведен его адаптированный прототип.
int close (int fd);
Очевидно, что единственный аргумент — это файловый дескриптор. Возвращаемое значение — ноль в случае успеха, и -1 — в случае ошибки. Довольно часто close() вызывают без проверки возвращаемого значения. Это не очень грубая ошибка, но, тем не менее, иногда закрытие файла бывает неудачным (в случае неправильного дескриптора, в случае прерывания функции по сигналу или в случае ошибки ввода-вывода, например). В любом случае, если программа сообщит пользователю, что файл невозможно закрыть, это хорошо.
Теперь можно написать простенкую программу, использующую системные вызовы open() и close(). Мы еще не умеем читать из файлов и писать в файлы, поэтому напишем программу, которая создает файл с именем, переданным в качестве аргумента (argv[1]) и с правами доступа 0600 (чтение и запись для пользователя). Ниже приведен исходный код программы.
/* openclose.c */ #include /* open() and O_XXX flags */ #include /* S_IXXX flags */ #include /* mode_t */ #include /* close() */ #include #include int main (int argc, char ** argv) < int fd; mode_t mode = S_IRUSR | S_IWUSR; int flags = O_WRONLY | O_CREAT | O_EXCL; if (argc < 2) < fprintf (stderr, "openclose: Too few arguments\n"); fprintf (stderr, "Usage: openclose \n"); exit (1); > fd = open (argv[1], flags, mode); if (fd < 0) < fprintf (stderr, "openclose: Cannot open file '%s'\n", argv[1]); exit (1); >if (close (fd) != 0) < fprintf (stderr, "Cannot close file (descriptor=%d)\n", fd); exit (1); >exit (0); >
Обратите внимание, если запустить программу дважды с одним и тем же аргументом, то на второй раз open() выдаст ошибку. В этом виноват флаг O_EXCL (см. Таблицу 4 Приложения 2), который «дает добро» только на создание еще не существующих файлов. Наглядности ради, флаги открытия и флаги режима мы занесли в отдельные переменные, однако можно было бы сделать так:
fd = open (argv[1], O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
fd = open (argv[1], O_WRONLY | O_CREAT | O_EXCL, 0600);
5.5. Чтение файла: системный вызов read()
Системный вызов read(), объявленный в файле unistd.h, позволяет читать данные из файла. В отличие от библиотечных функций файлового ввода-вывода, которые предоставляют возможность интерпретации считываемых данных. Можно, например, записать в файл следующее содержимое:
2006
Теперь, используя библиотечные механизмы, можно читать файл по-разному:
fscanf (filep, "%s", buffer); fscanf (filep, "%d", number);
Системный вызов read() читает данные в «сыром» виде, то есть как последовательность байт, без какой-либо интерпретации. Ниже представлен адаптированный прототип read().
ssize_t read (int fd, void * buffer, size_t count);
Первый аргумент — это файловый дескриптор. Здесь больше сказать нечего. Второй аргумент — это указатель на область памяти, куда будут помещаться данные. Третий аргумент — количество байт, которые функция read() будет пытаться прочитать из файла. Возвращаемое значение — количество прочитанных байт, если чтение состоялось и -1, если произошла ошибка. Хочу заметить, что если read() возвращает значение меньше count, то это не символизирует об ошибке.
Хочу сказать несколько слов о типах. Тип size_t в Linux используется для хранения размеров блоков памяти. Какой тип реально скрывается за size_t, зависит от архитектуры; как правило это unsigned long int или unsigned int. Тип ssize_t (Signed SIZE Type) — это тот же size_t, только знаковый. Используется, например, в тех случаях, когда нужно сообщить об ошибке, вернув отрицательный размер блока памяти. Системный вызов read() именно так и поступает.
Теперь напишем программу, которая просто читает файл и выводит его содержимое на экран. Имя файла будет передаваться в качестве аргумента (argv[1]). Ниже приведен исходный код этой программы.
/* myread.c */ #include #include #include #include #include #include int main (int argc, char ** argv) < int fd; ssize_t ret; char ch; if (argc < 2) < fprintf (stderr, "Too few arguments\n"); exit (1); >fd = open (argv[1], O_RDONLY); if (fd < 0) < fprintf (stderr, "Cannot open file\n"); exit (1); >while ((ret = read (fd, &ch, 1)) > 0) < putchar (ch); >if (ret < 0) < fprintf (stderr, "myread: Cannot read file\n"); exit (1); >close (fd); exit (0); >
В этом примере используется укороченная версия open(), так как файл открывается только для чтения. В качестве буфера (второй аргумент read()) мы передаем адрес переменной типа char. По этому адресу будут считываться данные из файла (по одному байту за раз) и передаваться на стандартный вывод. Цикл чтения файла заканчивается, когда read() возвращает нуль (нечего больше читать) или -1 (ошибка). Системный вызов close() закрывает файл.
Как можно заметить, в нашем примере системный вызов read() вызывается ровно столько раз, сколько байт содержится в файле. Иногда это действительно нужно; но не здесь. Чтение-запись посимвольным методом (как в нашем примере) значительно замедляет процесс ввода-вывода за счет многократных обращений к системным вызовам. По этой же причине возрастает вероятность возникновения ошибки. Если нет действительной необходимости, файлы нужно читать блоками. О том, какой размер блока предпочтительнее, будет рассказано в последующих главах книги. Ниже приведен исходный код программы, которая делает то же самое, что и предыдущий пример, но с использованием блочного чтения файла. Размер блока установлен в 64 байта.
/* myread1.c */ #include #include #include #include #include #include #define BUFFER_SIZE 64 int main (int argc, char ** argv) < int fd; ssize_t read_bytes; char buffer[BUFFER_SIZE+1]; if (argc < 2) < fprintf (stderr, "Too few arguments\n"); exit (1); >fd = open (argv[1], O_RDONLY); if (fd < 0) < fprintf (stderr, "Cannot open file\n"); exit (1); >while ((read_bytes = read (fd, buffer, BUFFER_SIZE)) > 0) < buffer[read_bytes] = 0; /* Null-terminator for C-string */ fputs (buffer, stdout); >if (read_bytes < 0) < fprintf (stderr, "myread: Cannot read file\n"); exit (1); >close (fd); exit (0); >
Теперь можно примерно оценить и сравнить скорость работы двух примеров. Для этого надо выбрать в системе достаточно большой файл (бинарник ядра или видеофильм, например) и посмотреть на то, как быстро читаются эти файлы:
$ time ./myread /boot/vmlinuz > /dev/null real 0m1.443s user 0m0.383s sys 0m1.039s $ time ./myread1 /boot/vmlinuz > /dev/null real 0m0.055s user 0m0.010s sys 0m0.023s $
5.6. Запись в файл: системный вызов write()
Для записи данных в файл используется системный вызов write(). Ниже представлен его прототип.
ssize_t write (int fd, const void * buffer, size_t count);
Как видите, прототип write() отличается от read() только спецификатором const во втором аргументе. В принципе write() выполняет процедуру, обратную read(): записывает count байтов из буфера buffer в файл с дескриптором fd, возвращая количество записанных байтов или -1 в случае ошибки. Так просто, что можно сразу переходить к примеру. За основу возьмем программу myread1 из предыдущего раздела.
/* rw.c */ #include #include #include /* read(), write(), close() */ #include /* open(), O_RDONLY */ #include /* S_IRUSR */ #include /* mode_t */ #define BUFFER_SIZE 64 int main (int argc, char ** argv) < int fd; ssize_t read_bytes; ssize_t written_bytes; char buffer[BUFFER_SIZE]; if (argc < 2) < fprintf (stderr, "Too few arguments\n"); exit (1); >fd = open (argv[1], O_RDONLY); if (fd < 0) < fprintf (stderr, "Cannot open file\n"); exit (1); >while ((read_bytes = read (fd, buffer, BUFFER_SIZE)) > 0) < /* 1 == stdout */ written_bytes = write (1, buffer, read_bytes); if (written_bytes != read_bytes) < fprintf (stderr, "Cannot write\n"); exit (1); >> if (read_bytes < 0) < fprintf (stderr, "myread: Cannot read file\n"); exit (1); >close (fd); exit (0); >
В этом примере нам уже не надо изощеряться в попытках вставить нуль-терминатор в строку для записи, поскольку системный вызов write() не запишет большее количество байт, чем мы ему указали. В данном случае для демонстрации write() мы просто записывали данные в файл с дескриптором 1, то есть в стандартный вывод. Но прежде, чем переходить к чтению следующего раздела, попробуйте самостоятельно записать что-нибудь (при помощи write(), естественно) в обычный файл. Когда будете открывать файл для записи, обратите пожалуйста внимание на флаги O_TRUNC, O_CREAT и O_APPEND. Подумайте, все ли флаги сочетаются между собой по смыслу.
5.7. Произвольный доступ: системный вызов lseek()
Как уже говорилось, с каждым открытым файлом связано число, указывающее на текущую позицию чтения-записи. При открытии файла позиция равна нулю. Каждый вызов read() или write() увеличивает текущую позицию на значение, равное числу прочитанных или записанных байт. Благодаря этому механизму, каждый повторный вызов read() читает следующие данные, и каждый повторный write() записывает данные в продолжение предыдущих, а не затирает старые. Такой механизм последовательного доступа очень удобен, однако иногда требуется получить произвольный доступ к содержимому файла, чтобы, например, прочитать или записать файл заново.
Для изменения текущей позиции чтения-записи используется системный вызов lseek(). Ниже представлен его прототип.
off_t lseek (int fd, ott_t offset, int against);
Первый аргумент, как всегда, — файловый дескриптор. Второй аргумент — смещение, как положительное (вперед), так и отрицательное (назад). Третий аргумент обычно передается в виде одной из трех констант SEEK_SET, SEEK_CUR и SEEK_END, которые показывают, от какого места отсчитывается смещение. SEEK_SET — означает начало файла, SEEK_CUR — текущая позиция, SEEK_END — конец файла. Рассмотрим следующие вызовы:
lseek (fd, 0, SEEK_SET); lseek (fd, 20, SEEK_CUR); lseek (fd, -10, SEEK_END);
Первый вызов устанавливает текущую позицию в начало файла. Второй вызов смещает позицию вперед на 20 байт. В третьем случае текущая позиция перемещается на 10 байт назад относительно конца файла.
В случае удачного завершения, lseek() возвращает значение установленной «новой» позиции относительно начала файла. В случае ошибки возвращается -1.
Я долго думал, какой бы пример придумать, чтобы продемонстрировать работу lseek() наглядным образом. Наиболее подходящим примером мне показалась идея создания программы рисования символами. Программа оказалась не слишком простой, однако если вы сможете разобраться в ней, то можете считать, что успешно овладели азами низкоуровневого ввода-вывода Linux. Ниже представлен исходный код этой программы.
/* draw.c */ #include #include #include #include #include #include #include /* memset() */ #define N_ROWS 15 /* Image height */ #define N_COLS 40 /* Image width */ #define FG_CHAR 'O' /* Foreground character */ #define IMG_FN "image" /* Image filename */ #define N_MIN(A,B) ((A)<(B)?(A):(B)) #define N_MAX(A,B) ((A)>(B)?(A):(B)) static char buffer[N_COLS]; void init_draw (int fd) < ssize_t bytes_written = 0; memset (buffer, ' ', N_COLS); buffer [N_COLS] = '\n'; while (bytes_written < (N_ROWS * (N_COLS+1))) bytes_written += write (fd, buffer, N_COLS+1); >void draw_point (int fd, int x, int y) < char ch = FG_CHAR; lseek (fd, y * (N_COLS+1) + x, SEEK_SET); write (fd, &ch, 1); >void draw_hline (int fd, int y, int x1, int x2) < size_t bytes_write = abs (x2-x1) + 1; memset (buffer, FG_CHAR, bytes_write); lseek (fd, y * (N_COLS+1) + N_MIN (x1, x2), SEEK_SET); write (fd, buffer, bytes_write); >void draw_vline (int fd, int x, int y1, int y2) < int i = N_MIN(y1, y2); while (i int main (void) < int a, b, c, i = 0; char ch; int fd = open (IMG_FN, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) < fprintf (stderr, "Cannot open file\n"); exit (1); >init_draw (fd); char * icode[] = < "v 1 1 11", "v 11 7 11", "v 14 5 11", "v 18 6 11", "v 21 5 10", "v 25 5 10", "v 29 5 6", "v 33 5 6", "v 29 10 11", "v 33 10 11", "h 11 1 8", "h 5 16 17", "h 11 22 24", "p 11 5 0", "p 15 6 0", "p 26 11 0", "p 30 7 0", "p 32 7 0", "p 31 8 0", "p 30 9 0", "p 32 9 0", NULL >; while (icode[i] != NULL) < sscanf (icode[i], "%c %d %d %d", &ch, &a, &b, &c); switch (ch) < case 'v': draw_vline (fd, a, b, c); break; case 'h': draw_hline (fd, a, b, c); break; case 'p': draw_point (fd, a, b); break; default: abort(); >i++; > close (fd); exit (0); >
Теперь разберемся, как работает эта программа. Изначально «полотно» заполняется пробелами. Функция init_draw() построчно записывает в файл пробелы, чтобы получился «холст», размером N_ROWS на N_COLS. Массив строк icode в функции main() — это набор команд рисования. Команда начинается с одной из трех литер: ‘v’ — нарисовать вертикальную линию, ‘h’ — нарисовать горизонтальную линию, ‘p’ — нарисовать точку. После каждой такой литеры следуют три числа. В случае вертикальной линии первое число — фиксированная координата X, а два других числа — это начальная и конечная координаты Y. В случае горизонтальной линии фиксируется координата Y (первое число). Два остальных числа — начальная координата X и конечная координата X. При рисовании точки используются только два первых числа: координата X и координата Y. Итак, функция draw_vline() рисует вертикальную линию, функция draw_hline() рисует горизонтальную линию, а draw_point() рисует точку.
Функция init_draw() пишет в файл N_ROWS строк, каждая из которых содержит N_COLS пробелов, заканчивающихся переводом строки. Это процедура подготовки «холста».
Функция draw_point() вычисляет позицию (исходя из значений координат), перемещает туда текущую позицию ввода-вывода файла, и записывает в эту позицию символ (FG_CHAR), которым мы рисуем «картину».
Функция draw_hline() заполняет часть строки символами FG_CHAR. Так получается горизонтальная линия. Функция draw_vline() работает иначе. Чтобы записать вертикальную линию, нужно записывать по одному символу и каждый раз «перескакивать» на следующую строку. Эта функция работает медленнее, чем draw_hline(), но иначе мы не можем.
Полученное изображение записывается в файл image. Будьте внимательны: чтобы разгрузить исходный код, из программы исключены многие проверки (read(), write(), close(), диапазон координат и проч.). Попробуйте включить эти проверки самостоятельно.
Include unistd h что это
#include
int link(const char *oldpath, const char *newpath);
#include /* определения констант of AT_* */
#include
int linkat(int olddirfd, const char *oldpath,
int newdirfd, const char *newpath, int flags); Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)): linkat(): Начиная с glibc 2.10: _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L До glibc 2.10: _ATFILE_SOURCE
ОПИСАНИЕ
link() создаёт новую ссылку (также называемую жёсткую ссылку) на существующий файл. Если newpath существует, то он не будет перезаписан. Это новое имя может использоваться как и старое для любых операций; оба имени ссылаются на один файл (и имеют одинаковые права и владельцев) и невозможно сказать какое имя считать «изначальным».
linkat()
Системный вызов linkat() работает также как системный вызов link(), за исключением случаев, описанных здесь. Если в oldpath задан относительный путь, то он считается относительно каталога, на который ссылается файловый дескриптор olddirfd (а не относительно текущего рабочего каталога вызывающего процесса, как это делается в link()). Если в oldpath задан относительный путь и olddirfd равно специальному значению AT_FDCWD, то oldpath рассматривается относительно текущего рабочего каталога вызывающего процесса (как link()). Если в oldpath задан абсолютный путь, то olddirfd игнорируется. Значение newpath интерпретируется как oldpath, за исключением того, что относительный путь интерпретируется относительно каталога, на который ссылается файловый дескриптор newdirfd. Следующие значения можно побитово (OR) объединять в flags: AT_EMPTY_PATH (начиная с Linux 2.6.39) Если значение oldpath равно пустой строке, то создаётся ссылка на файл, на который указывает olddirfd (который может быть получен с помощью open(2) с флагом O_PATH). В этом случае olddirfd может указывать на файл любого типа, а не только на каталог. Это, обычно, не работает, если файл имеет счётчик ссылок равный нулю (исключение — файлы, созданные с флагом O_TMPFILE и без O_EXCL). Вызывающий должен иметь мандат CAP_DAC_READ_SEARCH, чтобы использовать этот флаг. Данный флаг есть только в Linux; для получения его определения определите _GNU_SOURCE. AT_SYMLINK_FOLLOW (начиная с Linux 2.6.18) По умолчанию, linkat() не разыменовывает oldpath, если это символьная ссылка (подобно link()). В flags можно указать флаг AT_SYMLINK_FOLLOW, и тогда значение oldpath будет разыменовано, если это символьная ссылка. Если смонтирована procfs, то это может быть использовано как альтернатива AT_EMPTY_PATH, например:
linkat(AT_FDCWD, "/proc/self/fd/", newdirfd, newname, AT_SYMLINK_FOLLOW);
До версии ядра 2.6.18 аргумент flags не использовался и должен был быть равен 0. Смотрите в openat(2) объяснение необходимости linkat().
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении возвращается 0. В случае ошибки возвращается -1, а errno устанавливается в соответствующее значение.
ОШИБКИ
EACCES Нет прав на запись в каталог, содержащий newpath, или в одном из каталогов пути до oldpath или newpath не разрешён поиск (см. также path_resolution(7)). EDQUOT Исчерпана пользовательская квота на дисковые блоки файловой системы. EEXIST newpath уже существует. EFAULT Значения oldpath и newpath указывают за пределы доступного адресного пространства. EIO Произошла ошибка ввода-вывода. ELOOP Во время определения oldpath или newpath встретилось слишком много символьных ссылок. EMLINK На файл, на который указывает oldpath, уже создано максимальное количество ссылок. ENAMETOOLONG Слишком длинное значение аргумента oldpath или newpath. ENOENT Компонент каталога в oldpath или newpath не существует или является повисшей ссылкой. ENOMEM Недостаточное количество памяти ядра. ENOSPC На устройстве, содержащем файл, нет места для создания нового элемента каталога. ENOTDIR Компонент, используемый как каталог в oldpath или newpath, на самом деле не является каталогом. EPERM oldpath является каталогом. EPERM Файловая система, содержащая oldpath и newpath, не поддерживает создание жёстких ссылок. EPERM (начиная с Linux 3.6) Вызывающий не имеет прав для создания жёстких ссылок на этот файл (смотрите описание /proc/sys/fs/protected_hardlinks в proc(5)). EROFS Файл расположен в файловой системе, доступной только для чтения. EXDEV oldpath и newpath находятся не в одной смонтированной файловой системе (Linux позволяет монтировать файловую систему в несколько точек, но link() не работает с отличающими точками монтирования, даже если в них смонтирована идентичная файловая система). В linkat() дополнительно могут возникнуть следующие ошибки: EBADF В olddirfd или newdirfd указаны неправильные файловые дескрипторы. EINVAL В flags указано неверное значение флага. ENOENT Флаг AT_EMPTY_PATH указан в flags, но вызывающий не имеет мандата CAP_DAC_READ_SEARCH. ENOENT Предпринята попытка сделать ссылку на файл /proc/self/fd/NN, который соответствует файловому дескриптору, созданному с помощью
open(path, O_TMPFILE | O_EXCL, mode); Смотрите open(2). ENOENT Значение oldpath является относительным путём и olddirfd ссылается на каталог, который был удалён, или newpath является относительным путём и newdirfd ссылается на каталог, который был удалён. ENOTDIR Значение oldpath содержит относительный путь и olddirfd содержит файловый дескриптор, указывающий на файл, а не на каталог; или произошло тоже самое с newpath и newdirfd. EPERM Значение AT_EMPTY_PATH было указано в flags, значение oldpath равно пустой строке и olddirfd указывает на каталог.
ВЕРСИИ
Вызов linkat() был добавлен в ядро Linux версии 2.6.16; поддержка в glibc доступна с версии 2.4.
СООТВЕТСТВИЕ СТАНДАРТАМ
link(): SVr4, 4.3BSD, POSIX.1-2001 (но смотрите ЗАМЕЧАНИЯ), POSIX.1-2008. linkat(): POSIX.1-2008.
ЗАМЕЧАНИЯ
Жёсткие ссылки, создаваемые link(), не могут располагаться в разных файловых системах. Если это необходимо, используйте symlink(2). В POSIX.1-2001 сказано, что link() должен разыменовывать oldpath, если он является символьной ссылкой. Однако, начиная с ядра версии 2.0, Linux также не поступает: если oldpath является символьной ссылкой, то newpath создаётся как (жёсткая) ссылка на файл этой символьной ссылки (т.е., newpath становится символьной ссылкой на файл, содержащий ссылку на oldpath). Некоторые другие реализации поступают также как Linux. В POSIX.1-2008 изменено описание link(), теперь разыменование символьной ссылки oldpath зависит от реализации. Для точного управления по обработке символьных ссылок при создании используйте linkat(2).
Замечания по glibc
В старых ядрах, где linkat() отсутствует (и если не указан флаг AT_SYMLINK_NOFOLLOW), обёрточная функция glibc использует link(). Если oldpath и newpath являются относительными путями, то glibc собирает пути относительно символической ссылки в /proc/self/fd, которая соответствует аргументам olddirfd и newdirfd.
ДЕФЕКТЫ
В файловых системах NFS возвращаемый код может быть неправильным, если сервер NFS выполнит создание ссылки и завершит работу до возврата результата операции. Используйте stat(2), чтобы выяснить была ли создана ссылка.
Лучшие ответы пользователя
Как создать папку на рабочем столе средствами c++?

В c++ 17 версии имеется библиотека filesystem, позволяющаяя производить некоторые манипуляции с файловой системой компьютера. Что касательно вашего вопроса там есть такая функция как bool create_directory(. ). Данная функция принимает в качестве аргумента путь конец которого — имя папки для создания.
В Windows рабочий стол находится по пути:
/users//desktop/
В Unix-like (BSD, Linux и т.д.) по пути:
/home//desktop/
И того нам нужно получить имя юзера:
В Windows это как-то так:
#include #include char username[UNLEN+1]; //
Итого, код для Windows будет выглядеть как-то так:
#include #include #include #include namespace fs = std::filesystem; // Для краткости bool createDesktopDir(std::string dir_name) < char username[UNLEN+1]; DWORD username_len = UNLEN+1; GetUserName(username, &username_len); return fs::create_directory(std::string("/users/") + username + "/desktop/" + dir_name) >
Для Unix-like ОС получения юзера выглядит так:
#include char username[1024] = ; getlogin_r(username, sizeof(username)-1);
А следовательно код для создания директории будет такой:
#include #include #include namespace fs = std::filesystem; bool createDesktopDir(std::string dir_name) < char username[1024] = ; getlogin_r(username, sizeof(username)-1); return fs::create_directory(std::string("/home/") + username + "/desktop/" + dir_name) >
Для использования filesystem могут потребоваться дополнительные параметры компилятора / компоновщика. Реализация GNU до 9.1 (то есть GCC) требует связывания с -lstdc++fs, а реализация LLVM до LLVM 9.0 (то есть clang) требует связывания с -lc++fs.
С Unix могут быть проблемы если вы запустили программу от root поскольку его домашняя директория находится по адресу /root/, а следовательно и рабочий стол тут /root/desktop/. Так же могут быть проблемы когда директории в Linux имеют русские имена например /home//Рабочий\ стол, надо как-то обработать этот момент, благо GCC и clang хотябы умеют в Unicode. В Windows же могут быть проблемы с компиляторами MinGW если имя пользователя на русском (или другом языкие содержащим Unicode-символы) ибо использовать в путях на текущий момент они могут только в ASCII-символы, то есть только латиница. Тут либо кушать кактус и кидать из кодировки в кодировку, либо юзать visual c++. В общем удачи