1. C++ / Говнокод #12396

    +10

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    8. 8
    for (int i = 0; i < 15; i++) {
        // Прикольное место, надо прокомментировать
        // Если наша функция Fork() вернула true, то мы
        // в дочернем процессе и форкаться больше не надо
        // Форканье - это задача родителя
        // Дети этим заниматься не должны
        if (Fork()) break;
    }

    Создание дочерних процессов. Вот такой костыль. Есть идеи, как улучшить?

    Запостил: kafeman, 07 Января 2013

    Комментарии (53) RSS

    • > Дети этим заниматься не должны
      Чем бы дитя не тешилось, лишь бы не форкалось.

      > Есть идеи, как улучшить?
      Ну я в свое время, когда форкал процессы, вообще не возвращал управления из функции, подобной вашей Fork, в дочернем процессе:
      pid_t do_in_child_process(int (*f)(void *), void *data) {
          pid_t pid = fork();
          if (pid)
              return pid;
          int result = f(data);
          exit(result);
      }
      
      // пример
      pid_t child = do_in_child_process(&some_function, some_data);
      Ответить
    • Лучше пулы юзать.
      Ответить
      • Ну как бы при старте пула процессов и понадобится этот код с циклом и форками... Так что вашу мысль про пулы до конца не понял.
        Ответить
        • > do_in_child_process
          должен получать указатель еще указатель на пул и ставить в очередь этого пула

          {@see ExecutorService}
          Ответить
          • А, так это к моему коду коммент был. Я думал к ТС'овскому.

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

            > должен получать указатель еще указатель на пул и ставить в очередь этого пула
            Ну да, логично. Еще до кучи можно передать колбек, который вызовет пул, когда задача будет выполнена.
            Ответить
            • Я так, кстати, делал... Но мне кажется, что лучше посмотреть в сторону Future.

              ПС надо бы сделать rebase для коментов )
              Ответить
          • > {@see ExecutorService}
            Да, только тут процессы, а не потоки. В жабе такого из коробки нет. Вот за что люблю python - в нём есть решения практически на все случаи жизни:
            http://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.pool
            Ответить
            • Я имел ввиду общую архитектуру, а не конкретную реализацию.
              Тем более ExecutorService - интерфейс
              Ответить
              • Хотел бы я посмотреть на того человека, который осилит реализовать жабий ExecutorService, кроссплатформенно раздающий Future по Callable, используя пулл процессов.
                Ответить
                • Сделать-то можно, но зачем?
                  Ответить
                  • >кроссплатформенно
                    С этим будут основные проблемы.
                    >раздающий Future по Callable, используя пулл процессов.
                    Но действительно - зачем? Или я чего-то не пойму.
                    Потоки они ведь легковесные и проблем с ними меньше.
                    Ответить
                    • > Потоки они ведь легковесные
                      Ну в винде да, разница на порядки. А вот в линухе емнип форк медленнее птред_крейта всего раза в 2.

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

                      P.S. А с future на процессах я так понимаю основная проблема будет в маршаллинге. Все что не Serializable, и не сможет прокачаться через пайп, в такую фьючу не пропихнешь, и результатом не вытащишь.

                      P.P.S. Шаренная память в этом случае не особо вариант, т.к. если обмен идет через маленькое окно - те же самые траблы что у пайпа, только код сложнее. А если всю расшарить - ну тогда проще и юзать потоки.
                      Ответить
                      • > основная проблема будет в маршаллинге
                        Именно. Необходимость сериализации данных между жабо-машинами ломает семантику ExecutorService, делая его реализацию на процессах просто невозможной. Ну и кросс-платформенное решение возможно, скорее всего, только при допиливании jre.
                        Ответить
                        • Ну почему. Если сказать, что на входе и выходе у задачек только сериализуемые объекты, то можно сделать все кошерно и кроссплатформненько, на чистой жабе. Или я в чем-то туплю?
                          Ответить
                      • >а еще надо запускать недоверенный код
                        SecurityManager
                        > и возможности запустить каждую задачу
                        > ограничив ей видимость
                        Для ряда задач можно использовать ClassLoaderы. Правда там свои подводные камни.
                        Ответить
                        • > SecurityManager
                          Не жабой единой... все-таки тема этого раздела С++.

                          P.S. А SecurityManager спасёт от OutOfMemoryError?

                          P.P.S. В ведре есть нативные приложения и JNI, поэтому, видимо, они и не стали заморачиваться с менеджером безопасности, и сделали по юзеру на каждое приложение.
                          Ответить
                          • >все-таки тема этого раздела С++
                            По меркам этого сайта мы не сильно ушли от темы :)
                            Ответить
                          • >SecurityManager спасёт от OutOfMemoryError?
                            Хороший вопрос. Мне на ум приходят только разные извраты. Не буду озвучивать.
                            Очень хороший. Много думал. Наверное никак. Жаба сама по себе песочница, и они считают что риска для остальной системы нет.
                            >нативные приложения и JNI
                            Ну вот как раз для них оно и придумано. Все внешние обращения (в т.ч. LoadLibrary) там мониторятся.
                            А вот память кагбе внутренний ресурс JVM, который ограничен при запуске.
                            Ответить
                            • > Наверное никак.
                              Ну вот поэтому для недоверенного кода и нужны процессы ;(

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

                              > Все внешние обращения (в т.ч. LoadLibrary) там мониторятся.
                              И приходится либо убирать возможность запуска нативных программ и либ (т.к. с ними уже никакой секурити манагер не справится), как поступили в браузерах, или забить на менеджера безопасности, сделать по юзеру на прогу и запретить им лазить друг к другу, как поступили на ведре.
                              Ответить
                              • В жабе вроде как нельзя, да. И нет пула для процессов.
                                Хорошо, а как в других языках (тот же питон с пулом и мультипроцессами) решить такую задачу - поставить квоту на цп, память процесса?
                                Ответить
                                • OS specific
                                  в винде есть Job Objects
                                  Ответить
                                • > И нет пула для процессов.
                                  Ну его можно сделать при желании.

                                  > в винде есть Job Objects
                                  А в линухе есть setrlimit.

                                  Так что хоть и os specific, но, по идее, можно на всех приличных осях...

                                  P.S. Если же запускать жабомашины - то можно в параметрах и указать лимит памяти, получится более-менее кроссплатформенное решение. С CPU - хз.
                                  Ответить
                                  • Вопрос скорее к Роману.
                                    > В жабе такого из коробки нет.
                                    > осилит реализовать жабий ExecutorService, кроссплатформенно используя пулл процессов.

                                    А в питоне есть такое кроссплатформенное решение "из коробки", которым можно ставить квоты по памяти на запускаемые процессы?
                                    Других разумных применений такому расточительству как пул потоков я пока не вижу.
                                    Ответить
                                  • > можно на всех приличных осях...
                                    ну этот виндовый Job Objects умеет в ограничение CPU только чуть ли не с Шиндошс-8 только (http://bit.ly/VOXWJJ), а в XP - только ограничить user-time + sheduling class
                                    собсно, setrlimit, судя по ману недалеко ушел от XP (RLIMIT_CPU + RLIMIT_NICE)
                                    Ответить
                                    • Хм, под ограничением CPU имеется в виду максимальный процент процессорного времени, отжираемый данным процессом?

                                      А так ли нужно это ограничение? Имхо достаточно искусственная вещь. При правильно розданных scheduling class'ах и так все будет нормально делиться, к тому же проц не будет зазря простаивать, если нет более приоритетной работы. Х.з. конечно, может быть я и ошибаюсь.
                                      Ответить
    • fork - это же не кроссплатформенно.

      А так нормальное решение, почему бы нет?
      Ответить
      • Кроссплатформенность не нужна, программа будет запущена под *nix и забыта.
        Ответить
        • Люблю проги в стиле write & forget, не нужно постоянно поддерживать...

          Главное не забывать задокументировать где она стоит, что делает, и, по возможности, как. А то потом случаются не очень приятные истории, когда весь отдел сидит и пытается понять, нужен ли кому-то демон, конфиги и исходники которого последний раз правились лет 7-8 назад...
          Ответить
    • > Вот такой костыль.

      для *нихов это не костыль, это нормально. у меня где-то валяются сырцы сервера с пре-форком - там форк в самом условии while() вызывается. выглядит феерично, но суть та же: надо просто процессов дочерних процессов нафоркать, которые тупо в accept()е ждут запросов от клиентов.

      > Лучше пулы юзать.

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

      (*) относительно потоков.
      Ответить
      • Скорее наоборот. Очень часто время, которое тратится на fork больше времени выполнения задачи. Лучше уже vfork тогда.

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

          фишка fork() как раз в том что его можно сделать легковесным. что почти все *нихи и делают. copy-on-write уж как лет десять везде реализован.

          vfork() - я как бы и не уверен что он еще жив. сама идея vfork() была убогой. на паре *нихов и линухе это просто алиас для fork() потому что создание копии vma уже в самом fork() сделано ленивым.

          ЗЫ vfork() в POSIX.6 - deprecated, POSIX.7 - non-existent.

          ЗЗЫ линух реализовал vfork()! правда то как он работает это совсем не BSD vfork() - http://www.kernel.org/doc/man-pages/online/pages/man2/vfork.2.html
          Ответить
          • Нет, я работаю под JVM ;D

            А вообще я в качестве лабы по ОС писал простенький веб-сервер, так вот, смена fork на vfork дала прирост производительности... О подробностях ничего не знал.
            Ответить
            • под какой осью?

              только что еще раз проверил. AIX и Линух - нету vfrok. (непортабельный линуховый vfork() не в счет.)

              HP-UX & Solaris - есть, но (по традиции) зарезервирован только для последующего вызова exec() (но для HP-UX я точно видел официальное указание что разницы с fork()ом нету). та же песня в FreeBSD, но с нытьем в манпэйдже которое намекает что гондурасы все еще lazy vma copy не сделали: "The vfork() system call can be used to create new processes without fully copying the address space of the old process, which is horrendously inefficient in a paged environment."
              Ответить
              • Да линукс... Странно...
                Ответить
                • странно. но не удивительно. в линухе все меняется каждые 2-3 года. иногда к лучшему.
                  Ответить
              • обсуждали же https://govnokod.xyz/_27518/#comment-634283
                Ответить
                • лол

                  ты по меркам этого треда кинул ссылку на далёкое светлое будущее
                  Ответить
        • > Да и вообще мне не особо понятно зачем плодить процессы, если есть специально придуманные потоки?
          Изоляция. Можно запустить часть детей под нужным юзером, родитель не крашится при поломке одного ребенка и т.п. Часто ради этого приходится жертвовать производительностью. Хотя в линуксе время форка и время создания потока не сильно отличаются, в отличие от винды, где разница на порядки.

          > Лучше уже vfork тогда
          vfork, если мне не изменяет память, можно юзать только в сочетании с exec, по отдельности он бесполезен:
          vfork() differs from fork(2) in that the parent is suspended until the child terminates (either normally, by calling _exit(2), or abnormally, after delivery of a fatal signal), or it makes a call to execve(2). Until that point, the child shares all memory with its parent, including the stack.
          Ответить
          • Ну на счет изоляции, я сам выше уже писал. Другой вопрос, что других юзкейсов я не знаю.
            vfork уже выше обсудили, так что Вы опоздали )
            Ответить
            • Ну про то что линушный vfork без exec бесполезен выше не обсуждалось.
              Ответить
              • Ну он же работал... И причем быстрее... Либо и бенчмарк, и профайлер врали...
                Ответить
                • Сколько параллельных запросов делалось к серверу во время бенчмарка? Если все последовательно - то понятно почему быстрее. Тупо потому что все из-за vfork'а все в одном процессе разруливалось, и ничего никуда не форкалось.
                  Ответить
              • http://ideone.com/wyTfpX чтобы развеять все сомнения... Работают они одинаково )
                Ответить
                • http://ideone.com/xRD4q3
                  Мда. И правда не блочит. Не зря там ниже написано: In particular, the programmer cannot rely on the parent remaining blocked until the child either terminates or calls execve(2), and cannot rely on any specific behavior with respect to shared memory..

                  Т.е. походу vfork можно юзать перед exec, чтобы получить небольшое ускорение на старых системах. А на новых он тупо ничем не отличается от fork.

                  UPD: сейчас переделаю тест, clock же измеряет не реальное время, а процессорное.
                  Ответить
                • Вот, чтобы окончательно развеять миф о вфорке: http://ideone.com/gB93Xm. (На ideone форк походу бажный, не видно его выхлопа).

                  На моей машине выдает вот такое:
                  Linux bormand-home 3.2.0-35-generic #55-Ubuntu SMP Wed Dec 5 17:45:18 UTC 2012 i686 i686 i386 GNU/Linux
                  fork parent return: 0 (pid=5674)
                  fork child started
                  vfork child started
                  vfork child ended: 2
                  vfork parent return: 2 (pid=5675)
                  fork child ended: 2
                  done
                  Т.е. в vfork блокировка есть, в fork ее нет.

                  Ваш ход.
                  Ответить
    • В наше время благодаря интернету дети узнаяют о форканьи уже с ранних лет...
      Ответить
      • интернет не обязателен. первый раз я креативно (но совсем неоригинально) воспользовался вилкой уже в возрасте 3х лет. интернетов тогда еще и в помине не было. я воткнул ее в розетку. и оставил без света весь подъезд.
        Ответить
        • >и оставил без света весь подъезд.
          Хорошо, что не свои глаза.
          Ответить
    • какое извращение этот ваш fork
      Ответить
    • УРА! Заработало!
      p.s. интересно, страйко постарался или оно само восстанавливается?
      Ответить

    Добавить комментарий