Базовые операции с файловой системой unix

Содержание:

ОПИСАНИЕ

writecountbuffd

Количество записанных байт может быть меньше чем count если, например,
недостаточно места на физическом носителе, или исчерпан отведённый лимит
ресурса RLIMIT_FSIZE (см. setrlimit(2)), или вызов был прерван
обработчиком сигналов после уже записанных меньше чем count
байт. (См. также pipe(7).)

В случае с файлами, разрешающими позиционирование (т.е., к которым можно
применить lseek(2), например, обычные файлы), запись производится по
текущему файловому смещению, а смещение файла увеличивается на реальное
число записанных байт. Если файл был открыт с помощью open(2) с
аргументом O_APPEND, то перед записью файловое смещение устанавливается в
конец файла. Согласование файлового смещения и операции записи выполняются
атомарно.

Другие системы

шест. имя ls восм. описание
f000 S_IFMT 170000 маска типа файла
0000 000000 в SCO — недействующий inode; в BSD — неизвестный тип; в SVID-v2 и XPG2 —
0 и 0100000 означают обычный файл
1000 S_IFIFO p| 010000 FIFO (именованный канал)
2000 S_IFCHR c 020000 символьный специальный (V7)
3000 S_IFMPC 030000 мультиплексированный символьный
специальный (V7)
4000 S_IFDIR d/ 040000 каталог (V7)
5000 S_IFNAM 050000 в XENIX — именованный специальный файл с двумя подтипами, различающимися
значениями st_rdev — 1, 2
0001 S_INSEM s 000001 подтип IFNAM семафора XENIX
0002 S_INSHD m 000002 подтип IFNAM общих данных XENIX
6000 S_IFBLK b 060000 блочный специальный (V7)
7000 S_IFMPB 070000 мультиплексированный блочный
специальный (V7)
8000 S_IFREG 100000 обычный (V7)
9000 S_IFCMP 110000 VxFS: сжатый
9000 S_IFNWK n 110000 сетевой специальный (HP-UX)
a000 S_IFLNK 120000 символьная ссылка (BSD)
b000 S_IFSHAD 130000 в Solaris — теневой inode для ACL (не виден пользовательскими процессами)
c000 S_IFSOCK s= 140000 сокет (BSD; также «S_IFSOC» в VxFS)
d000 S_IFDOOR D> 150000 Solaris: дверь
e000 S_IFWHT w% 160000 BSD whiteout (не используется для inode)
0200 S_ISVTX 001000

закрепляющий бит: сохраняет код программы в файле подкачки даже после использования (V7)

зарезервировано (SVID-v2)

для не каталогов: не кэшировать этот файл (SunOS)

для каталогов: флаг ограниченного удаления (SVID-v4.2)

0400 S_ISGID 002000

set-group-ID при выполнении (V7)

для каталогов: использовать семантику BSD для распространения GID

0400 S_ENFMT 002000 жёсткая блокировка файлов в стиле System V (общий c S_ISGID)
0800 S_ISUID 004000 set-user-ID на выполнение (V7)
0800 S_CDF 004000 каталог является файлом, зависящим от контекста (HP-UX)

fstatat()

fstatatstat

Если в pathname задан относительный путь, то он считается относительно
каталога, на который ссылается файловый дескриптор dirfd (а не
относительно текущего рабочего каталога вызывающего процесса, как это
делается в stat()).

Если в pathname задан относительный путь и значение dirfd равно
AT_FDCWD, то pathname рассматривается относительно текущего рабочего
каталога вызывающего процесса (как stat()).

Если в pathname задан абсолютный путь, то dirfd игнорируется.

Значение flags может быть 0, или включать один или более следующих
флагов:

