Если в имени функции присутствует символ ‘l’, то аргументы arg командной строки передаются в виде списка arg0, arg1.... argn, NULL.
Если в имени функции присутствует символ ‘v’, то аргументы командной строки передаются в виде массива argv[]. Отдельные аргументы адресуются через argv[0], argv[1], ..., argv[n]. Последний аргумент (argv [n]) должен быть NULL.
Если в имени функции присутствует символ ‘e’, то последним аргументом функции является массив переменных среды envp[].
Если в имени функции присутствует символ ‘p’, то программа с именем file ищется не только в текущем каталоге, но и в каталогах, определенных переменной среды PATH.
Если в имени функции отсутствует символ ‘p’, то программа с именем path ищется только в текущем каталоге, или имя path должно указывать полный путь к файлу.
Функция execve(), является основной в семействе, остальные функции обеспечивают интерфейс к ней.
В случае успешного выполнения вызова функция не возвращает никакого результата. В случае ошибки возвращается -1, а глобальной переменной errno присваивается значение в соответствии с видом ошибки.
В стандарте POSIX именованный семафор создается следующим вызовом:
sem_t *sem_open(const char *name,
int oflag,
mode_t mode,
unsigned int value),
где:
name – имя семафора;
oflag – флаг, управляющий операцией создания семафора, при создании семафора необходимо указать флаг O_CREAT;
mode – права доступа к семафору, могут быть установлены, например, в 0644;
value – начальное состояние семафора.
Именованный семафор закрывается следующим вызовом:
int sem_close(sem_t *sem).
При входе в критический участок необходимо вызвать функцию:
int sem_wait(sem_t *sem).
При выходе из критического участка необходимо вызвать функцию:
int sem_post(sem_t *sem).
Именованный семафор удаляется следующим вызовом:
int sem_unlink(const char *name).
В стандарте SVID именованный семафор создается следующим вызовом:
int semget(key_t key, int nsems, int semflg);
где:
key_t key - ключ для создания уникального объекта;
int nsems – количество создаваемых семафоров;
int semflg- флаги управления доступом к семафору.
Ключ должен быть получен функцией:
key_t ftok(const char *pathname, int proj_id);
где:
const char *pathname – имя существующего файла;
int proj_id – идентификатор проекта.
При задании в двух программах одинакового имени файла и одинакового идентификатора проекта функция возвращает в этих программах одинаковый ключ.
Захват и освобождение семафора выполняется одной и той же функцией следующего вида:
int semop(int semid, struct sembuf *sops, unsigned nsops);
где:
int semid – идентификатор семафора, возвращаемый функцией semget();
struct sembuf *sops – указатель на структуру, определяющую операции, которые надо выполнить с семафором;
unsigned nsops – количество операций.
Структура struct sembuf имеет следующий вид:
struct sembuf {
short sem_num;
short sem_op;
short sem_flg;
};
где:
short sem_num – номер семафора, над которым делается операция;
short sem_op – вид операции над семафором;
short sem_flg – флаги операции.
Три вида операций могут быть выполнены над семафором:
если sem_op = 0, то процесс ждет, пока семафор не обнулится;
если sem_op > 0, то текущее значение семафора увеличивается на величину sem_op;
если sem_op < 0, то процесс ждет, пока значение семафора не станет или равным абсолютной величине sem_op. Затем абсолютная величина sem_op вычитается из значения семафора.
Пусть значение семафора 0 означает, что ресурс свободен, а значение 1 означает, что ресурс занят.
Создадим структуру следующего вида:
struct sembuf lock[2] = {
0,0,0, //ждать обнуления семафора
0,1,0 //увеличить значение семафора на 1
};
Тогда следующая операция будет захватывать ресурс:
semop(semid,&lock[0],2);
Введем структуру следующего вида:
struct sembuf unlock[1] = {
0,-1,0, //обнулить семафор
};
Тогда следующая операция будет освобождать ресурс:
semop(semid,&unlock[0],1);
По окончанию работы с семафором его необходимо удалить функцией:
int semctl(int semid, int semnum, int cmd);
где:
int semid – идентификатор удаляемой группы семафоров;
int semnum – номер семафора – игнорируется для команды IPC_RMID;
int cmd – команда, которая в случае удаления принимает значение IPC_RMID.
Поскольку именованные семафоры используются для координации взаимодействия процессов, то для выполнения работы необходимо написать две программы.
Необходимо выбрать общий ресурс - файл и осуществлять запись данных в него из двух программ.
Обе программы должны создать (или открыть, если создан) один и тот же именованный семафор, должны создать (или открыть, если создан) один и тот же файл, но записывать в файл разные символы.
Необходимо убедиться, что в случае отсутствия именованного семафора процессы выводят символы в файл в произвольном порядке, например:
1212121212121212121212121212121212121212121212121212121212121.
В случае использования именованного семафора процессы выводят символы в файл в определенном порядке, например:
111111111122222222221111111111222222222211111111112222222222.
Использовать функции входа в критический участок с блокировкой и без блокировки.
Студенты используют семафоры стандарта POSIX для координации доступа к ресурсу.
Шаблон одной из программ представлен ниже. Вторая программа отличается от первой выводом в файл другого символа
объявить флаг завершения потока;
объявить идентификатор именованного семафора;
объявить дескриптор файла;
функция потока()
{
объявить переменную типа символ и присвоить ей значение ‘1’;
пока (флаг завершения потока не установлен)
{
захватить именованный семафор;
в цикле несколько раз выполнять
{
выводить символ в файл;
задержать на время;
}
освободить именованный семафор;
задержать на время;
}
}
основная программа()
{
создать (или открыть, если существует) именованный семафор;
создать (или открыть, если существует) файл;
создать поток из функции потока;
ждать нажатия клавиши;
установить флаг завершения потока;
ждать завершения потока;
закрыть файл;
закрыть именованный семафор;
удалить именованный семафор;
}
Какие программные интерфейсы для именованных семафоров существуют?
В чем отличие именованных семафоров от неименованных семафоров?
Дайте сравнительную характеристику программных интерфейсов семафоров.
Как реализовать определенную очередность записи данных в файл с помощью именованного семафора (например, первый процесс всегда первым начинает запись файл)?
Опишите действия, которые выполняются над именованным семафором при вызове операций sem_wait() и sem_post().
Какими операциями с именованным семафором можно осуществить
проверку занятости ресурса без блокирования процесса?
Какими операциями с именованным семафором можно осуществить проверку занятости ресурса с определенной периодичностью?
Цель работы – знакомство с механизмом обмена данными между процессами – разделяемой памятью и с системными вызовами, обеспечивающими создание разделяемой памяти, отображения ее на локальную память, передачу данных, чтение данных, закрытие и удаление разделяемой памяти.
В стандарте POSIX участок разделяемой памяти создается следующим вызовом:
int shm_open(const char *name, int oflag, mode_t mode),
где:
name – имя участка разделяемой памяти;
oflag – флаги, определяющие тип создаваемого участка разделяемой памяти;
mode – права доступа к участку разделяемой памяти.
Установка размера участка разделяемой памяти производится следующим вызовом:
int ftruncate(int fd, off_t length),
где:
fd - дескриптор разделяемой памяти, полученный как результат вызова функции shm_open();
length – требуемый размер разделяемой памяти.
Отображение разделяемой памяти на локальный адрес создается вызовом:
void *mmap(void *addr,
size_t length,
int prot,
int flags,
int fd,
off_t offset),
где:
addr - начальный адрес отображения;
length - размер отображения;
prot – параметр, определяющий права чтения/записи отображения;
flags – параметр, определяющий правила видимости отображения процессами;
fd - дескриптор разделяемой памяти;
offset – смещение на участке разделяемой памяти относительно начального адреса.
Удаление отображения разделяемой памяти на локальный адрес производится вызовом:
int munmap(void *addr, size_t length),
где:
addr – локальный адрес отображения;
length - размер отображения.
Закрытие участка разделяемой памяти производится вызовом:
int close(int fd),
где:
fd - дескриптор разделяемой памяти.
Удаление участка разделяемой памяти производится вызовом:
int shm_unlink(const char *name),
где:
name – имя участка разделяемой памяти.
В стандарте SVID участок разделяемой памяти создается вызовом:
int shmget(key_t key, int size, int shmflg);
где:
key_t key – ключ, получаемый функцией ftok();
int size – требуемый размер памяти;
int shmflg – флаги, задающие права доступа к памяти, например, 0644|IPC_CREAT.
После создания участка разделяемой памяти его необходимо подсоединить к адресному пространству процесса. Это делается вызовом:
void *shmat(int shmid, const void *shmaddr, int shmflg);
где:
int shmid – идентификатор сегмента;
const void *shmaddr – адрес памяти;
int shmflg - флаги, задающие права доступа к памяти.
После использования памяти ее необходимо отсоединить от адресного пространства процесса вызовом:
int shmdt(const void *shmaddr);
где:
const void *shmaddr – адрес памяти;
А затем удалить вызовом:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
где:
int shmid – идентификатор сегмента;
int cmd – код команды, для удаления используется IPC_RMID;
struct shmid_ds *buf – структура для хранения информации о сегменте разделяемой памяти, в случае удаления не используется.