nixp.ru v3.0

18 октября 2017,
среда,
12:22:59 MSK

DevOps с компанией «Флант»
Longobard написал 9 января 2006 года в 00:20 (326 просмотров) Ведет себя как мужчина; открыл 291 тему в форуме, оставил 2499 комментариев на сайте.

Задача такая:

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

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

Спасибо :)

Uncle Theodore

Определи сегмент shared memory, сообщи его адрес всем процессам и приаатачь. Засунь свой объект в общую память и пользуй изо всех процессов. Больше никак.

Good Luck,

UT

vkle

Может быть pipe или socket сойдет?

Longobard
Uncle Theodore
Определи сегмент shared memory, сообщи его адрес всем процессам и приаатачь. Засунь свой объект в общую память и пользуй изо всех процессов. Больше никак.

Good Luck,

UT

Как засунуть — понятно (Стивенс есть). А как вызвать метод обьекта, находящегося в общей памяти, без копирования его в память процесса?

Uncle Theodore
LONGOBARD
Как засунуть — понятно (Стивенс есть). А как вызвать метод обьекта, находящегося в общей памяти, без копирования его в память процесса?

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

Good Luck,

UT

rgo
LONGOBARD
Как засунуть — понятно (Стивенс есть). А как вызвать метод обьекта, находящегося в общей памяти, без копирования его в память процесса?

Моих познаний для ответа на этот вопрос не хватает жутко: как грузиться elf, как g++ оформляет вызов virtual методов, и куча вопросов о shared memory (ну не пользовался я ею не разу) для меня остаются загадкой.

Но попробуй затолкать все объекты которые надо так использовать в libmylib.so, и компоновать приложения с -lmylib. Есть у меня подозрение, что тогда адреса всех функций класса окажутся одинаковыми в обоих процессах, и, может быть, даже будут вызываться без SEGFAULT и прочих сигналов.

Uncle Theodore

Напишу подробнее

хедер: myheader.h

class myclass{
 int value;
 void mymethod(){
   //do something
  };
 myclass(int v){
   value = v;
  };
}

Первый процесс (создающий объект)

#include "myheader.h"
int main() {
  //obtain a key for the ipc stuff
  if((ipc_key = ftok(".", 'R')) == -1)
    {
      cout << "Error key generating!\n";
      return -1;
    };
  //obtain some memory
  if((mem_id = shmget(ipc_key,sizeof(myclass),IPC_CREAT|0666))==-1)
    {
      cout << "Error getting shared memory!\n";
      return -1;
    };
  //attach the segment
  myclass * pointer = (myclass *)shmat(mem_id, NULL, 0666);
  //mark the segment to be destroyed when last process detaches it
  shmctl(mem_id,IPC_RMID,NULL);
  
  //now, do stuff
  *pointer = myclass(12);
  pointer -> mymethod();
  //when done, detach
  shmdt(pointer);
  return 0;
}

Второй процесс должен знать memory id сегмента. Можно получить его при запуске или через pipe/сокет

Второй процесс:

#include "myheader.h"
int main()
{
 int mem_id = str2int(argv[1]);
 //str2int is a function of mine
 myclass *pointer = (myclass*)shmat(mem_id, NULL, 0666);
 pointer -> mymethod();
 //detach when done
 shmdt(pointer);
 return 0;
}

Конечно, надо еще следить, чтобы сегмент не был уничтожен первым процессом, прежде чем второй получит шанс его использовать. Но это уже тонкости…

Good Luck,

UT

rgo
Uncle Theodore
Напишу подробнее


угу это работает, но не для виртуальных функций…То есть если myclass::mymethod объявить как virtual оно не работает. Причём я не понял почему :(. Надо будет повтыкать в работу ld.

Longobard

Uncle Theodore:

Спасибо за подробный ответ!

Но что делать, если (например) обьект является обьектом шаблонного класса? Или же у него есть виртуальные функции, как быть тогда?

decvar
Или же у него есть виртуальные функции, как быть тогда

что значит шаблонного? если инстанс в памяти есть, и vtable для методов есть, то без разници что за класс. главное что бы ты смог докастить void* до него. а для этого надо что бы сам класс был не абстрактный…

ЗЫ

Или я не понял проблемы?

Uncle Theodore

Скорее всего, проблема в изменении имен функций в С++. Что с этим делать я не знаю, extern «C» запретит оверлоадинг напрочь. Наверное, придется изголяться… Избавляться от виртуальных функций, например.

Good Luck,

UT

rgo

не, имена там не причём.

Виртуальные функции не работают не из-за shared memory, и даже не из-за того что методы класса валяются в библиотеке. Ежели память под объект выделять не shmget, а malloc, тот же самый SEGFAULT вылетает. Зато если пользоваться new то всё нормально. То есть, конструкция `*pointer = new myclass;' делает что-то не то. Хотя что, собственно, не то, я так и не понял. Достало разбираться в реализации всех этих virtual call’ов.

В общем, вопрос к знатокам C++, как в C++ можно сказать оператору new, что кусок памяти уже выделен, и его осталось только привести в юзабельный вид. В BorlandC 3.1, такая фича была, как она использовалась не помню. И подозреваю что это было свойством borlandc++, а не c++, но фича была.

Uncle Theodore
rgo
не, имена там не причём.

Виртуальные функции не работают не из-за shared memory, и даже не из-за того что методы класса валяются в библиотеке. Ежели память под объект выделять не shmget, а malloc, тот же самый SEGFAULT вылетает. Зато если пользоваться new то всё нормально. То есть, конструкция `*pointer = new myclass;' делает что-то не то.

new вызывает конструктор. Мне удалось воспроизвести то, о чем ты говоришь (Извиняюсь за длинный пример, но мне было интересно).

#include 
using namespace std;
class virt
{
public:
  int value;
  virtual int display() = 0;
};
class concr: public virt
{
public:
  int display()
  {
    cout << "Displaying value " << value << endl;
    return value;
  };
};
int main()
{
  concr *pointer = (concr*)malloc(sizeof(concr));
// так -- сегфолт, но если откомментарить следующую строку --
// все очень путем. Или если заменить предыдущую
//строку на pointer = new concr
//  new(pointer) concr;
  pointer -> value = 12;
  pointer -> display();
  return 0;
};


В общем, вопрос к знатокам C++, как в C++ можно сказать оператору new, что кусок памяти уже выделен, и его осталось только привести в юзабельный вид. В BorlandC 3.1, такая фича была, как она использовалась не помню. И подозреваю что это было свойством borlandc++, а не c++, но фича была.

Вот так, наверное

new(pointer) concr;

Не думаю, что это фича Борланда, но почему-то не могу найти вразумительной ссылки…

Соответственно, что-то вроде этого надо делать для shmget

Good Luck,

UT

rgo

new(pointer) concr; //<-- в g++ это работает.

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

Хотя для не-виртуальных функций работает (это я про shmget и далее). Там система разрешения адресов символов работает. Одна странность осталась: если myclass не в .so класть, а статически линковать, то какие-то аберрации поведения класса наблюдаются :-/