AT_EMPTY_PATH (начиная с Linux 2.6.39)
Если значение pathname равно пустой строке, то выполнять действие над
файлом, на который указывает dirfd (который может быть получен с помощью
open(2) с флагом O_PATH). Если dirfd равно AT_FDCWD, то вызов
выполняет действие над текущим рабочим каталогом. В этом случае, dirfd
может указывать на файл любого типа, а не только на каталог. Этот флаг есть
только в Linux; для получения его определения определите _GNU_SOURCE.
AT_NO_AUTOMOUNT (начиная с Linux 2.6.38)
Не выполнять автоматическое монтирование конечного компонента («basename»)
pathname, если это каталог, который является точкой монтирования. Это
позволяет вызывающему получить атрибуты точки монтирования (а не
расположения, где её предполагалось смонтировать). Этот флаг можно
использовать в инструментах, сканирующих каталоги, для предотвращения
массового автоматического монтирования каталогов в их точки
монтирования. Флаг AT_NO_AUTOMOUNT не учитывается, если к точке уже уже
была выполнено монтирование. Этот флаг есть только Linux; для его получения
нужно задать _GNU_SOURCE.
AT_SYMLINK_NOFOLLOW
Если значение pathname является символьной ссылкой, не разыменовывать её,
а вернуть информацию о самой ссылке, как это делается в lstat(). (По
умолчанию, fstatat() разыменовывает символьные ссылки как и stat().)

vfork() — оптимизация

Казалось бы, когда при fork() процесс копируется, выполняется лишняя работа, ведь далее результаты этого копирования никому не нужны: дочерний процесс вызовет exec(), и всё, начнёт работать совсем другая программа.

Разработчики подумали об этом. И был введён отдельный системный вызов.

#include <sys/types.h>
#include <unistd.h>
 
pid_t vfork(void);

По смыслу он аналогичен fork(). Но здесь новый дочерний процесс имеет право после начала жизни сделать только следующее:

  • выйти через _exit(),
  • загрузить новую программу. Стандарт POSIX разрешает делать это через любую функцию из семейства exec(). Документация к Linux гласит, что допускается использовать лишь вариант execve(). Выходит, что в этом месте ОС Linux не полностью следует стандарту и отходит от него. Дело в том, что в реализации execl() и подобных функций-обёрток могут выполняться небезопасные действия: выделение памяти под аргументы, конкатенация фрагментов пути и пр.

Если дочерний процесс будет делать что-то ещё, это будет неопределённым поведением. Нельзя даже выходить через return из функции, нельзя использовать exit(), только _exit().

В Linux при vfork() родительский процесс блокируется, дочерний использует его виртуальное адресное пространство, включая стек.

В целом vfork() считается небезопасным. Не рекомендуется использовать его без веских оснований.

  • Легко случайно сделать недопустимое действие и получить неопределённое поведение.
  • Недоступен способ передачи сообщения об ошибке через пайп.
  • Если придёт сигнал в момент выполнения кода дочернего процесса, обработчик начнёт выполняться в адресном пространстве родителя, и могут быть проблемы.

ДЕФЕКТЫ

Следующие функции должны выполняться атомарно по отношению друг к другу,
чтобы работать с обычными файлами или символическими ссылками так, как
указано в POSIX.1-2008: …

Среди перечисленных в программном интерфейсе есть write() и
writev(2). И среди действий, которые должны выполняться атомарно между
нитями (и процессами), если обновление файлового смещения. Однако в Linux до
версии 3.14 это было не так: если два процесса с общим открытым файловым
описанием (смотрите open(2)) выполняют write() (или writev(2))
одновременно, то операции ввода-вывода не атомарны при обновлении файлового
смещения; в результате записанные двумя процессами блоки данных могут
(некорректно) перекрываться. Эта ошибка исправлена в Linux 3.14.

6 ответов

123

Лучший ответ

Так как мы не можем найти версию в Интернете, давайте начнем ее здесь.
Большинство портов для Windows, вероятно, требуется только подмножество полного Unix файла.
Здесь отправная точка. При необходимости добавьте определения.

05 май 2009, в 18:58
Поделиться

87

Попробуйте включить файл . Это похоже на эквивалент Visual Studio .

Надеюсь, это поможет.

18 нояб. 2009, в 23:16
Поделиться

21

Я бы рекомендовал использовать mingw/msys в качестве среды разработки. Особенно, если вы портируете простые консольные программы. Msys реализует Unix-подобную оболочку в Windows, а mingw — это порт сборник компилятора GNU (GCC) и другие инструменты сборки GNU для Windows Платформа. Это проект с открытым исходным кодом и хорошо подходит для решения этой задачи. В настоящее время я использую его для создания служебных программ и консольных приложений для Windows XP, и у него наверняка есть заголовок , который вы ищете.

