Перейти к публикации
Форум - ComputerCraft
swg2you

Подмена computer.pullSignal или методика построения резидентных программ в OpenOS

Рекомендованные сообщения

Подмена computer.pullSignal или методика построения резидентных программ в OpenOS

Допустим, захотелось нам иметь резидентную программу, которая будет работать на фоне OpenOS и, периодически, или по какому либо событию, что-то полезное делать.

 

Воспользуемся нашим маленьким bibi, а в качестве boot.lua напишем такой код:

local cl=component.list
local gp=component.proxy(cl("gpu")())

--сохраняем исходные функции из _G.computer, для внутреннего пользования
local cp={} for k,v in pairs(computer) do cp[k]=v end

--подменяем функцию computer.pullSignal
computer.pullSignal = function (...)
  local e={cp.pullSignal(...)}
 --для примера, по тильде (~) будем ребутать компьютер
  if e[1]=='key_down' and e[4]==41 then cp.shutdown(true) end
  --а по lAlt будем выводить список компонентов используя определенную в OpenOS функцию print и сохраненную cl
  if e[1]=='key_down' and e[4]==56 then
    for k,v in cl() do print(k,v) end
  end
  --OpenOS периодически сама дергает эвенты (чтобы курсор мигал и прочее) подробнее смотрите в lib/event.lua
  --поэтому мы можем выводить свои часики, куда-ж без них то ) и прочие полезности, например инфу о памяти
  local s=tostring(math.floor(cp.freeMemory()/1024))..'/'..tostring(cp.totalMemory()/1024)..' kb free '..os.date('!%R')
  local w,h = gp.maxResolution()
  gp.set(w-#s+1,1,s)
  --в конце мы должны вернуть событие ничего не подозревающей OpenOS
  return table.unpack(e)
end

--функцию мы подменили, теперь загружаем, компилируем и выполняем init.lua
local fs=component.proxy(computer.getBootAddress())
local h=fs.open('init.lua')
local s,r='',''
while r do
  r=fs.read(h,math.huge)
  s=s..(r or "")
end
fs.close(h)
load(s)()
Запускаем компьютер, bibi пискнув и подождав секундочку, выполняет наш boot.lua, который хитро подменив pullSignal выполнит init.lua и после загрузки OpenOS в углу экрана радостно затикают часики.

 

h_1434887575_1643213_5ca46d9108.png

 

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

--==--

Конечно, вы можете сказать, что вывод списка компонентов портит экран, что перед выводом хорошо бы сохранять состояние экрана каким-нибудь gpu.get, вывод делать в свой интерфейс, а после, восстанавливать экран. И часики у меня никудышние. При скролле вниз (например в редакторе edit.lua) скроллируются вместе с текстом, вместо того, чтобы оставаться там где им положено. Но моя задача, показать метод на простейших примерах, а не переопределяя gpu строить интерфейсы.

 

На основе этого метода можно построить API для TSR программ, а комбинируя его с перехватом component.proxy, component.invoke и последующей подменой gpu на виртуальный терминал, и вовсе, написать свою операционную систему. Для которой, кстати говоря, было бы неплохо уметь запускать OpenOS в окошке, но самое главное, в изолированной среде, чтобы труд программистов писавших ПО под OpenOS не пропадал зря.

 

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

 

Посмотрим что выйдет.

--==--

Небольшое дополнение:

Если вам не охота играть с bibi, но нужно запустить резидентную программу прямо из-под OpenOS, то это сделать еще проще.

local component = require("component")
local cp = require("computer")
local gpu = component.gpu
local pullSignal = cp.pullSignal

cp.pullSignal = function (...)
  local e={pullSignal(...)}
  if e[1]=='key_down' and e[4]==41 then cp.shutdown(true) end
  if e[1]=='key_down' and e[4]==56 then for k,v in component.list() do print(k,v) end end
  local s=' '..math.floor(cp.freeMemory()/1024)..'/'..(cp.totalMemory()/1024)..' kb free  '..os.date('!%R')
  local w,h = gpu.maxResolution() gpu.set(w-#s+1,1,s)  
  return table.unpack(e)
end
Сохраняем этот код в файл, допустим tsr.lua и запускаем его под OpenOS

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

--==--

Если выбросить все что относится к демонстрации работы, у нас останется маленькая обертка

local cp = require("computer")
local pullSignal = cp.pullSignal
cp.pullSignal = function (...)
  local e={pullSignal(...)}

  --код TSR программы

  return table.unpack(e)
end
Заключив в которую код своей TSR программы мы получим резидента, который будет выполнять TSR-код, где-то раз в пол секунды. Изменено пользователем swg2you
  • Like 8

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Это круто! Я все никак не мог сообразить, как такое провернуть, спасибо, чувак!

Пожалуйста, чувак )

 

upd:

перенес дополнение в первый пост

Изменено пользователем swg2you

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Неплохо, но похоже, я догадываюсь к чему это всё идет. Еще немного и swg2you напишет свою ось. Ось для Биосов.

А что, слабо написать ось на 4к памяти?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Неплохо, но похоже, я догадываюсь к чему это всё идет. Еще немного и swg2you напишет свою ось. Ось для Биосов.

А что, слабо написать ось на 4к памяти?

Все идёт к тому что я собираюсь открыть проект MNL (MNL's Not Linux), копию ядра линукс, только под Lua Opencomputers и со своим преферансом и мормуазелями.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Все идёт к тому что я собираюсь открыть проект MNL (MNL's Not Linux), копию ядра линукс, только под Lua Opencomputers и со своим преферансом и мормуазелями.

 

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

Открыть и забыть. Интересное решение.

 

 

Неплохо, но похоже, я догадываюсь к чему это всё идет. Еще немного и swg2you напишет свою ось. Ось для Биосов.

А что, слабо написать ось на 4к памяти?

ОСь загруженная в темпе из инета (и/или по ОпенНет) и запускаемая из-под биоса... Интересно)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

ОСь загруженная в темпе из инета (и/или по ОпенНет) и запускаемая из-под биоса... Интересно)

Вот и Сони так думают. Вон уже консоли в сервера несут, будет так: проплатил - играешь, не проплатил - не играешь.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

swg2you, я смотрю, ты как и некоторые здесь, любитель переопределять системные функции. Предлагаю тебе и другим желающим реализовать совместно проект FTP. Для доступа к дисковому пространству удаленного компьютера по вайфаю или ОпенНет.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

swg2you, я смотрю, ты как и некоторые здесь, любитель переопределять системные функции. Предлагаю тебе и другим желающим реализовать совместно проект FTP. Для доступа к дисковому пространству удаленного компьютера по вайфаю или ОпенНет.

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

Я когда-то под CC такое делал.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Разве OpenCloud не заменит функции ftp?

Какой OpenCloud?? Во-первых, вы его никогда, похоже, не допишете.

Во-вторых, не путай FTP с облаком. Это две разные вещи!

В первом случае — это резидентная программа, а во втором — интерфес мост — сервер.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Какой OpenCloud?? Во-первых, вы его никогда, похоже, не допишете.

Во-вторых, не путай FTP с облаком. Это две разные вещи!

В первом случае — это резидентная программа, а во втором — интерфес мост — сервер.

:giggle:  :smile148:  Ага не допишем. :smile3:

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

:giggle:  :smile148:  Ага не допишем. :smile3:

Тогда полный вперёд. А то прогресс стоит, ничего не делается.

Кстати, посмотрите на тему. Потом на сообщение своё. Потом снова на тему. Потом на Правила.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2@Krutoy

>...А что, слабо написать ось на 4к памяти?

34816 бит, без учета "имени", это довольно много.

 

2@LeshaInc

>...И писать на биосе по мне --- тупиковая ветвь развития...

тупиковая - не писать.

 

2@Fingercomp

>...ОСь загруженная в темпе из инета (и/или по ОпенНет) и запускаемая из-под биоса... Интересно)

Биос такой, - Сосед, а сосед, ты один пинганулся на этом порту, а дай мне код операционки, чтоб я в инет сам не лез!

- Ты офигел? я вообще-то дрон и у меня ос, из других модулей состоит. Хочешь дроном стать?

- Дроном мне не судьба. Максимум - роботом, - Рожденный ползать... - как там говорится.

- Летать не может, - там говорится.

- Ну слетай к кому нибудь, принеси, а?

- Ага, щас. С инета тяни. Или жди пока еще кто из твоей породы проснется и потянет.

- Так хозяин инет-карту куда й то запропастил. Вчерась еще была, а сегодня - тык, - нету!

 

 

2@Zer0Galaxy

>...Это должна быть резидентная программа,...

как минимум две.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

У одного меня родилась ассоциация : " Резидентная программа --> Вирус " ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

У одного меня родилась ассоциация : " Резидентная программа --> Вирус " ?

Это один из вариантов использования резидентных программ

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Это один из вариантов использования резидентных программ

Мне больше это нравится :)

 

Изменено пользователем davial

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Из того,что я прочитал - такой демон может отвечать,к примеру,на автоматическое монтирование дисков(ивент peripheralAdded, на сколько я помню), автоматически прогружать в глобал АПИ компонентов(что бы каждый раз не писать modem=component.modem), работать корзиной и т.п. Вобщем - может быть не только вирусом. Часики swg тоже демон,в винде его бы назвали "служба гаджета часов"

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Из того,что я прочитал - такой демон может отвечать,к примеру,на автоматическое монтирование дисков(ивент peripheralAdded, на сколько я помню), автоматически прогружать в глобал АПИ компонентов(что бы каждый раз не писать modem=component.modem), работать корзиной и т.п. Вобщем - может быть не только вирусом. Часики swg тоже демон,в винде его бы назвали "служба гаджета часов"

Службы, демоны, резиденты - каких только имен не наплодили для фоновых задач )

  • Like 3

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

swg2you, я смотрю, ты как и некоторые здесь, любитель переопределять системные функции. Предлагаю тебе и другим желающим реализовать совместно проект FTP. Для доступа к дисковому пространству удаленного компьютера по вайфаю или ОпенНет.

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

легче создать proxy диска компьютера, который сам будет обращаться к другому компу и получать с него данные, а потом возвращать. потом через fs.mount смонтировать, или работать напрямую через прокси

Поделиться сообщением


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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

легче создать proxy диска компьютера, который сам будет обращаться к другому компу и получать с него данные, а потом возвращать. потом через fs.mount смонтировать, или работать напрямую через прокси

дадададададдадададада классная идея это надо будет изменить component.proxy к примеру чтобы если она получила "network" то тогда возвратила прокси сети компов с этой же прогой. Но это не все - ведь если резидентная программа будет только требовать файлы из сети - кто ей даст? Никто. Поэтому нужно сделать функцию распространения файликов к примеру через копирование файлов в папку "network/mf" ("network" - туда мы смонтируем сетевую FS) и выбрав загрузочную ФС для хранения сетевых файлов (т. е. в папке etc создать mf которая будет отображаться также и в прокси сетевой FS). Это будет шикарно если сделать. Суперская ФТП получится

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Подмена computer.pullSignal или методика построения резидентных программ в OpenOS

Допустим, захотелось нам иметь резидентную программу, которая будет работать на фоне OpenOS и, периодически, или по какому либо событию, что-то полезное делать.

 

Воспользуемся нашим маленьким bibi, а в качестве boot.lua напишем такой код:

local cl=component.list
local gp=component.proxy(cl("gpu")())

--сохраняем исходные функции из _G.computer, для внутреннего пользования
local cp={} for k,v in pairs(computer) do cp[k]=v end

--подменяем функцию computer.pullSignal
computer.pullSignal = function (...)
  local e={cp.pullSignal(...)}
 --для примера, по тильде (~) будем ребутать компьютер
  if e[1]=='key_down' and e[4]==41 then cp.shutdown(true) end
  --а по lAlt будем выводить список компонентов используя определенную в OpenOS функцию print и сохраненную cl
  if e[1]=='key_down' and e[4]==56 then
    for k,v in cl() do print(k,v) end
  end
  --OpenOS периодически сама дергает эвенты (чтобы курсор мигал и прочее) подробнее смотрите в lib/event.lua
  --поэтому мы можем выводить свои часики, куда-ж без них то ) и прочие полезности, например инфу о памяти
  local s=tostring(math.floor(cp.freeMemory()/1024))..'/'..tostring(cp.totalMemory()/1024)..' kb free '..os.date('!%R')
  local w,h = gp.maxResolution()
  gp.set(w-#s+1,1,s)
  --в конце мы должны вернуть событие ничего не подозревающей OpenOS
  return table.unpack(e)
end

--функцию мы подменили, теперь загружаем, компилируем и выполняем init.lua
local fs=component.proxy(computer.getBootAddress())
local h=fs.open('init.lua')
local s,r='',''
while r do
  r=fs.read(h,math.huge)
  s=s..(r or "")
end
fs.close(h)
load(s)()
Запускаем компьютер, bibi пискнув и подождав секундочку, выполняет наш boot.lua, который хитро подменив pullSignal выполнит init.lua и после загрузки OpenOS в углу экрана радостно затикают часики.

 

h_1434887575_1643213_5ca46d9108.png

 

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

--==--

Конечно, вы можете сказать, что вывод списка компонентов портит экран, что перед выводом хорошо бы сохранять состояние экрана каким-нибудь gpu.get, вывод делать в свой интерфейс, а после, восстанавливать экран. И часики у меня никудышние. При скролле вниз (например в редакторе edit.lua) скроллируются вместе с текстом, вместо того, чтобы оставаться там где им положено. Но моя задача, показать метод на простейших примерах, а не переопределяя gpu строить интерфейсы.

 

На основе этого метода можно построить API для TSR программ, а комбинируя его с перехватом component.proxy, component.invoke и последующей подменой gpu на виртуальный терминал, и вовсе, написать свою операционную систему. Для которой, кстати говоря, было бы неплохо уметь запускать OpenOS в окошке, но самое главное, в изолированной среде, чтобы труд программистов писавших ПО под OpenOS не пропадал зря.

 

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

 

Посмотрим что выйдет.

--==--

Небольшое дополнение:

Если вам не охота играть с bibi, но нужно запустить резидентную программу прямо из-под OpenOS, то это сделать еще проще.

local component = require("component")
local cp = require("computer")
local gpu = component.gpu
local pullSignal = cp.pullSignal

cp.pullSignal = function (...)
  local e={pullSignal(...)}
  if e[1]=='key_down' and e[4]==41 then cp.shutdown(true) end
  if e[1]=='key_down' and e[4]==56 then for k,v in component.list() do print(k,v) end end
  local s=' '..math.floor(cp.freeMemory()/1024)..'/'..(cp.totalMemory()/1024)..' kb free  '..os.date('!%R')
  local w,h = gpu.maxResolution() gpu.set(w-#s+1,1,s)  
  return table.unpack(e)
end
Сохраняем этот код в файл, допустим tsr.lua и запускаем его под OpenOS

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

--==--

Если выбросить все что относится к демонстрации работы, у нас останется маленькая обертка

local cp = require("computer")
local pullSignal = cp.pullSignal
cp.pullSignal = function (...)
  local e={pullSignal(...)}

  --код TSR программы

  return table.unpack(e)
end
Заключив в которую код своей TSR программы мы получим резидента, который будет выполнять TSR-код, где-то раз в пол секунды.

 

А как убрать это из памяти?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А как убрать это из памяти?

Гм, может, "Войну и мир" в цитату ещё запихнуть, чтобы потом написать " :smile9: "? Отрезайте ту часть цитаты, на которую отвечаете (а тут цитировать вообще не нужно было).

 

По поводу вопроса самого. Прежнее значение computer.pullSignal должно быть сохранено в другую переменную. Заменой переопределённой функции на изначальную можно остановить выполнение программы.

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

Изменено пользователем Fingercomp

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

Гм, может, "Войну и мир" в цитату ещё запихнуть, чтобы потом написать " "? Отрезайте ту часть цитаты, на которую отвечаете (а тут цитировать вообще не нужно было).

 

Постараюсь. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

должно быть сохранено в другую переменную. Заменой переопределённой функции на изначальную
 

То есть? Можешь как для дебила объяснить,а то я не умней этого статуса :\ 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

То есть? Можешь как для дебила объяснить,а то я не умней этого статуса :\ 

Да без проблем, я тут для этого и сижу.

 

computer.pullSignal — это low-level функция, то есть в обычных программах её напрямую нечасто используют (редко когда действительно необходимо). Всё, что она делает — получает события из некоторой копилки событий. Например, 100 раз тыкнул на клавишу — в копилке появилось 100 событий, и этой функцией они могут быть получены в коде.

Но пока напрямую используют немногие, косвенно — вызовом os.sleep и event.pull — почти во всех.

 

И появляется интересная мысль. Что если заменить функцию computer.pullSignal на свою? Она всё ещё будет делать изначальную работу, но при каждом запросе ивента заодно выполнит какой-то дополнительный код, как, например, отрисовку часиков, как в оригинальном посте.

 

Первое, что приходит на ум:

function computer.pullSignal(...)
  print("Hello, world!")
  return computer.pullSignal(...)
end

Переопределённая функция наша тогда возьмёт все переданные ей аргументы и скормит их изначальной, попутно написав "Hello, world!" В теории.

На практике получается так, что мы переопределили computer.pullSignal и для самой себя, поэтому потонем в бесконечной рекурсии. Прикольно, но вряд ли нам это нужно. Наверное.

 

Чтобы такого не было, надо сохранить прежнее значение computer.pullSignal в отдельную переменную и использовать уже её.

-- сохранились
local pullSignal = computer.pullSignal

function computer.pullSignal(...)
  print("Hello, world!")
  return pullSignal(...)
end

Та-дам: всё работает.

 

Теперь попробуем отключить переопределённую функцию. Тут всё очень просто. Например, вот как можно сделать так, чтобы отключаться при нажатии "Enter":

local kbd = require("keyboard")

local pullSignal = computer.pullSignal

function computer.pullSignal(...)
  local e = table.pack(pullSignal(...))
  if e[1] == "key_down" and e[4] == kbd.keys.enter then
    computer.pullSignal = pullSignal
  end
  return table.unpack(e)
end

По сравнению с предыдущим сниппетом кода, поменялось вот что:

  1. Мы получаем событие до выполнения своего кода и сохраняем в таблицу e.
  2. Выполняем свой код (при нажатии Enter снова подменяем computer.pullSignal, но на этот раз на стандартное значение).
  3. Возвращаем ивент.

 

У такого метода использования есть недостатки. Если два скрипта подменят одну и ту же функцию, проблематично будет отключить программы. Ещё функция запускается только при вызове computer.pullSignal, а не с каким-то интервалом. Система будет тормозить, если эту функцию заменить на какую-то, которая будет небыстро работать. И так далее.

 

Поэтому лучше для обработки ивентов использовать event.listen, для вызова функции по интервалу — event.listen, а для поочерёдной работы нескольких потов — стандартную в грядущей OpenOS 1.6.4 и выше библиотеку "thread" — или, пока OC не обновился, либу от @Zer0Galaxy.

  • Like 2

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

 

 

либу от Zer0Galaxy.
 

Я пытался но не работает файлы не выполняються.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте аккаунт или войдите в него для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!

Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас

×