Giter VIP home page Giter VIP logo

kcniksddfb's Introduction

Домашнее задание 3

Развиваем утилиту для работы с процессами. Разбираемся с таблицей страниц.

И немножко поддержим сигналы.

Общие моменты

Общие моменты второй домашки остаются в силе.

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

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

Полезная информация

Документация по xv6: https://pdos.csail.mit.edu/6.828/2023/xv6/book-riscv-rev3.pdf

Кратко самое полезное про GDB: https://cs.brown.edu/courses/cs033/docs/guides/gdb.pdf

Полный GDB User Guide: https://sourceware.org/gdb/current/onlinedocs/gdb.html/

Задание 1

Стоимость - 1 балл

Системный вызов

int ps_pt0(int pid, uint64* table)

Возвращает 0 в случае успеха и -1 в случае любых проблем.

Получает адрес выделенной в пользовательском пространстве памяти.

Заполняет ее данными первого уровня таблицы страниц процесса с идентификатором pid.

Используя его, реализуем подкоманду ps:

ps pt 0 <pid> [-v]

По строчке вывода на элемент таблицы. Первое поле - номер элемента. Второе поле - hex dump элемента. Третье поле - hex dump базового адреса и строковое описание битовых флагов через запятую. Что-то типа READ,EXECUTE. Для факта валидности ничего специально не пишем.

Без опции -v выводим только валидные элементы. С опцией -v выводим все. Для невалидного элемента в третьем поле пишем INVALID и больше ничего.

Задание 2

Стоимость - 1 балл

Сделаем нечто аналогичное для второго уровня.

int ps_pt_1(int pid, void* addr, uint64* table)

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

Используя этот системный вызов, реализуем подкоманду ps:

ps pt 1 <pid> <address> [-v]

Вывод - как для pt 0.

Задание 3

Стоимость - 1 балл

Разовьем эту идею на 1 шаг дальше. Новый системный вызов и новая подкоманда.

Новых параметров не появляется. Оба шага определяются адресом.

Задание 4

Стоимость - 2 балла

Дамп памяти. Хотим по виртуальному адресу и процессу понять содержимое памяти.

Реализуем системный вызов

int ps_copy(int pid, void* addr, int size, void* data)

Передаем область памяти data (в процессе, вызвавшем ps_copy) размером size.

Возвращает 0 при успехе и меньше нуля при любых проблемах.

Нужно в адресном пространстве процесса pid найти адрес addr, определить его физический адрес и скопировать size байтов в data.

Если процесс не живой на момент начала вызова, то это ошибка.

Если какой-либо адрес в диапазоне [addr, addr + size) не существует в адресном пространстве процесса, то это ошибка.

Мы можем передать в том числе и собственный pid.

На первый взгляд, в таком случае можно ограничиться простым копированием. Но толку от такой оптимизации на самом деле мало, потому что для проверки нам надо получить pid, а это уже поход в ядро. А раз идем в ядро, то можно и копирование там же организовать.

Поэтому давайте в такой ситуации мы тоже будем работать обычным образом.

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

Сделаем подкоманду ps

ps dump <pid> <address> <size>

И напечатаем результат - как обычно дамп печатают. Или сообщение об ошибке.

Задание 5

Стоимость - 2 балла

Хотим для спящих процессов узнавать, на каком системном вызове они уснули.

Невозможно уснуть, не сделав системного вызова.

И для спящего процесса может быть интересно узнать, на каком системном вызове он уснул.

Конечно, может быть важен и стек вызовов в пользовательской части, и (хотя, и реже) в части, относящейся к ядру.

Но давайте в рамках задания ограничися хотя бы информацией о вызове.

Но кроме самого вызова (кода/имени вызова) еще могут быть интересны параметры.

Только тут есть чисто технический нюанс. У каждого системного вызова свои параметры. И чтобы обработать все системные вызовы, надо для каждого из них сделать свою обработку. И написать длинный case.

Давайте ограничимся одним вызовом - write. Он интересный, потому что там несколько параметров. Один из них - указатель.

Напишем системный вызов который по pid сделает следующее:

  • проверит, находится ли процесс в спящем состоянии

  • если да, то поймет, на каком системном вызове процесс уснул

  • если он уснул на системном вызове write, то скопирует в переданную область памяти все параметры, использованные в данном вызове write (дескриптор, длину буфера и содержимое буфера).

Конкретная сигнатура системного вызова - на ваше усмотрение. Встраивание его в логику ps - тоже.

Поскольку часто мы пишем с помощью write строки, но не всегда, дамп содержимого буфера можно сделать так.

В каждой строчке выводятся 16 символов-байтов.

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

В правой части печатаем 16-ричное представление. Две позиции на байт, одна на пробел, дополнительный пробел на каждые 4 или 8 байтов. В итоге влезем в 80 символов.

Можно символьную часть справа, а не слева напечатать.

Задание 6

Стоимость - 3 балла

xv6 поддерживает только сигнал SIGKILL.

Добавим поддержку сигнала SIGPIPE.

Поддержка будет очень простая.

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

А настоящий сигнал SIGPIPE возникает при попытке записи в pipe, все читающие дескрипторы которого закрыты (надо учесть dup).

Реакцией будет только завершение процесса без дампа или игнорирование - никаких программных обработчиков.

Но нужно учесть маску. Ради маски заведем два системных вызова -

void sigsetmask(int mask)

и

int siggetmask()

Хотя у нас сигналов совсем немного, сделаем маску из рассчета на 32 сигнала. И пусть будут константы SIGPIPE и SIGKILL.

И еще поддержим вызов

int signal(int sig, void (*handler)(int))

Второй параметр - для совместимости с линуксным вызовом. Нам нужно поддержать только два макроса - SIG_IGN и SIG_DFL.

signal(SIGPIPE, SIG_IGN) должен приводить к ингорированию незамаскированного сигнала SIGPIPE.

signal(SIGPIPE, SIG_DFL) должен приводить к завершению процесса при наличии незамаскированного сигнала SIGPIPE.

Никакие вызовы signal(SIGKILL, ...) не должны препятствовать завершению процесса по SIGKILL.

Помним про отличие игнорирования от маскирования.

kcniksddfb's People

Contributors

maxim-druzhinin avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.