Процедура установки может быть немного сложной, но я обнаружил, что лучшее место для начала — MSYS.

04 дек. 2008, в 22:21
Поделиться

13

Я наткнулся на этот поток, пытаясь найти альтернативу Windows для (определенную в ). Оказывается, что включение делает трюк. Возможно, это помогает людям, которые найдут эту тему в будущем.

23 нояб. 2011, в 15:01
Поделиться

5

Нет, IIRC не существует getopt() в Windows.

Boost, однако имеет program_options библиотека… которая работает нормально. Сначала это будет казаться излишним, но это не страшно, особенно учитывая, что он может обрабатывать параметры настройки в файлах конфигурации и переменных среды в дополнение к параметрам командной строки.

05 дек. 2008, в 00:08
Поделиться

Создайте собственный заголовок unistd.h и включите необходимые заголовки для прототипов функций.

04 дек. 2008, в 20:45
Поделиться

Ещё вопросы

  • 210Есть ли хорошая замена Valgrind для Windows?
  • 197Утверждает ли это зло?
  • 188Защита исполняемого файла от обратного инжиниринга?
  • 186Разница между использованием Makefile и CMake для компиляции кода
  • 169Почему поведение целочисленного переполнения без знака определяется, а переполнение целого числа без знака?
  • 167В C ++ я плачу за то, что не ем?
  • 165оператор возврата против выхода () в main ()
  • 139Экзотические архитектуры, о которых заботятся комитеты по стандартам
  • 132Захватывать символы со стандартного ввода, не дожидаясь нажатия клавиши ввода
  • 137Что значит void в C, C ++ и C #?

Проблема обработки ошибок

Ошибки могут случаться на разных стадиях.

  1. Неудачный вызов fork: возвращается −1, дочерний процесс вообще не порождается.
  2. Неудачный вызов exec в дочернем процессе. Например, указан несуществующий путь к запускаемой программе. В случае успеха функция exec не возвращает управление совсем, в случае неуспеха возвращается −1.
  3. Вызов exec был успешен, дочерний процесс начал работу и завершился с ошибкой.

Возникает вопрос, как сообщить об ошибке в родительский процесс и уметь разделять второй и третий случаи.

Допустим, мы решили, что в случае неудачного exec наш дочерний процесс будет завершаться с кодом X, родительский процесс сможет прочитать этот код через waitpid. Но что если exec был успешен, а новый процесс сам по себе вышел с кодом X. Спрашивается, как мы в родительском процессе сможем это различать.

Есть классический трюк для решения этой проблемы с использованием анонимного канала, который мы рассмотрим далее.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
 
void Run(const char* program, const char* arg) {
    printf("P> Running %s %s\n", program, arg);
    pid_t pid = fork();
 
    if (pid == -1) {
        fprintf(stderr, "Unable to fork\n");
    } else if (pid > ) {
        printf("P> I am parent %d\n", getpid());
        printf("P> Child is %d\n", pid);
        int status;
        // wait(&status);
        waitpid(pid, &status, );
        printf("P> Wait OK\n");
        if (WIFEXITED(status)) {
            printf("P> Exit code = %d\n", WEXITSTATUS(status));
        }
        printf("\n");
    } else {
        // we are the child            
        printf("C> I am child %d of %d\n", getpid(), getppid());
        if (execlp(program, program, arg, NULL) == -1) {
            fprintf(stderr, "Unable to exec\n");
            _exit(42);
        }
    }
}
 
int main() {
    Run("ls", ".");
    Run("ls", "/usr/");
    Run("ls", "/abacaba/");
    Run("ls2", "/usr/");
    return ;
}
P> Running ls .
P> I am parent 16379
P> Child is 16380
C> I am child 16380 of 16379
a.out  main.c
P> Wait OK
P> Exit code = 0

P> Running ls /usr/
P> I am parent 16379
P> Child is 16381
C> I am child 16381 of 16379
bin  games  include  lib  local  locale  sbin  share  src
P> Wait OK
P> Exit code = 0

P> Running ls /abacaba/
P> I am parent 16379
P> Child is 16382
C> I am child 16382 of 16379
ls: cannot access '/abacaba/': No such file or directory
P> Wait OK
P> Exit code = 2

P> Running ls2 /usr/
P> I am parent 16379
P> Child is 16383
C> I am child 16383 of 16379
Unable to exec
P> Wait OK
P> Exit code = 42

ОПИСАНИЕ

На стадии компиляции это осуществляется при помощи включения
<unistd.h> и/или <limits.h> и проверки значений
определённых макросов.

Во время выполнения можно запрашивать числовые значение посредством функции
sysconf(). Можно запросить числовые значения, которые могут зависеть от
файла в файловой системе, с помощью вызовов fpathconf(3) и
pathconf(3). Строковые значения можно запрашивать с помощью
confstr(3).

Значения, полученные с помощью этих функций, являются системными
настроечными константами. Они не изменятся пока выполняется процесс.

Для параметров, как правило, используются константы вида _POSIX_FOO,
которые могут быть определены в <unistd.h>. Если параметр не
определён, то его можно запросить во время выполнения. Если он определён со
значением -1, то этот параметр не поддерживается. Если его значение равно 0,
то соответствующие функции и заголовочные файлы существуют, но нужно во
время выполнения запрашивать степень поддержки. Если он определён со
значениями не -1 и 0, то параметр поддерживается. Обычно значение (например
200112L) отражает год и месяц версии POSIX, в которой описан параметр. В
glibc используется значение 1, означающее, что поддержка в версии POSIX пока
не опубликована. В этом случае аргумент sysconf() будет выглядеть как
_SC_FOO. Список параметров смотрите в posixoptions(7).

ОШИБКИ

EAGAIN
Файловый дескриптор fd указывает на файл, не являющийся сокетом и
помеченный как неблокирующий ввод/вывод (O_NONBLOCK), а запись вызовет
блокировку. См. open(2) для дальнейшей информации по флагу O_NONBLOCK.
EAGAIN или EWOULDBLOCK
Файловый дескриптор fd указывает на сокет, который помечен как
неблокирующий ввод/вывод (O_NONBLOCK), а запись вызовет блокировку. По
POSIX.1-2001 разрешено возвращать ошибку в обоих случая и не требуется,
чтобы эти константы имели одно значение, поэтому переносимые приложения
должны проверять обе причины.
EBADF
Значение fd не является правильным файловым дескриптором или он не открыт
для записи.
EDESTADDRREQ
Значение fd ссылается на сокет дейтаграмм, у которого с помощью
connect(2) не назначен адрес другой стороны.
EDQUOT
Исчерпана пользовательская квота на дисковые блоки файловой системы с
файлом, на который указывает fd.
EFAULT
buf находится за пределами доступного вам адресного пространства.
EFBIG
Попытка записать в файл, который превышает заданное при реализации
ограничение на размер файла или ограничение на размер файла для текущего
процесса, или запись в позицию после максимально разрешённого смещения.
EINTR
Этот вызов был прерван сигналом, перед тем как были записаны какие-либо
данные; см signal(7).
EINVAL
fd присоединён к объекту, который не подходит для записи; или файл был
открыт с указанием флага O_DIRECT, или неправильно выравнено адрес в
buf, значение count или текущее файловое смещение.
EIO
Во время изменения индексного дескриптора (inode) возникла низкоуровневая
ошибка ввода/вывода.
ENOSPC
На устройстве, содержащем файл, на который ссылается fd, нет свободного
места.
EPERM
Выполнение операции предотвращено опечатыванием (file seal); смотрите
fcntl(2).
EPIPE
fd ссылается на конвейер или сокет, у которого закрыто чтение. Когда
такое случается, пишущий процесс также получит сигнал SIGPIPE. (Таким
образом, возвращаемое значение можно будет увидеть только если программа
перехватывает, блокирует или игнорирует этот сигнал.)

ОБЗОР

#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>

int stat(const char *pathname, struct stat *buf);int fstat(int fd, struct stat *buf);int lstat(const char *pathname, struct stat *buf);#include <fcntl.h> #include <sys/stat.h>int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags);

Требования макроса тестирования свойств для glibc
(см. feature_test_macros(7)):

lstat():

/* glibc 2.19 и старее */ _BSD_SOURCE ||

/* начиная с glibc 2.20 */_DEFAULT_SOURCE ||

_XOPEN_SOURCE >= 500 || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED

|| /* начиная с glibc 2.10: */ _POSIX_C_SOURCE >= 200112L

