Перейти к содержанию
    

Система, управляемая событиями и SST(super-simple tasker)

Нет, физически там один поток и один стек.
Хотелось бы посмотреть исходники такого сервера. Это в опэнсоурсе есть?

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


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

Хотелось бы посмотреть исходники такого сервера. Это в опэнсоурсе есть?

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

https://nodejs.org/dist/latest-v4.x/docs/api/http.html

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();
   });
 });
});

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

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


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

Слезть с шины, и что делать? Висеть в задаче без дела, и сам не гам и никому не дам? Или отдать управление другой, пусть менее приоритетной, задаче? Как?

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


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

Приветствую!

 

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

 

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

 

Илья

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


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

Слезть с шины, и что делать? Висеть в задаче без дела, и сам не гам и никому не дам? Или отдать управление другой, пусть менее приоритетной, задаче? Как?

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

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

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

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

// функция занятия шины
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");
}
}

Согласен, код довольно сложный. Но кто-то сможет привести более простой? Приведите - и я расскажу какие в вашем коде будут проблемы ;)

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


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

Конечо. При чем написанного на языке Javascript - полностью динамический язык, со сборкой мусора. И это работает в разы быстрее, и в десятки а то и сотню раз меньше памяти и во столько же десятков(а то и сотен) раз больше способно обрабатывать клиентов в секунду, чем сервер на C в классической многопоточной модели
Это шутка щас была да? :lol:

Спасибо, поржал.

Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )

http://v8project.blogspot.com/

 

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

 

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

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


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

Приветствую!

 

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

 

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

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

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

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

 

Это шутка щас была да? :lol:

Спасибо, поржал.

Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )

http://v8project.blogspot.com/

 

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

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

Для этого Вам понадобятся 2 компа - на одном сервер, на втором клиент. Только клиент должен быть гораздо мощнее сервера, чтобы он был в состоянии дать ему реальную нагрузку.

NodeJS надеюсь установите без особого бубна, компилятор C думаю тоже ;)

 

Так, чисто чтоб вы понимали как внутри JavaScript движок напрягается, чтоб вы тут свои onSomething события обрабатывали )

http://v8project.blogspot.com/

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

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


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

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

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

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


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

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

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


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

Все же Javascript для эмбеддед старого доброго не слишком показательная и типичная задача.

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

 

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

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

 

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

Мы сейчас говорим об оверхеде на треды, а не о интенсивных математических задачах. О них поговорим позже, и там тоже много чего интересного.

JavaScript не предназначен для интенсивных математических вычислений, для этого есть другие прекрасные языки, которые свободно могут работать в паре с Javascript.

Так что давайте для начала протестируем оверхед - сервер на C и на JS будет просто принимать коннект и отправлять клиенту какой-нибудь ответ скажем по таймеру, 10 ответов в секунду.

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


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

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

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


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

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

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

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


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

Ладно бы товарищ просто говорил что ребят, так проще и времени программиста меньше уходит. Все переходим на JS. Никто б с ним не спорил.

Но тут он совсем уже пошел по беспределу, заявив

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

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

Потому как JS то и Multi-threading поддерживает так то.

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

 

Мы сейчас говорим об оверхеде на треды, а не о интенсивных математических задачах. О них поговорим позже, и там тоже много чего интересного.

JavaScript не предназначен для интенсивных математических вычислений, для этого есть другие прекрасные языки, которые свободно могут работать в паре с Javascript.

Так что давайте для начала протестируем оверхед - сервер на C и на JS будет просто принимать коннект и отправлять клиенту какой-нибудь ответ скажем по таймеру, 10 ответов в секунду.

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

 

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

 

У меня создается впечатление, что у вас когда-то не заладилось с многопоточным программирвоанием и вы ушли в секту синглтрэад-одностековщиков )))))))))

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


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

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

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

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

Насчёт жабаскрипта в эмбеддед я пока тоже не понял:)

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


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

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)

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

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

 

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

http://embeddednodejs.com/chapters.html

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


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

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...