OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Четверг, 28 Март, 2024 12:08

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 26 ]  На страницу 1, 2  След.
Автор Сообщение
СообщениеДобавлено: Вторник, 31 Март, 2020 19:02 

Зарегистрирован: Пятница, 13 Март, 2009 16:36
Сообщения: 987
Откуда: Казань
Из книги "Project Oberon":
Цитата:
When a module is no longer needed, it should be possible to unload it; and when it is to be replaced by a new, perhaps corrected version, it must be unloaded. Obviously, in a hierarchy of modules, no module must be removed before its clients are removed. A procedure for unloading must therefore ensure that no clients exist.

For this purpose, each module descriptor is given a reference count. The field refcnt is initialized to zero when a module is loaded, and incremented each time the module is imported by a newly loaded client. Procedure Free checks whether or not this count is zero. Its parameter all indicates whether only the specified module is to be unloaded, or the process is to be transitive, i.e. applied to all its imports, too. Hence Free, or rather the local procedure unload, is recursive. We emphasize that unloading is never automatic, but must be explicitly requested by the system's user. The global variable res records the result of unloading:
0: unloading completed
1: module is not loaded
2: cannot be unloaded, because clients exist

Unloading a module is a tricky operation. One must make sure that no references to the unloaded module exist in the remaining modules and data structures. Unfortunately, this is not at all easy and is not guaranteed in the Oberon System. The violating culprits are procedure variables. If a procedure of a module A is assigned to a variable of a module B, and if A is unloaded, the procedure variable holds a dangling reference to the unloaded module's code block. In systems with virtual addressing (Ceres-1 and Ceres-2), the problem is solved by never reusing code addresses, i.e. by strictly sequential allocation in virtual address space. A dangling reference then points to a not allocated page, causing an address (NIL) trap when used. The situation is unsatisfactory, however, when code space is reused (Ceres-3).


Какие другие пути решения этой проблемы существуют, кроме как инвалидировать адрес процедуры (the problem is solved by never reusing code addresses)?

Думаю, что можно сделать так, чтобы выгрузка модуля не производилась, пока существуют такие указатели на процедуры из этого модуля. А для этого надо сделать подсчет ссылок на каждую из процедур модуля. Это можно реализовать изменив компилятор, чтобы при присваивании значения процедурной переменной производился подсчет ссылок. Но указатели на процедуры могут быть и в динамической памяти, а в динамической памяти они могут быть мусором, который висит, пока не будет собран мусор. Получается, что надо сначала посмотреть счетчики на процедуры, если все нули, то можно выгружать, если не нули, то надо попробовать сделать сборку мусора и снова проверить счетчики. Но еще же может быть и многопоточный код, когда что-то происходит с этими указателями на процедуры в разных потоках, значит должно быть взаимное исключение к этим счетчикам, чтобы там было корректное значение. Таким образом можно будет не выгружать модуль, пока остались ссылки на процедуры. Но стоит ли игра свеч? Возможно, есть еще какие-то решения?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Вторник, 31 Март, 2020 21:05 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Можно хранить вместе с процедурной переменной ссылку на модуль и проверять его счётчик выгрузки.
Это для тех сервисов (типа подписки на уведомления), которые не знают, какую процедурную переменную им всунули - и когда будет выгружен модуль.

Для плотно и постоянно связанных компонентов надо просто понимать порядок загрузки-выгрузки. А "на горячую" вообще аккуратно использовать.

Код:
MODULE L1ProcRefs;
   IMPORT Kernel, S := SYSTEM,
      Mem := L1Mem;

   TYPE
      Ref* = RECORD
         mod: Kernel.Module;
         procOffs: INTEGER;
         type: Kernel.Type
      END;

   PROCEDURE (VAR ref: Ref) Init* (IN var: ANYREC), NEW;
      VAR t, ft: Kernel.Type;
            ptr: INTEGER;
   BEGIN
      ASSERT(ref.mod = NIL, 20);
      t := S.VAL(Kernel.Type, S.TYP(var));
      ASSERT(t.fields.num = 1, 20);
      ft := t.fields.obj[0].struct;
      ASSERT(S.VAL(Mem.NativeInt, ft) > 1024, 21);
      ASSERT(ft.id MOD 4 = 0, 21);
      S.GET(S.ADR(var) + t.fields.obj[0].offs, ptr);
      ASSERT(ptr # 0, 22);
      ref.mod := NIL; ref.procOffs := 0; ref.type := t;
      Kernel.SearchProcVar(ptr, ref.mod, ref.procOffs)
   END Init;

   PROCEDURE (VAR ref: Ref) Close*, NEW;
   BEGIN
      ref.mod := NIL; ref.procOffs := 0; ref.type := NIL
   END Close;

   PROCEDURE (IN ref: Ref) NotNil* (): BOOLEAN, NEW;
   BEGIN
   RETURN ref.mod # NIL
   END NotNil;

   PROCEDURE (IN ref: Ref) Valid* (): BOOLEAN, NEW;
   BEGIN
   RETURN ref.mod.refcnt >= 0
   END Valid;

   PROCEDURE (IN ref: Ref) Get* (VAR var: ANYREC), NEW;
   BEGIN
      ASSERT(S.TYP(var) = S.VAL(Mem.NativeInt, ref.type), 20);
      IF ref.mod.refcnt >= 0 THEN
         S.PUT(S.ADR(var) + ref.type.fields.obj[0].offs, ref.mod.code + ref.procOffs);
      ELSE
         S.PUT(S.ADR(var) + ref.type.fields.obj[0].offs, 0)
      END
   END Get;

   PROCEDURE (IN ref: Ref) GetAs* (VAR val: ANYREC), NEW;
   BEGIN
      (* ср. через Low.EqualSignature *)
   END GetAs;

END L1ProcRefs.


Использование: в качестве val - RECORD с единственным полем - процедурным.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Вторник, 31 Март, 2020 23:23 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1428
Зачем подсчет ссылок, если можно сборку мусора?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 00:28 

Зарегистрирован: Пятница, 13 Март, 2009 16:36
Сообщения: 987
Откуда: Казань
Trurl писал(а):
Зачем подсчет ссылок, если можно сборку мусора?

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 08:17 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 12:02
Сообщения: 8500
Откуда: Троицк, Москва
Трурль практически всегда прав безо всякого "возможно" ))


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 11:50 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1428
Кроме процедур есть еще дескрипторы типов.
А еще возможна ситуация, когда процедура вызовет выгрузку модуля, из которого вызвана и некуда будет возвращаться.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 12:47 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Trurl писал(а):
Кроме процедур есть еще дескрипторы типов.
А еще возможна ситуация, когда процедура вызовет выгрузку модуля, из которого вызвана и некуда будет возвращаться.


Так дескрипторы типов - в невыгружаемой части модуля. Остаются навсегда.

Стек вызовов проверять можно. Только это из разряда экзотики. Если сценарии выгрузки-замены модулей столь сложны у вас, что вы не можете это увидеть и проверить просто и линейно, то что-то не так в консерватории (консервы неправильные).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 13:02 

Зарегистрирован: Пятница, 13 Март, 2009 16:36
Сообщения: 987
Откуда: Казань
На сделать так, чтобы всё работало корректно во всех возможных случаях. А не так, что 99% процентов работает, а 1% сам дурак. Так можно и за ручное выделение и освобождение памяти агитировать, а если где-то что-то не так, то сказать, что что-то не то в консерватории. При работе с памятью универсальный сборщик мусора решает все проблемы с освобождением памяти. А подсчет ссылок тоже решает какие-то проблемы, но не может освободить память, если есть циклические ссылки (пресловутый 1%).


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 13:25 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Да неправильный это подход для некритических отраслей (а там тоже надо выбирать правильно сочетание методов. Например, если уж решается задача безопасной загрузки/выгрузки "на горячую", то сформировать схему выгрузки-загрузки и формально её доказать, несложно и вручную).

Из-за избыточности автоматизации всяких 10% редких случаев, которые можно отработать явно, вы и получаете на 80% раздутие софта (Парето при нескольких рекурсивных применениях вам и 98/2 даст в итоге). И добиваетесь обратного - падения надёжности уже на интеграции этой всей сложности и её жизнеобеспечении.

Управление памятью - это именно задача из разряда "на каждом углу в коде, размазана по всей площади". И её защита даёт 90% эффекта (убирая разрушительные для памяти процесса ошибки и уязвимости для атак).

Выгрузка модулей - совсем не то самое.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 14:04 

Зарегистрирован: Пятница, 13 Март, 2009 16:36
Сообщения: 987
Откуда: Казань
Илья Ермаков писал(а):
Да неправильный это подход для некритических отраслей ...
...
Выгрузка модулей - совсем не то самое.