fstatat():

Техника fork-exec

Придумана Д. Ритчи.

Создание нового процесса выполняется двумя системными вызовами.

  • После fork появляется новый дочерний процесс, который продолжает выполнять программу родителя.
  • В результате exec процесс переключается на выполнение другой программы.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
 
int main() {
    pid_t pid = fork();
 
    if (pid == -1) {
        fprintf(stderr, "Unable to fork\n");
    } else if (pid > ) {
        printf("I am parent %d\n", getpid());
        printf("Child is %d\n", pid);
        int status;
        // wait(&status);
        waitpid(pid, &status, );
        printf("Wait OK\n");
    } else {
        // we are the child            
        printf("I am child %d of %d\n", getpid(), getppid());
        if (execlp("ls", "ls", "-l", NULL) == -1) {
            fprintf(stderr, "Unable to exec\n");
        }
    }
}

ОБЗОР

#include <unistd.h>

int setpgid(pid_t pid, pid_t pgid);
pid_t getpgid(pid_t pid);

pid_t getpgrp(void); /* по версии POSIX.1 */
pid_t getpgrp(pid_t pid); /* по версии BSD */

int setpgrp(void); /* по версии System V */
int setpgrp(pid_t pid, pid_t pgid); /* по версии BSD */

Требования макроса тестирования свойств для glibc
(см. feature_test_macros(7)):

getpgid():

_XOPEN_SOURCE >= 500 || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED

|| /* начиная с glibc 2.12: */ _POSIX_C_SOURCE >= 200809L

setpgrp() (POSIX.1):

_SVID_SOURCE || _XOPEN_SOURCE >= 500 || _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED || /* начиная с glibc 2.19: */ _BSD_SOURCE

setpgrp() (BSD), getpgrp() (BSD) :

Поля с отметками времени

st_atimest_mtimest_ctimetime_t

Начиная с ядра 2.5.48, в структуре stat поддерживается наносекундная
точность для всех трёх полей времени. Наносекундные компоненты каждого метки
времени доступны под именами вида st_atim.tv_nsec, если определён макрос
тестирования свойств _BSD_SOURCE или _SVID_SOURCE. Сейчас
наносекундные метки времени стандартизованы, начиная с POSIX.1-2008, и,
начиная с версии 2.12, в glibc также есть поддержка имён наносекундных
компонент, если определён _POSIX_C_SOURCE со значением 200809L или более,
или _XOPEN_SOURCE со значением 700 или более. Если ни один из
вышеупомянутых макросов не определён, то наносекундные значения доступны под
именами вида st_atimensec.

exec()

exec запускает исполняемый файл в контексте уже существующего процесса, заменяя предыдущий исполняемый файл.

На самом деле, функции с названием exec не существует. Есть несколько функций из этого семейства:

#include <unistd.h>
 
extern char **environ;
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execv(const char *path, char *const argv);
int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[]*/);
int execve(const char *path, char *const argv, char *const envp);
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
int execvp(const char *file, char *const argv);
  • Версия с l принимает аргументы через varargs, версия с v принимает массив строк.
  • Версия с e позволяет дополнительно передать переменные окружения.
  • Версия с p ищет исполняемый файл в PATH, без p требует полный путь.

Все они реализованы как обёртки вокруг системного вызова execve().

Получение информации о файле

Кроме имен файлов, находящихся в каталоге может понадобиться некоторая дополнительная информация, которая внесла бы ясность в то, что надо делать дальше. По крайней мере, нельзя только по имени отличить файл от каталога.

Функция заполняет структуру информацией об определенном файле; если вместо имени файла имеется файловый дескриптор, то можно использовать его совместно с . Если также необходимо обнаруживать символические ссылки, то вместе с именем файла следует использовать .

В отличие от , которую возвращает , имеет довольно много обязательных стандартных полей:

  • – права доступа к файлу (пользователь, группа, остальные) и флаги.
  • – порядковый номер файла.
  • – устройство, на котором расположен файл.
  • – счетчик числа связей.
  • – идентификатор пользователя-владельца файла.
  • – идентификатор группы-владельца.
  • – размер файла в байтах (для файлов regular).
  • – время последнего доступа к файлу.
  • – время последней модификации файла.
  • – время создания файла.

Используя макрос для поля , можно определить тип файла:

  • – специальный блочный файл? (обычно это блочное устройство).
  • – специальный символьный файл? (обычно это символьное устройство).
  • – каталог?
  • – UNIX-канал (pipe) или файл типа FIFO?
  • – символическая ссылка?
  • – обычный файл?

Функция выполняется достаточно медленно на большинстве файловых систем, поэтому лучше будет хранить эту информацию в памяти на случай, если она понадобится позже.

Немного о символических ссылках

В обычной ситуации символические ссылки не заслуживают отдельного внимания. Результатом вызова для символической ссылки будет информация о файле, на который эта ссылка указывает. Это соответствует привычным для пользователя приемам работы, так как права на доступ к целевому файлу, а не к самой символической ссылке, управляют взаимодействием с этим файлом.

Некоторые приложения, такие как и программы для резервного копирования, должны быть способны отобразить информацию о самой символической ссылке, например, на какой файл она указывает. Подобного результата можно добиться, используя вместо ; этот способ пригодится на случай, если надо работать с самой символической ссылкой, а не с файлом, на который она указывает.

ОПИСАНИЕ

getpgrpsetpgid

Вызов setpgid() устанавливает PGID у процесса с идентификатором pid
равным pgid. Если значение pid равно 0, то используется идентификатор
вызывающего процесса. Если значение pgid равно 0, то PGID процесса,
указанного в pid, становится равным его идентификатору процесса. Если
setpgid() используется для перевода процесса из одной группы в другую
(это делают некоторые оболочки командной строки для объединения каналов
процессов), то обе группы процессов должны быть частью одного сеанса
(см. setsid(2) и credentials(7)). В этом случае в pgid указывается
существующая группа процессов, в которую нужно выполнить перевод и
идентификатор сеанса этой группы должен совпадать с идентификатором сеанса
переводимого процесса.

В версии POSIX.1 вызов getpgrp() без аргументов возвращает PGID
вызывающего процесса.

Вызов getpgid() возвращает PGID процесса с заданным pid. Если значение
pid равно нулю, то используется идентификатор вызывающего процесса
(получение PGID процесса, отличного от вызывающего, требуется редко, а для
этой задачи хорошо подходит POSIX.1 getpgrp()).

В версии System V вызов setpgrp() без аргументов эквивалентен
setpgid(0, 0).

В версии BSD вызов setpgrp() с аргументами pid и pgid является
обёрточной функцией, которая вызывает

    setpgid(pid, pgid)

Начиная с glibc 2.19, BSD-функция setpgrp() была удалена из
<unistd.h>; вызовы должны быть заменены на вызов setpgid(),
как показано выше.

В версии BSD вызов getpgrp() с аргументом pid является обёрточной
функцией, которая вызывает

    getpgid(pid)

Заключение

Применяйте функции и для просмотра содержимого каталога и определения дальнейших действий, которые необходимо выполнить над этими записями, и, возможно, для выполнения каких-то особенных, специфических задач во время обработки содержимого каталога. Хотя это весьма полезные функции, некоторые начинающие UNIX-разработчики считают их слишком сложными. Эта статья поможет таким UNIX-разработчикам быстро освоить и .

Похожие темы

  • eclipse.org: сообщество разработчиков в Eclipse Platform.
  • C/C++ development with the Eclipse Platform (EN) (developerWorks, март 2006): статья об использовании C++ с Eclipse.(EN)
  • Open Group’s POSIX 1003.1: спецификация с подробной информацией о функциях и .(EN)
  • Спецификация функции .(EN)
  • Спецификация функции .(EN)
  • Раздел developerWorks AIX and UNIX содержит сотни информативных статей для читателей начальной, средней и высокой квалификации.
  • Разделы библиотеки информации по темам AIX и UNIX:(EN)

    • Системное администрирование
    • Разработка приложений
    • Производительность
    • Переносимость
    • Безопасность
    • Подсказки
    • Инструментальные средства и утилиты
    • Java-технологии
    • Linux
    • Open source
  • IBM trial software: ознакомительные версии программного обеспечения для разработчиков, которые можно загрузить прямо со страницы сообщества developerWorks.(EN)
  • Podcasts: аудиозаписи презентаций экспертов IBM.(EN)
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *