Section 1

Unix execution environment

1.     Output various process attributes according to specified options


Write a program that will process command line options specified below.  Options must be processed according to their order from right to left.  The same option can occur several times.  Use getopt(3C) to parse command line. 

You can start from a program that processes only some of the options.  Then add more options, until all required options will be supported.  You can use program getopt_ex.c and change it.

  • -i  Print real and effective ids of user and group.
  • -s  Process becomes a group leader. Hint: see setpgid(2).
  • -p  Print process id, parent process id and process group id.
  • -u  Print value of ulimit(2)
  • -Unew_ulimit  Change value of ulimit. Hint: see atol(3C) on man page strtol(3C)
  • -c  Print the size of a core file that can be created, in bytes.
  • -Csize  Change size of a core file.
  • -d  Print current working directory
  • -v  Print all environment variables and their values
  • -Vname=value  Create a new environment variable or change the name of an existing variable.

Test your program using different argument lists, including::

  • No arguments
  • Invalid option.
  • Options, separated by minus sign.
  • Invalid value for U.


2.     Time in California

            Change program ex_time.c to output time and date in California (Pacific Standard Time, PST). 

3.     Setting user id for file access

            Create data file that is accessible for read and write only to its owner (it can be done by shell command chmod 600 file) and write a program which

  1. Prints real and effective user ids.
  2. Opens a file using fopen(3). If fopen() is successful, close a file with fclose(3). Print error message using  perror(3C) if the file cannot be opened.
  3. Ensure that real and effective user ids are the same. Hint: setuid(2)
  4. Repeat two first steps.

 Test your program.

  1. Run the program and observe the output
  2. Make the program available for your group members and let some of your group members run the program.
  3. By shell command chmod u+s prog set the setuid bit and let your group members run the program again.


Memory management

4.     List of strings

            Write a program that inputs strings entered from the keyboard (stdin) and appends these string to a linked list. Memory for list nodes allocate using malloc(3). Input ends when a dot (.) is entered in the beginning of a sting. After end of the input, all lines are written to output.
            Hint: Declare an array of chars with size big enough for longest supported string.. Use
fgets(3) to read the line and strlen(3) to determine its length. If the sring is longer than your array, fgets(3) will split it to two or more strings.  Remember that strlen(3) does not count a terminating zero. When you know string length, allocate memory block of appropriate size and insert new pointer to the list.


Input/Output system calls

5.     Index of string in a text file.

            Write a program that scans a text file, created by text editor such as ed(1) or vi(1). Then program must hangle requests to output a line with the specified number. Use printf(3)to print a line. Number zero ends the program. Use open(2), read(2), lseek(2) и close(2) for input and file scanning. Build a table of the offsets of every line in the file.  You also can build a separate table with length of every line, or you can calculate a line length as difference between offsets of this and next lines.  When the table is built, you can position on the beginning of the line with any given number, and read the line from the file.  Assume that the file is not longer than 100 lines.
            Hint: Select or create a text file. Remember that first line starts with offset of zero. Find every line feed character and record its position.  You can use a call
lseek(fd, 0L, 1). To debug, you can print this table and compare it to manually created table.  As you can build a correct table, you can proceed to requesting line numbers and handling the requests.


6.     Index of string in a text file with timeout.

            Change the program 5 so that user will have 5 seconds to enter the line number. If the user does not enter a number in a given time, your program must output all file content and terminate. If the user enters the number in 5 seconds, the program must work like in exercise 5.


7.     Index of string in a text file 2.

            Change the program 6 to use memory mapping instead of read(2), lseek(2) and write(2).

File and record locking

8.     Protected text editor

            Write a program that locks a file before calling a text editor. This will protect a file from parallel editing by several users. After your programs locks the file, call your favorite text editor using library function system(3). Note: Some GUI text editors, like gedit(1), sometimes open the file in other processes.  For example, gedit command tries to detect existing gedit window, and, if it finds such a window, sends a command to that window to open a file, instead of opening the file in its own process. You must use the editor that always opens the file in its own process, like vi(1) or nano(1).

Try to use advisory locking in your program.  When your program locks the file, ask your group mate to run your program, or run it yourself in other terminal session. Explain the result. Then try to use mandatory locking. Explain the result.  Note: on NFS, mandatory locking does not work.  To test mandatory locking, you must use local file system, for example /tmp.


Process creation and program execution.  Process management.

9.     Process creation

            Write a program that creates a child process. This process must execute cat(1) of a long file. Parent process must call printf(3) and print some text. Afrer finishing the first part, modify your program such that a last string. Printed by the parent was printed after child process finishes.  Use wait(2), waitid(2) or waitpid(3).

10. Process exit code

            Write a program that launches a command passed as the first argument as a child process. All other arguments of your program must be passed as arguments to the command.. Then program must wait for child process and print its exit code. 

11.  execvpe() function

Implement execvpe() function that works as execvp(2), but allows to change environment variables, like execve(2).
Advice: use external variable



Terminal input/output

16. Answer without waiting for newline

            Write a program that prints a question and requests a single-character answer.  Change terminal attributes such that user would not need to enter a newline after the answer. 

17.  Line editor

            Many programs that accept terminal input allow to edit input before processing it. Write a program that turns off echo and canonical processing, thus turning off a delete character processing. Your program must accept keyboard input and print it to the terminal according to the following rules:

  1. Every input character must be immediately displayed.
  2. When ERASE character entered, last character of the current line is deleted.
  3. When KILL is entered, all characters in current line are deleted.
  4. When CTRL-W (WERASE) is entered, last word in current line is deleted, together with all following spaces.
  5. Program exits when is entered CTRL-D and cursor is positioned at the beginning of the line.
  6. All nonprintable characters except listed above must produce sound signal by outputting CTRL-G character.  Note that terminal emulators can be configured to produce “visual bell” (blinking) instead of a sound or ignore CTRL-G.
  7. Line length is limited by 40 characters. If a word crosses 40th column, this word must be transferred to next lime.


File management

18. Directory listing

Write a program that works like ls -ld. For every its argument, this program must print:

  • File state bits in human-readable form:
    • d if the file is a directory
    • if the file is a regular file
    • ? in other cases
  • Three character groups corresponding to owner, group and other access rights:
    • r if the file is available for reading, otherwise -
    • w if the file is available for writing, otherwise
    • x if the file is available for execution, otherwise -
  • Number of file links
  • Names of the owner and group of the file (advice - use getpwuid and getgrgid).
  • If the file is  regular file, its size. Otherwise leave this field empty.
  • File modification time (use ctime(3C)).
  • File name (if the name was given with the path, print only the name).

 Fields must have constant width, so output would lool like a table.
            Advice: use printf(3S).


Управление каталогами

19. Шаблоны имен файлов

            ═══════════Напишите программу, которая приглашает пользователя ввести шаблон имени файла, аналогичный тому, который используется в shell. Синтаксис шаблона таков:

        * соответствует последовательности любых символов кроме /, имеющей любую длину; возможно - пустой последовательности.

        ? соответствует любому одному символу.

        / не может встречаться.

        любой другой символ соответствует самому себе.


Символы * и ? в шаблоне могут встречаться в любом количестве и в любом порядке.


            ═══════════Затем программа должна найти и распечатать имена всех файлов в текущем каталоге, соответствующих шаблону. Если таких файлов нет, программа должна распечатать сам шаблон.

            ═══════════Совет: используйте readdir, чтобы считать все имена файлов в текущем каталоге, и выберите из них соответствующие шаблону.


20. Шаблоны имен файлов (2)

            ═══════════Измените предыдущую программу так, чтобы в шаблоне могли встречаться символы /. При этом программа должна распечатывать все файлы, путевые имена которых соответствуют шаблону. Так, шаблону */* соответствуют все файлы во всех подкаталогах текущего каталога.


Раздел 2


21. Пищалка

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


22. Мультиплексирование ввода

            ═══════════Напишите программу, которая читает из нескольких файлов по очереди, т.е. после чтения строки из одного файла, читается строка из следующего и т.д. Если в течение TIME_OUT секунд ничего не было прочитано, берется следующий файл.

            ═══════════Программа получает в качестве аргументов имена одного или нескольких файлов, из которых она будет читать. Обычно это терминальные файлы (т.е. /dev/ttynn), но могут быть файлы и других типов. (read(2) с нетерминального устройства может прочитать несколько строк, в зависимости от количества требуемых байтов и длины этих строк.) Если в одном из файлов достигнут конец файла, из него больше не читают. Когда конец файла достигнут во всех файлах, программа завершается. Проверьте вашу программу так:

      ═════$ multiplex /dev/tty 'tty'


23. Защита от сигналов, посылаемых с терминала (задание ╧5 по shell)

            ═══════════Измените исходный текст программы shell так, чтобы она не завершалась, когда вы генерируете сигнал SIGINT. Вместо этого должен завершаться процесс первого плана, а ваш shell должен немедленно выдавать приглашение. Кроме того, предохраните команды, исполняемые в фоновом режиме, от прерывания сигналами SIGINT и SIGQUIT. (Если вы не имеете собственной версии этой программы, вы можете посмотреть исходные тексты заготовки в файлах shell.c, parseline.c, promptline.c и shell.h. Откомпилировав их, вы получите простой командный интерпретатор, способный выполнять программы в виде порожденных процессов, запускать фоновые процессы и перенаправлять ввод/вывод.)

24. Простое управление заданиями (задание ╧6 по shell)

            ═══════════Если вы реализовали защиту от сигналов, модифицируйте ваш интерпретатор shell, так чтобы обеспечить следующие возможности для управления заданиями: SIGTSTP, посланный с клавиатуры (CTRL-Z по умолчанию), должен заставить основную программу перейти в фоновый режим и возобновить исполнение. PID переведенного на фон процесса будет выведен на stderr, и командный интерпретатор выдаст приглашение для следующей команды.
            ═══════════Совет: сделайте каждый порожденный процесс лидером группы процессов и явным образом переводите его на первый план. Когда порожденный процесс первого плана завершается или останавливается, переводите на первый план интерпретатор.


Программные каналы

25. Связь через программный канал

            ═══════════Напишите программу, которая создает два подпроцесса, взаимодействующих через программный канал. Первый процесс выдает в канал текст, состоящий из символов верхнего и нижнего регистров. Второй процесс переводит все символы в верхний регистр, и выводит полученный текст на терминал. Подсказка: см. toupper(3).


26. Связь с использованием функций стандартной библиотеки

            ═══════════Используйте стандартные библиотечные функции popen(3) и pclose(3) для выполнения тех же операций, что и в предыдущем упражнении.


27. Подсчет пустых строк в файле

            ═══════════Напишите программу, которая подсчитывает пустые строки в файле, используя команду wc(1).


28. Генератор случайных чисел

            ═══════════Напишите программу, которая генерирует сортированный список из ста случайных чисел в диапазоне от 0 до 99. Распечатайте числа по десять в строке. Используйте p2open(3), чтобы запустить sort(1) и rand(3) и srand(3) для генерации случайных чисел.


29. Конвейеры (задание ╧7 по shell)

            ═══════════Измените ваш командный интерпретатор так, чтобы он позволял создавать конвейеры. Если вы добавили управление заданиями в упражнениях Раздела 1, вы можете модифицировать программу так, чтобы все процессы в конвейере принадлежали к одной группе. Тогда, например, SIGINT мог бы прервать все процессы в конвейере первого плана.


Очеpеди сообщений

30. Очеpеди сообщений

            ═══════════Напишите две программы, одна из которых шлет сообщения другой. Получатель распечатывает содержимое сообщения, тип и значение, возвращенное msgrcv(2). Напишите программы так, чтобы они не исполнялись родственными процессами. Сделайте программы как можно проще. Для этого упражнения вам не нужна никакая проверка ошибок. Используйте численную форму вашего идентификатора пользователя в качестве ключа. Если позволит время, вы можете установить права доступа для очереди так, чтобы ваши одногруппники, зная ключ очереди, могли читать из нее или писать в нее. К обоим программам должен быть добавлен цикл, чтобы иметь дело с многими сообщениями. Если вы будете разделять идентификатор очереди с другими пользователями, вы должны сказать друг другу ваши идентификаторы пользователя и использовать их как типы сообщений.
            ═══════════Замечание: ipcs -q распечатывает количество сообщений в очереди и их общий размер.


31. Рассылка одного сообщения нескольким процессам

            ═══════════Напишите две программы: отправитель и получатель. Отправитель принимает текст с клавиатуры и рассылает сообщения нескольким копиям программы-получателя. Получатель сообщения распечатывает свое имя программы (argv[0]) и содержимое сообщения. Отправитель продолжает работу, пока не встретит конец файла. Тогда он рассылает всем получателям сообщение-ограничитель. Как только получатель заканчивает свою очередь сообщений, он отправляет сообщение отправителю. После получения такого подтверждения от всех получателей, отправитель удаляет очередь.


32. Несколько процессов, сообщающих состояние процессу-мастеру

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



33. Моделирование производственной линии

            ═══════════Вы должны смоделировать производственную линию, производящую Виджеты. Каждый Виджет состоит из Детали С и Модуля 1. Модуль 1 состоит из Детали A и Детали B. Изготовление Детали A требует 2 секунды, Детали B - 3 секунды и Детали C - 4 секунды.
            ═══════════Подсказка: элементы производственной линии должны быть представлены процессами. Используйте набор семафоров, по одному для каждой детали и модуля. Как только деталь или модуль произведены, добавляйте единицу к соответствующему семафору. Когда объект используется в на следующем этапе, вычитайте из того же семафора 1. Используйте sleep(3) для моделирования частоты, с которой производятся детали.



Разделяемая память

34. Производитель и Потребитель

            ═══════════Напишите две программы, Производитель и Потребитель, такие что Производитель заполняет буфер в разделяемой памяти, а Потребитель читает его. Производитель должен помещать новые данные в буфер только после того, как Потребитель прочитает его.
Совет: Для синхронизации можно использовать два семафора (эффективнее всего - набор из двух семафоров). Один из семафоров нужно ассоциировать с записью новых данных в буфер. Другой должен быть ассоциирован с чтением данных Потребителем.


35. Кольцевая очередь в разделяемой памяти

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


  1. Очередь должна представлять собой массив записей. Таким образом, размер каждой записи не может превосходить фиксированную максимальную величину.
  2. добавление записи в хвост очереди (псевдокод)

        tail <- tail +1

        if tail = QUE_SIZE, tail <- 0

        if tail = head, queue full condition

        else queue[tail] <- item

  1. получение записи из начала очереди (псевдокод)

        if tail = head, queue empty condition

        head <- head +1

        if head = QUE_SIZE, head <- 0

        else item <- queue[head]


36. Один производитель и несколько потребителей буфера

            ═══════════Напишите программу-производитель, которая помещает текст в буфер, размещенный в сегменте разделяемой памяти. Напишите программу-потребитель, которая будет читать из этого буфера. Может существовать несколько копий потребителя. Производитель может обновлять буфер только после того, как все потребители считали его содержимое.
            ═══════════Совет: Эта программа похожа на Задание 34. Нужны два семафора, но их значения будут меняться в диапазоне от 0 до количества потребителей.


37. Несколько читающих процессов и один эксклюзивный процесс записи в разделяемую память

            ═══════════Предположим, что исполняется n процессов. Часть из них читает, а остальные пишут в разделяемый сегмент памяти. Несколько читающих процессов могут работать с буфером одновременно. Если один из пишущих процессов выполняет запись, все остальные процессы должны ждать. Кроме того, если пишущий процесс хочет обновить данные, он должен ждать, пока все читающие процессы закончатся. Для обеспечения такого взаимного исключения надо использовать семафоры.
            ═══════════Замечание: В решении приведена одна программа, которая иногда читает, а иногда пишет.
            ═══════════Совет: Один из способов решения состоит в использовании набора из трех семафоров со следующими значениями:

  • индекс 0 счетчик процессов, выполняющих чтение
  • индекс 1 двоичный семафор, гарантирующий, что только один процесс может писать в буфер
  • индекс 2 используется для блокировки чтения на время записи или ожидания записи