Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Система, управляемая событиями и SST(super-simple tasker)
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Страницы: 1, 2, 3, 4, 5
brag
Недавно я начал пользоваться динамической памятью. Раньше пользовался только статикой, то есть placement new. Со статикой сложно работать с такими шаблонами, как фабрика обьектов, временные задачи итд.
Программирую в основном под cortex-m3/m4
Код реализации аллокатора вышел довольно простым и быстрым, критически секции есть, но очень короткие(там прочитать/записать пару байт).
При чем я не использую этих всяких монстров под названием RTOS, у меня свой простейший асинхронный планировщик задач, занимает строк 300 кода с комментариями(погуглите запрос "super simple tasker", поймете что это).
В купе с динамическим аллокатором, variable element-size queue, новыми фишками C++ (шаблоны, лямбда-функции, auto) получается очень простой, безопасный и мощный фреймворк.
Вспоминаю работу с классическими Thread-ами(с ихними стеками - куча кода было написано для анализа программ на предмет нужного обьема стека, и теперь эта куча кода отправлена на мусорку) со всеми этими тяжеленными мютексами, семафорами, вечными циклами как страшный сон.

Если кому интересно - расскажу и покажу как это все работает.
sigmaN
Ну так извините меня, классические, как вы говорите, Thread-ы и ваш планировщик задач это мягко говоря разные вещи!
Да, конечно, есть ряд применений где такой подход достаточен.

Например если у вас хотя-бы одна из задач будет представлять из себя длинный цикл то на время его выполнения остальные задачи будут курить бамбук )
Таким образом вы вынуждены каждую задачу подпиливать таким образом, чтобы она делала свою работу частями и быстро.
Представим что в системе есть задачи А Б и В. Задача А ресурсоемкая и долго считает, задача Б должна ожидать результатов. А задачу В надо крутить постоянно и независимо от А и Б.
С применением нормальных потоков вы можете синхронизировать задачи А и Б только в тех местах где это требуется остальные же живут своей жизнью(и возможно иногда синхронизируются там друг с другом).

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

Ну а если у вас нормальное переключение контекста и всё что я написал не актульно то поздравляю - вы изобрели потоки. Скоро в этой системе появятся мьютексы и семафоры )
Serhiy_UA
Цитата(brag @ Sep 7 2016, 12:07) *
Не очень недавно я начал пользоваться динамической памятью.
.......
Если кому интересно - расскажу и покажу как это все работает.

А какая у Вас стратегия управления кучей, хотел иметь представление о следующем:
1. Как ведется учет занятой и свободной памяти? Как ищется для объекта подходящий по размеру участок памяти?
2. Как в куче объединяются соседние пустые участки для увеличения непрерывного пространства под размещение следующих объектов? В какие моменты, и при каких условиях эти действия выполняются?

А фраза «Не очень недавно», это получается как бы «давно»?

brag
Цитата
Например если у вас хотя-бы одна из задач будет представлять из себя длинный цикл то на время его выполнения остальные задачи будут курить бамбук )

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

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

В этом нет никакой необходимости.

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

Точно так же себя ведет и мой SST-планировщик, только в отличии от нормальных тредов, для каждого из которых надо выделять стек(какого размера? что будет, если стек переполнится, в то время, как у другого треда стека в избытке?), в моем планировщике один стек, при чем используется очень эффективно.

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

Да ничего подпиливать не нужно:
Код
enum { TASK_V_PRIORITY = 1, TASK_A_PRIORITY = 2, TASK_B_PRIORITY = 3};
void task_V(){
    while(true){
      // do some heavy work
    }
}
void task_A(){
//  do work
  return;
}
void task_B(){
//  do work
  return;
}

SST::runTask(task_V, TASK_V_PRIORITY);

void on_event_a(){
   SST::runTask(task_A, TASK_A_PRIORITY);
}
void on_event_b(){
   SST::runTask(task_B, TASK_B_PRIORITY);
}


Цитата
Ну а если у вас нормальное переключение контекста и всё что я написал не актульно то поздравляю - вы изобрели потоки. Скоро в этой системе появятся мьютексы и семафоры )

Да, это полноценные потоки, зато без выделения стека, инверсии приоритетов, livelock-ов, и кучи других проблем. Тут идеология и техника другая.
Мютексы и семафоры не появятся. Зачем они нужны? Тут всем рулят очередя. В отличии от тредовой архитектуры тут нет вышеперечисленных проблем. А переполнение очереди легко отлавливается на программном уровне, при чем оно может произойти даже прозрачно для пользователя. В то время, как переполнение стека приводит к падению всей системы, а глюки с мютексами вообще очень трудно находимые, целый год может отработать нормально и в один прекрасный момент глюканет, потом еще год будете моделировать ситуацию искать баг. Потом со временем будете вынуждены написать свой анализатор гонок(как в свое время делал я, потому что Relacy не детектирует даже самую простую реальную ситуацию https://github.com/dvyukov/relacy/issues/3 ) и анализатор стека(а что если рекурсия, что если куча виртуальных функций, порядок и уровень вложения которых на этапе компиляции неизвестен?)

Цитата
А фраза «Не очень недавно», это получается как бы «давно»?

Извиняюсь, имелсь в виду недавно. исправил.

Цитата
А какая у Вас стратегия управления кучей, хотел иметь представление о следующем:
1. Как ведется учет занятой и свободной памяти?

Через linked-list. служебное поле занимает одно 32-битное слово, это и есть оверхед, все остальное - полезные данные.
Каждый блок указывает на предыдущий и следующий. Для удаления блока достаточно сходить не предыдущий и следующий, и если они(один из них) пустые - обьеденить с удаляемым.

Цитата
Как ищется для объекта подходящий по размеру участок памяти?

Разные алгоритмы пробовал, но для моих задач лучше всего работает first-fit, фрагментация на одинаковом уровне, а добавление гораздо быстрее - добавление происходит в последний освобожденный(обьедененный) блок.

Цитата
2. Как в куче объединяются соседние пустые участки для увеличения непрерывного пространства под размещение следующих объектов? В какие моменты, и при каких условиях эти действия выполняются?

Ну уже написал выше от части, дополню:
1. идем в предыдущий блок, если пустой - увеличиваем его размер на N+1 слов. Где N - размер удаляемого блока.
2. идем в следующий блок. если он пустой - N=размер этого блока, повторяем процедуру 1. для предыдущего и выходим. Если не пустой - меняем указатель prev на блок, который получился в п1. все.
Физически блок выглядит вот так:
|prev_offset_word[14 bits], reserved[2 bits], size_words[14 bits], mask[2 bits]; uint32_t data[size_words]|


В самом аллокаторе ничего сложного нет. Doubly linked list на основе 14-битных "ссылок". Этого хватит, чтобы покрыть 64кб памяти - для большинства задач этого достаточно.
Фит-алгоритм возможно придется подобрать, который больше понравится.
1. best-fit (ищем блок минимального размера) - проблема - остаются очень мелкие бесполезные фрагменты. нужно искать блок - тормоза.
2. first-fit (добавляем в первый попавшийся блок, обычно тот, который недавно очистили) - мне больше всего нравится - во первых, быстро, во вторых, по моей статистике меньше всего фрагментации.
3. worst-fit (добавляем в блок самого большого размера) - тоже неплохо работает, но надо искать блок.

Блокировок прерываний нет, тк аллокатор не используется в прерываниях и других высоко-приоритетных задачах sm.gif У меня всего 32 приоритета юзера + еще 16-256 приоритетов прерываний(зависит от конкретного процессора). Задачи и прерывания более высокого приоритета всегда вытесняют(прерывают) задачи более низкого.
Обычно аллокатор нужен для задач с приоритетом 0,1,2 - это всякого рода UI и другой медленный ввод-вывод. Тогда ставим приоритет аллокатору 4 и все, никаких блокировок не нужно - аллокатор всегда будет вытеснять задачи, которые его используют.
Serhiy_UA
brag, Спасибо за пояснения!

AHTOXA
Цитата(brag @ Sep 7 2016, 14:07) *
Если кому интересно - расскажу и покажу как это все работает.

Конечно интересно!
arhiv6
Цитата(brag)
Если кому интересно - расскажу и покажу как это все работает.

Интересно. Где можно посмотреть код, примеры?
Kabdim
Цитата(brag @ Sep 7 2016, 12:07) *
Если кому интересно - расскажу и покажу как это все работает.

Присоединюсь к интересующимся. Если я правильно понял то для более низкоприоритетной задачи высокоприоритетная по сути является прерыванием? И соответственно равноприоритетные задачи не предусмотрены?
brag
Цитата
Присоединюсь к интересующимся. Если я правильно понял то для более низкоприоритетной задачи высокоприоритетная по сути является прерыванием?

Да, совершенно верно.

Цитата
И соответственно равноприоритетные задачи не предусмотрены?

Конечно предусмотрены! И они выполняются в порядке очереди. Так же, как и прерывания. Закончилась одна задача - начинается выполнение другой. Или закончилось одно прерывание(а второе висело в пендинге) - начинается выполнение другого.

В дополнение - я считаю вечные циклы и мютексы - каменным веком. Необходимость мютексов возникает из за вечных циклов. Нет вечных циклов - нет и мютексов.
Зачем нужны вечные циклы? Какая их практическая цель? Везде, где я их встречаю - их запросто можно оттуда выкинуть. Зачастую это код вида:
Код
void Thread1(){
    while(true){
        if(must_i_exit)break;
        if(is_user_clicked_helo_button){
             messageBox("Hi dear user");
        }
    }
}
void Thread2(){
    while(true){
        if(must_i_exit)break;
        if(mp3_frame_ready){
             decodeMp3Frame(frame);
        }
   }
}

То есть и за вечных циклов наm понадобятся тяжеленные мютексы, семафоры - работа с которыми зачастую приводит к блокировке всех прерываний внутри кода самих семафоров; 2 довольно больших стека, заведомо неизвестного и трудно просчитываемого размера. И так под каждый тред - по сути под обычную функцию!

Теперь давайте попробуем откажемся от этих циклов:
Код
void on_helo_button_clicked(){
    messageBox("Hi dear user");
}

void on_mp3_frame_received(const Mp3Frame* frame){
    decodeMp3Frame(frame);
}

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

Что происходит при блокировке:
1. блокируются, как минимум все потоки. и как максимум - все прерывания
2. сохраняются все регистры в стеке(а это 16 4-байтовых слов для кортекса без FPU)
3. производится поиск готового к выполнению потока. очень тяжелая задача, особенно при наследовании приоритетов.
4. восстанавливаются из стека все регистры.
5. разблокируется все обратно

Теперь что происходит при добавлении задачи в очередь:
1. блокируется все потоки и прерывания, которые могут работать с этой очередью. на кортексе это одна инструкция.
2. резервируется в очереди место под задачу. Фактически данные задачи состоят из аргументов функции, то есть там пара-тройка 4-байтовых слов. В отличии от сохранения контекста, где их как минимум 16, а на практике больше.
Физически резервирование места это чтение двух указателей, простое условие, затем инкремент одного из них и запись в память. Это несколько простых инструкций.
3. блок снимается.
4. записывается в зарезервированное место данные задачи(наши аргументы функции). При этом все прерывания и все задачи продолжают работу
5. опять блокируемся
6. помечается зарезервированный участок очереди, как валидный - пара инструкций
7. снимаем блок
Все. И того операция очень простая и понятная, мало того, любое переполнение очереди нормально отлавливается программно и зачастую это recoverable error - прозрачный для пользователя, в то время как переполнение стека - это труба.
Код такой очереди занимает 240 строк вместе с комментами и C++ными приколами. Сколько строк исполняемого кода занимает мютекс и переключение контекста в какой-нибудь FreeRTOS sm.gif?

Цитата
Интересно. Где можно посмотреть код, примеры?

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

Выше я показал почему не нужны треды. А вот как можно без них обойтись - рассказано в этом материале http://www.embedded.com/design/prototyping...r-Simple-Tasker
Я конечно пошел дальше, сделал некоторые моменты более оптимально с учетом конкретной архитектуры(архитектур) и реализовал все на c++ чтобы было просто и удобно и безопасно пользоваться. Об этом позже.
AlexandrY
Цитата(brag @ Sep 7 2016, 15:47) *
Я конечно пошел дальше, сделал некоторые моменты более оптимально с учетом конкретной архитектуры(архитектур) и реализовал все на c++ чтобы было просто и удобно и безопасно пользоваться. Об этом позже.


Здесь с завидной регулярностью находятся первооткрыватели метода конечных автоматов Miro Samek-а.
Но индустрия уже давно проехала этот этап.
Вытесняющая асинхронная многозадачность сейчас даже в Arduino применяется.
Никто не пишет софт годный под модель SST.
Приняв SST вы останетесь голым, даже FatFS или lwIP не сможете портировать.
brag
Цитата
Вытесняющая асинхронная многозадачность сейчас даже в Arduino применяется.

SST и есть вытесняющая асинхронная многозадачность. Точно такая же, как и есть в мире PC, под названием Active Object паттерн итд. Где физически создается тред под конкретную задачу, отрабатывает и выходит.
Или взглянуть на NodeJS/javascript - там хоть и один поток, но все асинхронно без блокировок. Или ту же Qt. Мир потихоньку, вернее очень даже резво, движется в сторону неблокирующей модели.

Цитата
Никто не пишет софт годный под модель SST.

Готовый софт мы не используем, мы пишем софт сами. Вот библиотеки - другое дело. Но последние редко зависимы от тредовой модели.

Цитата
Приняв SST вы останетесь голым, даже FatFS или lwIP не сможете портировать.

Портируем без проблем, при необходимости. Что есть такого в lwIP, что не ляжет на SST-модель? У меня давно написан свой fat и простейший ip-стек, который вообще не требует никакой ОС, то есть может работать на любой. Если нужно будет что-то готовое по-сложнее - запросто портируем на SST-модель. Тем более, на сколько я знаю, lwIp работает и в standalone, вообще без ОС.
Минусов я у нее не вижу, а вот очевидных плюсов достаточно. Перешел на нее я не так давно, и не по причине быстродействия, а по причине того, что сложный код под тредовую блокирующую модель рано или поздно либо ставал мертвым и глючным, либо сам превращался в active-object и другие асинхронные паттерны, появлялись очереди(каналы, как во всех модерн-языках типа Go) то есть все превращалось в SST. Треды были нужны только ради возможности работы этого всего. Создание запуск и удаление треда сопровождались огромными затратами ресурсов и блокировками. Потом я их выкинул за ненадобностью и заменил на простой и быстрый SST, где создание задачи занимает пару десятков инструкций sm.gif
sigmaN
Очень интересно. Видимо этот SST прошел мимо меня... Надо будет почитать..
Add: Прочел http://www.embedded.com/design/prototyping...r-Simple-Tasker
Вселье закончилось тем, что все были загнаны в угол принудительным limiting all intertask communication to events после чего было замечено, что выполняющаяся задача блокирует все задачи ниже себя приоритетом и гордо были изобретены и мьютекс и критические секции )
Цитата
Recall that the SST scheduler can only launch tasks with priorities higher than the initial priority with which the scheduler was entered. This means that temporarily increasing the current SST priority SST_currPrio_ blocks any tasks with priorities lower than this priority "ceiling." This is exactly what a priority-ceiling mutex is supposed to do. Listing 5 shows the SST implementation.



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

brag
Все, что мы теряем отказываясь от классических тредов - это тайм-кванты - то есть переключение равно-приоритетных потоков по таймеру. Но где такое нужно в embedded? Даже в PC это зачастую не нужно. Это нужно во всяких линуксах и виндах, чтобы криво написанная программа не тормозила остальных в своем уровне приоритета.
Но даже если такое понадобится, например когда есть 2 тяжеленных алгоритма, например скажем mp3 и jpeg и надо чтобы они оба работали "одновременно", тогда и это не сложно решить на SST. Стек конечно придется выделить для этих двух задач и сделать простую переключалку по таймеру, но работать с остальными компонентами системы они запросто смогут через очереди - без всяких мютексов. И остальная система будет работать на едином стеке.
Но это в теории. На практике же любая работа с данными подразумевает деление на фреймы - получение и обработка фреймов по отдельности - Run to completion, а значит и необходимость в тайм-квант-планировщике практически отпадает.
DASM
Что то не пойму, речь о кооперативной "оси"?
sigmaN
Никто же не мешает организовать обмен ивентами и в классической релизации потоков.

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

P.S. Никто не говорил, что многопоточное программирование это легко и просто. Но этот ваш SST больше отбирает чем дает.
Ну да, такты экономятся, спору нет. Реализация по-проще.... На этом все и заканчивается )
И я уже гвоорил, что в некоторых местах это может быть и вполне уместно!
brag
Цитата
Что то не пойму, речь о кооперативной "оси"?

Нет, вытесняющей

Цитата
Пусть каждый поток имеет свою ивэнт очередь. Каждую итерацию цикла(а может и чаще) поток проверяет не поступил ли мне там ивэнт какой? И обрабатывает его если да.
Некий поток хочет взаимодействовать с ним. Ок. Просто кидаем ивэнт ему в очередь и идем дальше. Таки да, саму очередь придется защитить мьютексом или критической секцией, но в целом, по семантике получится то-же самое.

Только зачем все так сложно, если можно гораздо проще и с гораздо меньшими затратами ресурсов? Зачем проверять, поступил или нет? Зачем писать if(постипило) значит тчо-то делаем, если можно написать - при поступлении что-то делаем?

Цитата
P.S. Никто не говорил, что многопоточное программирование это легко и просто. Но этот ваш SST больше отбирает чем дает.

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

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

В то время как в классической модели я могу давать задачам кванты времени полностью переключая контекст и значительно более сгладить среднее время реакции, уменьшить джиттер.
И вообще в этой вашей SST проблема параллельной работы задач с одинаковым приоритетом, как вы сами земетили, реализуется уже через некие костыли.
arhiv6
Цитата(DASM)
Что то не пойму, речь о кооперативной "оси"?

SST - всё-таки вытесняющая (по крайней мере более приоритетные задачи могут вытеснять менее приоритетные).
http://embedders.org/blog/teap0t/miro-same...ch-perevod.html
DASM
Спасибо, интересно, почитал.
brag
Цитата
В то время как в классической модели я могу давать задачам кванты времени полностью переключая контекст и значительно более сгладить среднее время реакции, уменьшить джиттер.
И вообще в этой вашей SST проблема параллельной работы задач с одинаковым приоритетом, как вы сами земетили, реализуется уже через некие костыли.

Да, тайм-квантов прямо в SST нет, но их можно получить свободно поверх SST. Джиттер будет и там и там.
К стати в SST фактически джиттер приоритетных задач меньше - прерывания блокируются на очень короткое время(часто на 2-3 инструкции), а часто да еще и на заточенной под это архитектуре - вообще не блокируются(lock free).
Но где пример задачи, где реально нужны тайм-кванты?
Я думаю, они нужны в сложных системах, где пользователь запускает приложения, типа линукса, где есть процессы. Да и планировщики там далеко не тривиальные. А в прикладных программах и на мк до 200мгц и DSP я им применения не вижу.
Зато там отлично работает SST, повышает скорость разработке и снижает себестоимость проекта, а значит повышает рентабельность проекта. Там, где для тредов нужен мк на 200мгц и 128кб оперативки, я с SST и C++ обхожусь 72мгц и 16-32кб оперативки.

Цитата
Поэтому сравнивать нужно совокупность свойств(качеств) обеих систем и делать вывод о применимости в конкретном случае.

Вот хотелось бы такой конкретный случай увидеть. Не даите конкретный примерчик, где SST неуместно/сложно/плохо?
Лично я вижу обратное - там, где все легко и просто ложится на SST(события), при чем зачастую на один поток - вешают несколько тредов, при чем 90% времени они висят в блокировке и чего-то ждут.
DASM
Ну я бы не стал столь восторженно писать, не могут 99% людей сидящих на традиционных РТОС быть идиотами. Но определенный резон тут есть, не спорю.
brag
Цитата(DASM @ Sep 7 2016, 23:43) *
Ну я бы не стал столь восторженно писать, не могут 99% людей сидящих на традиционных РТОС быть идиотами. Но определенный резон тут есть, не спорю.

Я ниразу не назвал их идиотами. Выбор РТОС обусловлен не только самими потоками, но и зависимостями от других проектов и библиотек.
Я лишь показываю принцип. Сколько вижу примеров и реального использования тредов - везде одно и то же - куча сложного ненужного кода и памяти. И зачастую простейшая задача, которая ложиться не то что на SST, а даже на один поток в event-driven стиле реализована как минимум в 3-4х потоках с вытекающими отсюда проблемами.

Да и PC мир уже давно переходит на event-ы.
Как раньше выглядел Proxy-сервер? - Основной процесс в вечном цикле ждал входящее соединение, и для каждого соединения создавал отдельный процесс(даже не поток), и в каждом таком процессе создавалось по 2 потока - один ожидал данные от пользователя и передавал дальше, второй ожидал от удаленной стороны и передавал пользователю. И того на каждый коннект по 3 тяжелых потока, один из которых супер-тяжелый. Завалить такой сервак DDOS-атакой нет проблемы - память и вычислительные ресурсы быстро кончатся.

А теперь это выглядит гораздо проще - событие входящего коннекта(void on_connected(...) ) создает обьект, который в свою очередь подключается к удаленной стороне и имеет 2 обработчика событий - on_data_received_from_client и on_data_received_from_remote. Код стал в разы короче, работает в одном потоке и требует копеечные затраты памяти и проца, и способен обрабатывать на том же железе в разы(десятки раз, а иногда и сотни) больше запросов.
Мало того - события будут выполнятся тру-параллельно - планировщик сам будет раскидывать события по ядрам(на многоядерном процессоре)/процессорам.

Конечно тут есть свой порог вхождения. Дело в том, что так исторически сложилось, что мозг программиста работает в императивном стиле sm.gif Ему блокирующий код понятный, он(мозг) его может шаг за шагом выполнять. Чтобы начать думать событиями нужно перестраивать свой моск, привыкать, учить другие языки, где трудно работать иначе(например Javascipt-NodeJS).
Вообще сейчас понемногу тенденция переходит с императива на другие стили - декларативное и функциональное программирование.
Вот сейчас посмотрел на текущий свой код - в классе практически нет функций и конструктор пустой, зато инициализируется куча обьектов и переменных - медленно но уверенно движемся в декларативный стиль, сами этого не осознавая...

Куча кода вот так выглядит:
Код
// Define DF task
    class DfTask{
    public:
        DfTask(Df* df,
            DfBuffer* buffer,
            int len,
            const delegate<void()>& cbk,
            SST::TPriority cbk_priority):
                df(df),
                buffer(buffer),
                cbk(cbk),
                len(len),
                cbk_priority(cbk_priority)
        {}
    private:
        Df* const df;
        DfBuffer* const buffer;
        delegate<void()> cbk;
        const uint16_t len;
        SST::TPriority cbk_priority;
    };

Ни единой функции - одни переменные. Тело функции-конструктора - пустое!
Программы строим не из императивного кода, а из блоков - кубиков sm.gif
sigmaN
Цитата
Да и PC мир уже давно переходит на event-ы....
А теперь это выглядит гораздо проще - событие входящего коннекта(void on_connected(...) ) создает обьект, который в свою очередь подключается к удаленной стороне и имеет 2 обработчика событий - on_data_received_from_client и on_data_received_from_remote.
Ну давайте не будем путать всё в кучу. Это решение чисто архитектурное. Инкапсулировать всё в объект и наружу вывести события. Это не коим образом не означает, что внутри этот объект не работает в двух потоках wink.gif
DASM
а что такое "императивный код"? https://ru.wiktionary.org/wiki/%D0%B8%D0%BC...%BD%D1%8B%D0%B9
DASM
"В этом случае программа представляет собой формальную теорию, а её выполнение является одновременно автоматическим доказательством этой теории, и характерные для императивного программирования составляющие процесса разработки (проектирование, рефакторинг, отладка и др.) в этом случае исключаются: программа проектирует и доказывает сама себя." - а это как ? wacko.gif " Как следствие, декларативные программы не используют понятия состояния, то есть не содержат переменных и операторов присваивания (см. также ссылочная прозрачность)." ... эээ как я отстал...
ViKo
Допустим, надо стереть flash-память, что длится до 10 секунд. По теории SST для этого случая (события!) нужно создать свою задачу. Пропади пропадом такая ОС.
sigmaN
Ну почему же обязательно создавать задачу. Стирание флэша может быть ветвлением одной из существующих задач.... Т.е. аргумент как-бы не очень объективный )
ViKo
Ветвление, это что? После стирания нужно дальше работать, писать и т. д.
Да, я не понял глубины идеи, кроме того, что все задачи работают до конца.
brag
Цитата
Ну давайте не будем путать всё в кучу. Это решение чисто архитектурное. Инкапсулировать всё в объект и наружу вывести события. Это не коим образом не означает, что внутри этот объект не работает в двух потоках wink.gif

Нет, физически там один поток и один стек.

Цитата
Допустим, надо стереть flash-память, что длится до 10 секунд. По теории SST для этого случая (события!) нужно создать свою задачу. Пропади пропадом такая ОС.

Если Вы не поняли как это работает и как этим пользоваться, то это не значит, что это работает плохо или этим неудобно пользоваться.
Цитата
Ну почему же обязательно создавать задачу. Стирание флэша может быть ветвлением одной из существующих задач.... Т.е. аргумент как-бы не очень объективный )

Для стирания флеша(физически) надо всего лишь:
1. Передать несколько байт через SPI (команда стирания, возможно команда разблокировки - RWEN) и слесть с шины.
2. Через некоторое время(по таймеру) опять занять шину и проверить -стерлось или нет. Если стерлось - все, готово. Если нет - опять запустить таймер.
Но это если на шине только одна флешка. А если там еще что-то - понадобится задача. В принципе ничем не сложнее первого варианта, только действия выполняем не сразу, а в теле задачи. И когда шина освободится выполнится тело задачи и флешка будет стерта.

Все эти действия выполняет не пользователь, а класс,работающий с флешкой.
Вот как это выглядит в реально работающем проекте, только принты переписал на русские, чтоб было понетнее:
Код
    // Запускаем стирание флешки
    df->MassErase([this](){
        // получаем уведомление, что флешка стерта. Этот код выполнится аж через 10 секунд после запуска стирания.
        printf("Теперь флешка чистая.\n");
    });
    printf("Стирание флешки запущено, обратно пути нет\n");

Сильно сложно?

Цитата
" ... эээ как я отстал...

Почитайте еще про функциональное программирование sm.gif
sigmaN
Цитата
Нет, физически там один поток и один стек.
Хотелось бы посмотреть исходники такого сервера. Это в опэнсоурсе есть?
brag
Цитата(sigmaN @ Sep 8 2016, 10:31) *
Хотелось бы посмотреть исходники такого сервера. Это в опэнсоурсе есть?

Конечо. При чем написанного на языке Javascript - полностью динамический язык, со сборкой мусора. И это работает в разы быстрее, и в десятки а то и сотню раз меньше памяти и во столько же десятков(а то и сотен) раз больше способно обрабатывать клиентов в секунду, чем сервер на C в классической многопоточной моделиsm.gif
https://nodejs.org/dist/latest-v4.x/docs/api/http.html
CODE
const http = require('http');
const net = require('net');
const url = require('url');

// Create an HTTP tunneling proxy
var proxy = http.createServer( (req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('okay');
});
proxy.on('connect', (req, cltSocket, head) => {
// connect to an origin server
var srvUrl = url.parse(`http://${req.url}`);
var srvSocket = net.connect(srvUrl.port, srvUrl.hostname, () => {
cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
'Proxy-agent: Node.js-Proxy\r\n' +
'\r\n');
srvSocket.write(head);
srvSocket.pipe(cltSocket);
cltSocket.pipe(srvSocket);
});
});

// now that proxy is running
proxy.listen(1337, '127.0.0.1', () => {

// make a request to a tunneling proxy
var options = {
port: 1337,
hostname: '127.0.0.1',
method: 'CONNECT',
path: 'www.google.com:80'
};

var req = http.request(options);
req.end();

req.on('connect', (res, socket, head) => {
console.log('got connected!');

// make a request over an HTTP tunnel
socket.write('GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n');
socket.on('data', (chunk) => {
console.log(chunk.toString());
});
socket.on('end', () => {
proxy.close();
});
});
});

При чем это еще довольно сложный код, но надеюсь будет понятно как он работает. Если я приведу более простой - то без опыта разработки в подобном стиле точно не поймете
ViKo
Слезть с шины, и что делать? Висеть в задаче без дела, и сам не гам и никому не дам? Или отдать управление другой, пусть менее приоритетной, задаче? Как?
501-q
Приветствую!

Модель SST хорошо работает до того момента, когда с одним и тем же ресурсом нужно работать из потоков с разным приоритетом.

Реальный пример: на шине висят пультик и второй МК, связь с которым должна завершиться в течении 300 мкс (каждые 3 мс). Или объединять логику пультика (которая в разных проектах одна и та же) с логикой связи со вторым МК, или разносить по разным потокам с их синхронизацией. Во втором случае при добавлении абонента на шину первые два потока вообще менять не надо. А SST?

Илья
brag
Цитата(ViKo @ Sep 8 2016, 10:39) *
Слезть с шины, и что делать? Висеть в задаче без дела, и сам не гам и никому не дам? Или отдать управление другой, пусть менее приоритетной, задаче? Как?

Слесть - значит сказать драйверу, что мы с шины слезли. У нас не должна голова болеть что там с задачами и что будет дальше - этим занимается планировщик и частично - компилятор.
Приведу полный код самой процедуры(если ее можно так назвать) стирания.
Он немного сложный, поскольку не только шина может быть занята в это время, но и сама флешка. Даже наоборот - шина может быть свободна, а Flash-микросхема в это время будет выполнять внутри себя операцию стирания, а любую другую команду в лучшем случаи отвергнет, а в худшем - заглючит.
То есть нам нужны 2 задачи - первая, которая выполнится когда флешка освободится, а вторая - когда шина.
CODE
// функция занятия шины
void Df::setCallback_and_CS(const delegate<void()>& cbk){
// устанавливаем обработчик прерывания DMA
Dma::setChannel_onXfrc(spi->dmaChannelRx(), cbk);
// устанавливаем chip select
DFCS_n_pin::clear();
}

// ----- Mass Erase ------
// Описываем задачу
class DfMassEraseTask{
public:
DfMassEraseTask(Df* df,
const delegate<void()>& cbk,
SST::TPriority cbk_priority):
df(df),
cbk(cbk),
cbk_priority(cbk_priority)
{}

// сама функция задачи. будет выполнена, как только Flash станет свободна
void operator()(){ // DF_TASK_PRIORITY
// Создаем задачу и добавляем в очередь шины SPI
bool r = df->spi->enqueueTask([this](){ // SSP_TASK_PRIORITY
// занимаем шину
df->setCallback_and_CS([this](){ // Handler mode
// команда WREN успешно подана, временно снимаем chip-select (так требует документция на Flash)
DFCS_n_pin::set();
__nop();
__nop();

// опять устанавливаем CS
df->setCallback_and_CS([this](){
// команда ERASE закончилась, слазим с шины
DFCS_n_pin::set();
// сигнализируем драйвер шины, что мы с шины слезли. После этого шина может быть испльзована кем-то другим
df->spi->taskCompleted();

// Запускаем таймер, который время от времени будет проверять - закончилась операция стирания или нет
df->timer.start(11, [this](){ // DF_TASK_PRIORITY
// таймер сработал - запускаем проверку
df->waitWip(this);
});
});

// подаем команду ERASE
df->small_buffer[0] = Df::CMD_MASS_ERASE;
bool r = df->spi->DmaWriteRead(df->small_buffer, df->small_buffer, 1);
if(!r){
// тоже никогда сюда не попадем
printf("Df::DfMassEraseTask error: SPI is busy! It must not happen, it's a bug!\n");
}
});

// подаем WREN команду через SPI-DMA - для разблокировки записи флеш
df->small_buffer[0] = Df::CMD_WREN;
bool r = df->spi->DmaWriteRead(df->small_buffer, df->small_buffer, 4);
if(!r){
// сюда мы никогда не попадем, а если попали - значит где-то баг в реализации драйвера SPI
// поскольку шина по определению должна уже быть свободна
printf("Df::DfMassEraseTask error: SPI is busy - bug!\n");
}
});
// вдруг очередь переполнена
if(!r){
printf("Df::DfMassEraseTask error: spi fifo full\n");
}
}

public:
Df* const df;
delegate<void()> cbk;
SST::TPriority cbk_priority;
};

// функция стирания FLASH
bool Df::MassErase(
const delegate<void()>& cbk,
SST::TPriority cbk_priority = DF_TASK_PRIORITY)
{
// создаем задачу и добавляем в очередь FLASH
bool r = task_queue.enqueueTask(
DfMassEraseTask(this, cbk, cbk_priority)
);
if(!r){
printf("Df::MassErase error: task fifo full\n");
}
// и выходим
return r;
}

// процедура проверки WIP - write/eras in progress
void Df::waitWip(DfWriteTask_base* t){ // Thread mode
// Создаем задачу и добавляем в очередь шины SPI
bool r = spi->enqueueTask([t](){
// Шина свободна - занимаем ее
t->df->setCallback_and_CS([t](){ // Handler mode
// команда подана - снимаем CS
DFCS_n_pin::set();
// окончательно слазим с шины
t->df->spi->taskCompleted();

// проверяем
if(t->df->small_buffer[2]&1){ // retry
// если операция не закончилась(флешка все еще стирается) - опять запускаем таймер
t->df->timer.start(11, [t](){ // DF_TASK_PRIORITY
t->df->waitWip(t);
});
}else{ // если закончилась
// сигнализируем пользователя, что все прошло успешно
SST::postMessage(t->cbk_priority, t->cbk);
// завершаем задачу - теперь воспользоваться flash-кой сможет кто-то другой (кто стоит в очереди)
t->df->task_queue.taskCompleted();
}
});

// подаем команду через шину(DMA)
t->df->small_buffer[0] = CMD_RDSR;
bool r = t->df->spi->DmaWriteRead(
t->df->small_buffer,
t->df->small_buffer,
3);
if(!r){
printf("Df::waitWip error: SPI is busy - bug!\n");
}
});

// вдруг очередь SPI переполнена
if(!r){
printf("Df::waitWip error: spi fifo full\n");
}
}

Согласен, код довольно сложный. Но кто-то сможет привести более простой? Приведите - и я расскажу какие в вашем коде будут проблемы wink.gif
sigmaN
Цитата
Конечо. При чем написанного на языке Javascript - полностью динамический язык, со сборкой мусора. И это работает в разы быстрее, и в десятки а то и сотню раз меньше памяти и во столько же десятков(а то и сотен) раз больше способно обрабатывать клиентов в секунду, чем сервер на C в классической многопоточной модели
Это шутка щас была да? lol.gif
Спасибо, поржал.
Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )
http://v8project.blogspot.com/

Пруфлинк бы на сравнение быстродействия серверов на JavaScript vs традиционный Сишный ))))) А то может мы тут действительно отстали от жизни совсем.

Кстати вам точно будет интересно. Советую сразу окно не закрывать, чувак там дальше показывает реальную магию https://youtu.be/gawmdhCNy-A
brag
Цитата
Приветствую!

Модель SST хорошо работает до того момента, когда с одним и тем же ресурсом нужно работать из потоков с разным приоритетом.

Реальный пример: на шине висят пультик и второй МК, связь с которым должна завершиться в течении 300 мкс (каждые 3 мс). Или объединять логику пультика (которая в разных проектах одна и та же) с логикой связи со вторым МК, или разносить по разным потокам с их синхронизацией. Во втором случае при добавлении абонента на шину первые два потока вообще менять не надо. А SST?

Нет, можно с одним и тем же ресурсом работать абсолютно с любого уровня приоритета. Но если шина уже занята более низкоприоритетной задачей, тогда ничего не остается, как добавляться в очередь. Мы не можем прервать, скажем операцию записи в Flash, потому что это может привести к порчи старницы или всего блока/сектора.
Но очередь у нас приоритетная - и если приоритет нашей задачи самый высокий он станет в самое начало очереди и как только шина освободится - задача будет выполнена.
Так что с шиной можно работать даже из прерываний и одновременно из самых низкоприоритетных потоков(хоть Idle) - какая RTOS даст Вам такую возможность? Если Вы попытаетесь залочить мютекс из прерывания - это либо приведет просто к глюку и крешу всей системы, либо(если ОС написана корректно) - прерывание может быть заблокировано в ожидании этого мютекса - со всеми вытекающими из этого проблемами

Цитата(sigmaN @ Sep 8 2016, 11:19) *
Это шутка щас была да? lol.gif
Спасибо, поржал.
Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )
http://v8project.blogspot.com/

Пруфлинк бы на сравнение быстродействия серверов на JavaScript vs традиционный Сишный ))))) А то может мы тут действительно отстали от жизни совсем.

Значит Вы не разобрались. Пруфлинк Вам не поможет - давайте сделаем по другому. Я даю Вам 2 исходника - один сишный классический многопоточный, второй на javascript. Вы у себя запускаете, даете реальную нагрузку и смотрите, когда ресурсы системы закончатся при классической многопоточной модели на С, а когда в событийной на Яваскрипте, Ну и в процессе посмотрите, сколько будут занимать памяти оба под реальной нагрузкой(скажем хотя бы 100-200 одновременных коннектов)
Для этого Вам понадобятся 2 компа - на одном сервер, на втором клиент. Только клиент должен быть гораздо мощнее сервера, чтобы он был в состоянии дать ему реальную нагрузку.
NodeJS надеюсь установите без особого бубна, компилятор C думаю тоже wink.gif

Цитата
Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )
http://v8project.blogspot.com/

В том то и дело, что надо один раз напрячься и сделать, а потом пользоваться сколько угодно раз. Спасибо гуглу за их прекрасный движок и разработчикам NodeJS за то, что сделали из этого движка замечательный мощный, быстрый и удобный инструмент
sigmaN
Прям с удовольствием потестирую )))))
Особенно создам ситуацию, когда оба сервера получают от клиента запрос требующий интенсивной обработки и буду весело наблюбдать как ваш JavaScript сервер перестанет обслуживать остальных клиентов.
DASM
На самом деле мы наверное действительно отстали от жизни. Сейчас цена железа стремится к нулю, а труд программистов такой тенденции не имеет. Посмотрите на тот же Андроид. Та же задача на голом С потребовала бы 400 Мгц (да тот же WinMobile вспомним). Но сейчас не спроста средний проц имеет 8 ядер под 2 ГГц, выходит дешевле и писать проще. Ладно, сие есть лирика, но динозавром выглядеть не хочется. Подозреваю что на SST можно решить ровно теже задачи, что и на обычной ОСи, и чаще всего это будет экономичнее и быстрее. Все же Javascript для эмбеддед старого доброго не слишком показательная и типичная задача. Ну реально смешно опрашивать кнопку в отдельном громоздком потоке в бесконечном цикле со всеми жирными объектами синхронизации. Мне представляется оптимальным некий гибрид обоих подходов к многозадачности. Ну как бы с идеей SST крутится всякая мелкая и не очень логика, а традиционный подход продолжает работать одновременно для не очень связных сложных задач, например веб-сервер крутится, GUI и что еще. На верхнем уровне это три разных задачи для традиционного планировщика, а внутри каждой - подход в духе SST. Может я не уловил сути и написал сейчас бред, тогда сорри.
brag
Цитата
Все же Javascript для эмбеддед старого доброго не слишком показательная и типичная задача.

Javascript очень даже уместен в embedded.

Цитата
Прям с удовольствием потестирую )))))

Отлично, готовлю код.

Цитата
Особенно создам ситуацию, когда оба сервера получают от клиента запрос требующий интенсивной обработки и буду весело наблюбдать как ваш JavaScript сервер перестанет обслуживать остальных клиентов.

Мы сейчас говорим об оверхеде на треды, а не о интенсивных математических задачах. О них поговорим позже, и там тоже много чего интересного.
JavaScript не предназначен для интенсивных математических вычислений, для этого есть другие прекрасные языки, которые свободно могут работать в паре с Javascript.
Так что давайте для начала протестируем оверхед - сервер на C и на JS будет просто принимать коннект и отправлять клиенту какой-нибудь ответ скажем по таймеру, 10 ответов в секунду.
arhiv6
brag, на сколько я понял, в обычном SST пока задача X не выполнена, менее приоритетные задачи блокированы. В таком случае, если эта задача X заняла ресурс - ту же шину SPI, мы никак не можем отдать управление менее приоритетным процессам и ядро простаивает. Если не сложно, можете подробнее описать, как правильно избавиться от этого ограничения? На сколько я понял, у Вас задача делится на несколько независимых (X1, X2) и они работают по событиям: X1 заняла шину и закончила работу, по событию освобождения шины вызвалась задача X2. Так?
brag
Цитата
Ну как бы с идеей SST крутится всякая мелкая и не очень логика, а традиционный подход продолжает работать одновременно для не очень связных сложных задач, например веб-сервер крутится, GUI и что еще. На верхнем уровне это три разных задачи для традиционного планировщика, а внутри каждой - подход в духе SST. Может я не уловил сути и написал сейчас бред, тогда сорри.

Наоборот, веб-сервер и GUI - это типичные задачи для SST и классические потоки здесь только создают проблемы. Да, их можно реализовать на классических потоках, но рано или поздно там появятся ActiveObjet-ы, а это значит, что треды пора выбрасывать в мусорку и переходить на более простой SST. в котором все является этими самыми Active Objectamи
sigmaN
Ладно бы товарищ просто говорил что ребят, так проще и времени программиста меньше уходит. Все переходим на JS. Никто б с ним не спорил.
Но тут он совсем уже пошел по беспределу, заявив
Цитата
Нет, физически там один поток и один стек.

Цитата
При чем написанного на языке Javascript - полностью динамический язык, со сборкой мусора. И это работает в разы быстрее, и в десятки а то и сотню раз меньше памяти и во столько же десятков(а то и сотен) раз больше способно обрабатывать клиентов в секунду, чем сервер на C в классической многопоточной модели
После такого надо бы и пруфы в студию.
Потому как JS то и Multi-threading поддерживает так то.
Но у нас всё в один стек в один поток и в десятки и сотни раз круче сервер получается )))))))

Цитата
Мы сейчас говорим об оверхеде на треды, а не о интенсивных математических задачах. О них поговорим позже, и там тоже много чего интересного.
JavaScript не предназначен для интенсивных математических вычислений, для этого есть другие прекрасные языки, которые свободно могут работать в паре с Javascript.
Так что давайте для начала протестируем оверхед - сервер на C и на JS будет просто принимать коннект и отправлять клиенту какой-нибудь ответ скажем по таймеру, 10 ответов в секунду.
Давайте вы наконец осознаете, что классический сервер на то и создает потоки/процессы под кажого клиента, чтобы их обслуживать ПАРАЛЛЕЛЬНО. Параллелизм этот обеспечивается в данном случае таск шедулером операционной системы. О каком овэрхеде на трэды мы говорм, если у вас трэд один и стек тоже ))))) Нет трэдов нет оверхедов.

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

У меня создается впечатление, что у вас когда-то не заладилось с многопоточным программирвоанием и вы ушли в секту синглтрэад-одностековщиков )))))))))
AHTOXA
Цитата(sigmaN @ Sep 8 2016, 13:54) *
Но у нас всё в один стек в один поток и в десятки и сотни раз круче сервер получается )))))))

Вполне возможно, что так и будет. Потому что при большом наплыве клиентов накладные расходы на создание процесса на каждого клиента будут очень велики. Поэтому и в сях сейчас развивают асинхронное обслуживание сети. (async.io)
Процессоры сейчас быстрые, сети толстые, так что проще быстренько обслужить клиента, не создавая для него отдельного процесса, и перейти к следующему.
Насчёт жабаскрипта в эмбеддед я пока тоже не понялsm.gif
brag
Цитата
brag, на сколько я понял, в обычном SST пока задача X не выполнена, менее приоритетные задачи блокированы.

Верно!
Цитата
В таком случае, если эта задача X заняла ресурс - ту же шину SPI, мы никак не можем отдать управление менее приоритетным процессам и ядро простаивает.

А это не совсем верно. Если задача постоянно работает с шиной через дрыгание ногами - непрерывно туда что-то передает, тогда да, все задачи с приоритетом таким же или ниже блокированы. А если задача использует DMA или просто прерывания, тогда эта задача будет только обрабатывать эти прерывания, а остальное время будет отдано другим.

Цитата
Если не сложно, можете подробнее описать, как правильно избавиться от этого ограничения? На сколько я понял, у Вас задача делится на несколько независимых (X1, X2) и они работают по событиям: X1 заняла шину и закончила работу, по событию освобождения шины вызвалась задача X2. Так?

Тут немножко стоит ввести терминологию. Я то понимаю и мне это естественно, но другим не очень. И так, терминология:
1. Событие(event) - это некоторое абстрактное понятие. Каждое событие имеет приоритет. Прерывание - это тоже событие.
2. Очередь событий(Event Queue) - это такая очередь, в которую можно добавлять события. При чем эти события сортируются в этой очереди в порядке:
а) - приоритета
б) - поступления
Тут есть некоторая оговорка - прерывание - это тоже событие, но размещением их в очереди занимаемся не мы, а процессор.
3. Обработчик события(Event Handler) - это функция связанная с конкретным событием, которая будет выполнена, как только будет для этого время.
4. Планировщик событий - достает события из очереди и выполняет для них соответствующие обработчики. При чем, если какой-то обработчик уже работает - он будет прерван обработчиком более приоритетного события. Планировщик состоит из 2х частей:
а) процессора, его контроллера прерываний который занимается планировкой событий приоритета выше или равного IRQ_MIN_PRIORITY. этот приоритет всегда выше приоритета обычных событий(user mode)
б) кусок кода на 200 строк, который занимается планировкой событий приоритета ниже IRQ_MIN_PRIORITY - то есть обычных пользовательских событий
5. Задача(Task) - это исполняемый обьект. Она тоже имеет свой приоритет. Задача состоит из:
а) обработчика события запуска задачи (обычная функция, физически у меня в С++ это operator() - почитайте про него)
б) любого количества обработчиков других событий. То есть задача может добавлять любые собития в очередь, а так же их обрабатывать.
в) любого количества внутренних переменных - это называется состояние задачи
6. Очередь задач - это очередь, в которую помещаются задачи в порядке их приоритета, затем в порядке попадания в очередь.

С этим все ясно?

Цитата
Но у нас всё в один стек в один поток и в десятки и сотни раз круче сервер получается )))))))

Так Вы тестировать сервак будете, делать для Вас(и остальных) код? или будете продолжать дальше глумиться, не понимая предмета?

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

Поздравляю, Вы поняли о чем идет речь wink.gif

Цитата
Насчёт жабаскрипта в эмбеддед я пока тоже не понялsm.gif

http://embeddednodejs.com/chapters.html
sigmaN
Цитата
Поэтому и в сях сейчас развивают асинхронное обслуживание сети.
Это когда куча сокетов в массиве а потом в цикле WaitForMultipleObjects()? Старо как мир же )

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

Цитата
Так Вы тестировать сервак будете, делать для Вас(и остальных) код? или будете продолжать дальше глумиться, не понимая предмета?
ну а смысл что-то тестировать если и так ясно, что вы говорили про сценарий с 2мя байтами и большим коннект рэйтом, а я про обслуживание клиентов параллельно. Две разные задачи два разных решения. Доказывать Вам, что то-же самое решение будет потреблять еще меньше ресурсов если заменить JS на C у меня желания нет. Это уже совсем холливар какой-то получится.
brag
Цитата
Это когда куча сокетов в массиве а потом в цикле WaitForMultipleObjects()? Старо как мир же )

Вы все равно ничего не поняли. Ключевое слово Wait - оно у вас в голове и у меня тоже было, и очень трудно его было оттуда выбросить.
WaitForMultipleObjects() заблокирует поток, а у нас нет блокировок, нет Wait, у нас есть On

Цитата
Ну товарищ не уточнил мы хотим обслужить 10000 коннектов в секунду с отдачей каждому по 2 байта или подключаем 900 клиентов и каждый просит у сервера что хочет. В том числе и нечто, что заставляет сервер подождать(прихода некоего события или интенсивные вычисления потребуются).

Сервер будет отдавать запрошенный пользователем файл - устроит? Тестить будете?
В математику давайте пока лесть не будем, об этом позже и там тоже все довольно интересно и тоже все отлично ложится на SST-модель. Просто Javascript не предназначен для сложных вычислений, для этого есть другие инструменты, которые свободно работают в паре с JS.

Цитата
ну а смысл что-то тестировать если и так ясно, что вы говорили про сценарий с 2мя байтами и большим коннект рэйтом, а я про обслуживание клиентов параллельно. Две разные задачи два разных решения. Доказывать Вам, что то-же самое решение будет потреблять еще меньше ресурсов если заменить JS на C у меня желания нет. Это уже совсем холливар какой-то получится.

Параллельные вычисления можно проводить тоже без потоков, которые у Вас засели в голове. Планировщик запросто может раскидывать разные события по разных процессорах и обрабатывать таким образом несколько событий одновременно - сколько процессоров(ядер) столько и будет одновременно работать событий. При чем стеков нужно столько, сколько имеется процессоров.
А в Вашей многопоточной модели нужно стеков столько, сколько есть потоков(по несколько потоков на каждый коннект к серверу)
sigmaN
Цитата
Вы все равно ничего не поняли. Ключевое слово Wait - оно у вас в голове и у меня тоже было, и очень трудно его было оттуда выбросить.

Могу в аналогичном тоне заявить, что наверно вы тоже ничего не поняли и у вас в голове On. Чтобы кто-то вызвал этот On должен быть где-то и Wait и цикл бесконечный.
Например в эмбэддэд с этим проще, потому что этот вэйт реализуется логикой отвечающей за генерацию прерывания по изменению уровня пина(например). И вы потом гордо залетаете в прерывание, оттуда вызываете свой On и радуетесь. Но кто-то до этого аппаратно делал Wait )

Даже в SST таки есть бесконечный цикл, который крутится и вызывает нужные On взависимости от ивэнтов в очереди.
Чем это отличается от WaitForMultipleObjects() для сокетов? Я не знаю. Наверно ни чем)
Но делая этот системный вызов я отдаю ресурсы другим потокам в ОС(ох уж эти потоки опять а!). Хотя таки да, мог бы крутить цикл и проверять сокеты вручную, потом ложить ивэнт в очередь а потом вызывать On И как глубоко бы вы не зкапывали этот цикл или вэйт он всё равно где-то есть.

Если вы в JS подписываетесь на какое-то событие, чтобы движок дёргал ваш On - это всего-лишь означает что нужный Wait и цикл скрыты где-то далеко.
Так что не шибко то разгоняйтесь с перестраиванием мозга на On.

Надеюсь вы таки поняли о чем я говорю ) Потому что хоть вы в это и не верите, а большинство и с принципами работы SST тут разобрались и про On знают не по наслышке тоже давно )

Цитата
Сервер будет отдавать запрошенный пользователем файл - устроит? Тестить будете?
В математику давайте пока лесть не будем, об этом позже и там тоже все довольно интересно и тоже все отлично ложится на SST-модель. Просто Javascript не предназначен для сложных вычислений, для этого есть другие инструменты, которые свободно работают в паре с JS.
Файл объемом 1Гб, скорость сети каждого клиента ограничиваем шейпером на роутере до....1мегабита ) Ну, чтоб точно заметно было как остальные клиенты сервера курят бамбук )
Не тратьте силы и так всё ясно же. Застрянет ваш сервер на одном клиенте и пока тот файл не скачает - всё это весело встанет колом.

Ааа, нет! Я придумал! В системе появится таймер, он будет генерировать событие onTimer, в этом событии вы будете проходиться по массиву с сокетами и каждому отправлять там по пару байт данных. Обходя сокеты по кругу. А потом скажите ну вот, все качают и никаких вэйтов ))))))))))
DASM
а не в безопастности ли выполнения коренное различие? в системе с различными стеками потоков проще убить взбунотовавщийся поток, он в теории не нарушит работу других.
brag
Цитата(sigmaN @ Sep 8 2016, 12:33) *
Могу в аналогичном тоне заявить, что наверно вы тоже ничего не поняли и у вас в голове On.

Это верно. Я выбросил из головы Wait и вдул туда On.

Цитата(sigmaN @ Sep 8 2016, 12:33) *
Чтобы кто-то вызвал этот On должен быть где-то и Wait и цикл бесконечный.
Например в эмбэддэд с этим проще, потому что этот вэйт реализуется логикой отвечающей за генерацию прерывания по изменению уровня пина(например). И вы потом гордо залетаете в прерывание, оттуда вызываете свой On и радуетесь. Но кто-то до этого аппаратно делал Wait )
Даже в SST таки есть бесконечный цикл, который крутится и вызывает нужные On взависимости от ивэнтов в очереди.

Нет, в этом нет необходимости. Хотя физически, как Вы подметили один такой цикл вполне может быть, это обусловлено самой архитектурой конкретного процессора. Но он пустой, кроме как инструкции sleep там больше ничего нет.


Цитата(sigmaN @ Sep 8 2016, 12:33) *
Чем это отличается от WaitForMultipleObjects() для сокетов? Я не знаю. Наверно ни чем)

А тут Вы уже не правы. WaitForMultipleObjects блокирует пользовательский поток. Все, поток мертвый. В то время, как в SST или яваскрипте в принципе нет такой возможности

Цитата
Но делая этот системный вызов я отдаю ресурсы другим потокам в ОС(ох уж эти потоки опять а!). Хотя таки да, мог бы крутить цикл и проверять сокеты вручную, потом ложить ивэнт в очередь а потом вызывать On И как глубоко бы вы не зкапывали этот цикл или вэйт он всё равно где-то есть.
Если вы в JS подписываетесь на какое-то событие, чтобы движок дёргал ваш On - это всего-лишь означает что нужный Wait и цикл скрыты где-то далеко.
Так что не шибко то разгоняйтесь с перестраиванием мозга на On.

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


Цитата(DASM @ Sep 8 2016, 12:39) *
а не в безопастности ли выполнения коренное различие? в системе с различными стеками потоков проще убить взбунотовавщийся поток, он в теории не нарушит работу других.

Для безопасности есть MPU/MMU, если оно действительно нужно - тогда запросто берем и используем.

Цитата
Файл объемом 1Гб, скорость сети каждого клиента ограничиваем шейпером на роутере до....1мегабита ) Ну, чтоб точно заметно было как остальные клиенты сервера курят бамбук )
Не тратьте силы и так всё ясно же. Застрянет ваш сервер на одном клиенте и пока тот файл не скачает - всё это весело встанет колом.

Идет. Будете тестить? Файлик сами приготовите любой. Только боюсь, в этом случаи мы не SST-модель будем тестить, а Ваш жесткий диск или Ваш порезанный канал. Но как только Вы подсунете нормальный файл на несколько КБ-МБ и сделаете канал, достаточный для закрузки сервака на полную - все станет совсем иначе.
Хотя даже с гиговым файлом/файлами однопоточный javascript покажет выше производительность, чем классический многопоточный C. Проверим?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.