LINUX.ORG.RU

Самодельный загрузчик elf?


0

1

Моделируется работа сети микроконтроллерных устройств в нормальном режиме общающихся по радиоканалу. В настоящий момент каждое из моделируемых устройств представляет собой отдельный ELF, работающий в отдельном процессе и общающийся с сервером через TCP (часть кода по работе с приемо-передатчиком заменена на код общения с сервером, остальной код такой же как и в микроконтроллерах). Сервер постоянно висит на poll()-е, протокол простой запрос-ответ.

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

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

Вопрос: чем можно заменть TCP? Я так поимаю любые другие средства IPC тут будут абсолютно так же тормозить?

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

Есть вариант сделать свой простой загрузчик ELF-ов (их можно слинковать статически).

Какие еще есть варианты?

★★★★

> Какие еще есть варианты?

  1. загрузить и форкнуться
  2. форкнуться и загрузить

(предыдущую тему не читал/не помню)

arsi ★★★★★
()
Ответ на: комментарий от arsi

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

alexru ★★★★
() автор топика

Код исплняемый за один вызов очень маленький, он максимум 10 мс исполняется на 8 МГц микроконтроллере, сколько он исполняется на x86 и нескольких ГГц-ах я даже не знаю чем замерить.

alexru ★★★★
() автор топика

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

bvvv
()
Ответ на: комментарий от arsi

Какие есть средства IPC я знаю, но есть подозрение, что они все бубт тормозить абсолютно так же. Особенность алгоритма планирования и отсылки сообщей сервером такова, что в любой момент сообщения параллельно отсылаются максимум на 2-3 процесса, а часто на 1, так что от парралельности исполнения толку не много.Соответственно хочется вызовы через IPC заменть на прямые вызовы функций. То-есть нужно иметь в памяти 3 сегмента кода, которым можно было-бы «подсовывать» несколько сотен сегментов данных и просто вызывать нужные функции. Естественно несколько сотен сегментов кода тоже допускается наплодить, но так, чтобы к их функциям был прямой доступ.

alexru ★★★★
() автор топика
Ответ на: комментарий от bvvv

Используйте имитационное моделирование, а не аналоговое

Это как?

Зачем вам вообще TCP? Почему нельзя сделать всё в одном процессе и обмен данными через общую память?

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

Но есть несколько разновидностей устройств и как их все запустить в одном процессе я не представляю.

alexru ★★★★
() автор топика

Когда код работает в микроконтроллере main() выглядит схематично так:

int main(void)
{
  init();
  while(1)
    run_task();
}

Фактически для моделирования этот main() не используется и сервер последоватьельно на каждой копии программы звпускает run_task(), которая в свою очередь может привести к отправке сообщения всем. Есть еще имитация железных таймеров для всех устройств, но это мелочи.

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

> так что от парралельности исполнения толку не много.

от параллельности исполнения толк тот, что во всех процессах свой сегмент данных // K.O.

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

arsi ★★★★★
()

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

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

Всего несколько сотен устройств, наверняка не меньше двух ядер, и уже тормоза? Когда всё происходит на одной машине? По-моему, ты что-то не так делаешь. Или настраивай TCP, или попробуй другой протокол (UDP или PF_UNIX).

tailgunner ★★★★★
()
Ответ на: комментарий от arsi

и полностью отвергаете нормальное решение по каким-то надуманным причинам.

Причины не надуманные, а вполне реальные, средства IPC на несколько порядков медленне, чем код который исполнится при приеме комманды. Таким образм узким местом становится взаимодействие это кучи прцессов.

То что они тормозят я уверен, потому что проводил соответствующие экспериметы (это было около года назад, я тут несколько топиков по этому поводу создавал). Они просто не предназначенны для непрерывного обмена сообщениями.

alexru ★★★★
() автор топика
Ответ на: комментарий от tailgunner

Всего несколько сотен устройств, наверняка не меньше двух ядер, и уже тормоза? Когда всё происходит на одной машине?

Может я тут не прав. Как это выглядит сейчас: устройств пока что вообще 10, на сервере - стандартный TCP сервер (socket/bind/listen/accept), дальше poll() на всех клиентов. Клиент присылает сообщение, что он готов, ему в ответ разрешение исполнять дальше, и т. д.

То-есть в данной схеме все блокировки только на poll() на сервере и recv() на клиентах. Тормоза в данном случае - это 0% использования CPU и примерно такой вывод комманды time ./server:

real	0m30.152s
user	0m0.060s
sys	0m0.640s
То-есть большую часть времени он нтчего не делает.

По-моему, ты что-то не так делаешь. Или настраивай TCP, или попробуй другой протокол (UDP или PF_UNIX).

Может и не так, попробую на простом тестовом примере воспроизвести.

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

> Может я тут не прав. Как это выглядит сейчас: устройств пока что вообще 10, на сервере - стандартный TCP сервер (socket/bind/listen/accept), дальше poll() на всех клиентов

То есть ты даже Nagle не отключаешь?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

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


  struct sockaddr_in serveraddr;
  struct sockaddr_in clientaddr;
  int yes = 1;
  int listener;
  int addrlen = sizeof(clientaddr);

  if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    eexit("socket()");

  if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(int)) == -1)
    eexit("setsockopt()");

  if (setsockopt(listener, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(int)) == -1)
    eexit("setsockopt()");

  serveraddr.sin_family = AF_INET;
  serveraddr.sin_addr.s_addr = INADDR_ANY;
  serveraddr.sin_port = htons(PORT);
  memset(&serveraddr.sin_zero, 0, 8);

  if (bind(listener, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
    eexit("bind()");

  if (listen(listener, 1) == -1)
    eexit("listen()");

  while (1)
  {
    poll()
    ...

    if (fds[i].revents & POLLIN)
    {
      recv()
      1 или несколько send()
    }
  }
на клиенте:
  struct sockaddr_in serveraddr;

  if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    _perror("socket()");

  memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));
  serveraddr.sin_family = AF_INET;
  serveraddr.sin_port = htons(PORT);
  serveraddr.sin_addr.s_addr = inet_addr(SERVER);

  if (connect(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    _perror("connect()");

  ...
  while(1)
  {
    run_task();
    send();
    recv();
  }

SO_REUSEADDR не работает на PF_UNIX, так что с ходу их проверить не получилось, нужо разбираться.

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

> SO_REUSEADDR не работает на PF_UNIX

Попробуй SOCK_DGRAM на AF_INET

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Я бы включил TCP_NODELAY именно на клиенте.

Так значительно лучше:

real	1m6.449s
user	0m3.316s
sys	0m36.706s

Ах да, и REUSEADDR скорости точно не добавит %)

Оно же в процессе работы вроде не влияет ни на что? Или влияет?

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

> Так значительно лучше:

real 1m6.449s

user 0m3.316s

sys 0m36.706s

Хм. По-моему, это значительно хуже - времена выросли в разы и на порядки %)

Ах да, и REUSEADDR скорости точно не добавит %)

Оно же в процессе работы вроде не влияет ни на что? Или влияет?

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

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Хм. По-моему, это значительно хуже - времена выросли в разы и на порядки %)

Ну в смысле все эти процессы делом занлись. В идеале там должно быть real=user+sys. Раньше виртуальное время на симулируемых процессорах шло примерно в 3 раза быстрее реального, теперь оно идет в 100 раз быстрее, собственно это и нужно.

Но, вообще говоря, то, что ты это используешь, как бы намекает на грязное завершение программ.

Да, я знаю :) Этот сервер по идее должен работать непрерывно, поэтому чистое завершение пока не так важно, позже приделаю.

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

времена выросли в разы и на порядки %)

На точное время real можно не обращать внимания - это я где Ctrl-C нажал так и получилось, симуляция бесконечная. Как критерий эффетивности я использую реальное время/ на виртуальное время на узлах.

alexru ★★★★
() автор топика

Ну хорошо, допустим с TCP удалось добиться приличной производительности, завтра попробую с UDP, может даже побыстрее будет.

А если вернуться к исходной идеи и чсто ради интереса попробовать рукми загружать эти ELF-ы. Мне этот процесс видится так:

1. Собрать ELF-ы с статически и с PIC. 2. Загрузить сколько нужно копий, код в mmap(.., PROT_EXEC, ...), данные в свою область для каждого «процесса». 3. Поправить GOT для для каждого «процесса», я так понимаю для PIC кода они должны быть и их правки будет достаточно? 4. Собственно найти те пару функций, которые нужно дергать и дергать их.

Я что-то крупное упускаю?

alexru ★★★★
() автор топика
Ответ на: комментарий от alexru

> Я что-то крупное упускаю?

Да. Старую истину «дьявол в мелочах». Хотя, если тебе хочется написать свой загрузчик... %)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Рассчет на то, что статичекие ELF-ы должно быть довольно легко загрузить. Я в любом случае попробую это сделать, даже если к этому проекту не подойдет, хоть опыт будет :)

alexru ★★★★
() автор топика

Если соединений с сервером сотни, то надо использовать не poll, а epoll. А лучше - libevent.

Sorcerer ★★★★★
()
Ответ на: комментарий от alexru

Например чтобы сервер не блокировался в send()в ожидании пока клиент соизволит прочитать ответ.

anonymous
()
Ответ на: комментарий от anonymous

Да, согласн, для сервера можно попробовать добавить.

alexru ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.