Может быть сложно решить какая область критическая, а какая некритическая. Можно представить светофор, зеленый свет - некритическая область, красный свет - критическая область, а желтый cвет куда?

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 15:04 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Учитывая то, что при выгрузке модуля область виртуальной памяти можно просто выводить навсегда из использования, операция не является априори опасной. Ну т.е. может приводить к логической ошибке вызова на нулевую область кода, но не к каким-либо разрушениям процесса.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 15:23 
Аватара пользователя

Зарегистрирован: Суббота, 16 Февраль, 2008 02:47
Сообщения: 660
Илья Ермаков писал(а):
Учитывая то, что при выгрузке модуля область виртуальной памяти можно просто выводить навсегда из использования, операция не является априори опасной. Ну т.е. может приводить к логической ошибке вызова на нулевую область кода, но не к каким-либо разрушениям процесса.

И, кмк, эту ошибку вызова вполне можно отловить и превратить в авост.

А можно сконструировать процедуру проверки валидности процедурной переменной, условно:
Код:
PROCEDURE IsValid (p: ANYPROCEDURE): BOOLEAN;

чтобы предотвращать авосты. Правда, ее, наэн, придется встраивать в SYSTEM, поскольку нет типа ANYPROCEDURE:
Код:
SYSTEM.ISVALID(p: any procedure): BOOLEAN

Либо перейти к адресам:
Код:
PROCEDURE IsCallable (adr: ADDRESS (* = INTEGER*)): BOOLEAN


Этот последний способ гораздо проще, чем править сборщик мусора и делать трассировку процедурных переменных или проверять стек вызовов или править в компиляторе модуль SYSTEM. И, пожалуй, как раз бы подошли тем 10% случаев, о которых Илья Евгенич пишет.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 15:38 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
adimetrius писал(а):
...


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

Про ANYPROCEDURE - ну, я вот выкрутился в примере выше тем, что передаю RECORD с процедурным полем.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 15:41 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1428
Илья Ермаков писал(а):
Так дескрипторы типов - в невыгружаемой части модуля. Остаются навсегда.

Так и процедуры там же.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 15:49 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Trurl писал(а):
Илья Ермаков писал(а):
Так дескрипторы типов - в невыгружаемой части модуля. Остаются навсегда.

Так и процедуры там же.


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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 16:15 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 12:02
Сообщения: 8500
Откуда: Троицк, Москва
Почему нельзя выгрузку модулей (как и тэгов) трактовать как часть сбора мусора?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 17:17 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1428
Можно, наверное. Но тогда надо как-то по-другому организовать. Сейчас Unload выглядит как аналог dispose.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 17:26 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1428
Илья Ермаков писал(а):
Нет, в невыгружаемой - только дескрипторы типов и, кажется, каталог экспорта. Даже часть метаинформации попадает в выгружаемую. Недавно только работал с загрузчиком.

Я очень давно в ядро лазил. Но помнилось, что память оно не возвращает. Или только адресное пространство? Но я не об этом.
Ведь можно же оставлять и код, просто помечать модуль выгруженным. Тогда ошибок доступа не будет. Проблема в том, что у нас возникнут разные версии одной процедуры. Но ведь та же проблема с дескрипторами: (x IS Module.Type) може иметь разное значение до и после выгрузки Module.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 17:56 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Trurl писал(а):
Проблема в том, что у нас возникнут разные версии одной процедуры. Но ведь та же проблема с дескрипторами: (x IS Module.Type) може иметь разное значение до и после выгрузки Module.


Нет, с типами как раз проблем нет, если не через метапрограммирование.
Если вы делаете в коде IS Module.Type, значит вы статически импортировали Module. Значит, это уже актуальная версия типа (а не выгруженная).

С процедурными же переменными как раз и будет проблема, да. Что другая версия. Другие дескрипторы типов на вход могут уже пойти. И т.п.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 02 Апрель, 2020 18:20 

Зарегистрирован: Понедельник, 28 Ноябрь, 2005 10:28
Сообщения: 1428
Илья Ермаков писал(а):
Если вы делаете в коде IS Module.Type, значит вы статически импортировали Module. Значит, это уже актуальная версия типа (а не выгруженная).

Загрузили модуль, сделали NEW(x), выгрузили, еще раз загрузили. Версия типа актуальная, но она не совпадает с версией типа x.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 26 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 12


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2024, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB