Перейти к содержимому

Для чего могут использоваться динамические среды разработки

  • автор:

Review- или динамические окружения. Теория и практика в Kubernetes

Статья посвящена так называемым review-окружениям, реализуемым в рамках кластеров Kubernetes. Ранее эта тема затрагивалась, например, в нашем докладе «Лучшие практики CI/CD с Kubernetes и GitLab», но не была там основной темой, поэтому раскрывалась не во всех деталях. Попробую восполнить этот пробел, рассказав, для чего нужны и/или обычно используют review-окружения, как сделать pipeline c review-окружением в GitLab CI/CD, какие могут быть потенциальные проблемы и способы их решения.

Review-окружения. Что это и зачем нужны?

Общая теория

Начну с примечания, что есть разные переводы самого понятия, но здесь и далее в статье используется термин «окружение» (от англ. environment). Под ним понимается некоторая «среда» или «контур», в соответствии с которым настроено и непосредственно в котором работает приложение. Обычно для каждого окружения используются свои переменные (они так и называются — переменные окружения), которые определяют для запущенного приложения, например, параметры подключения к БД и т.п.

Review-окружения, они же динамические или preview-окружения, как правило, применяют для программного и визуального тестирования нового функционала или его демонстрации. Это очень удобно для команд разработки и тестирования. Разрабатывая в своей Git-ветке какую-то фичу, разработчики могут оперативно деплоить код в кластер Kubernetes. При этом у них есть отдельное окружение, которое используется для проверки корректной работы кода или для других целей. А команда тестирования может оперативно и удобно тестировать новый функционал…

Приведем простой и довольно типичный пример: небольшая команда разработки, окружения stage и production.

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

В зависимости от того, как устроен workflow конкретной команды/компании, у каждого разработчика может быть одно или несколько «своих» review-окружений или окружения, создаваемые из конкретных веток. Если не вдаваться в детали (о них ниже), то типичный рабочий процесс получится примерно таким:

  • Разработчик вносит правки в своей ветке локально и делает push в репозиторий.
  • Далее — либо автоматически, либо вручную — код готового приложения деплоится в кластер Kubernetes.
  • При этом в Git-репозитории есть заранее заготовленный DevOps-командой Helm-чарт. Он выкатывает в кластер все необходимое ПО, от которого зависит работа приложения: СУБД MySQL, Redis, Ingress, Secret’ы с доступами к каким-то внешним сервисам (S3-хранилище и т.п.), — а также само приложение или его компоненты.
  • Также в review-окружениях запускаются миграции для БД — различные job’ы, чтобы обеспечить работу с актуальной копией базы и приблизить review-окружение к «боевому».

Получается, что по своей сути review-окружение — это более удобный и гибкий аналог того, как разработчик запускал бы код локально, только при этом не нужно делать множество дополнительных манипуляций:

  • устанавливать кучу зависимого ПО на свою рабочую машину или поднимать Docker-контейнеры со всем ПО, от которого зависит приложение;
  • городить какие-то скрипты для запуска;
  • обеспечивать наличие всех переменных окружения для приложения…

Всё это делается силами DevOps-инженера, который реализует сборку и деплой приложения (в условном Helm-чарте), а это, как правило, довольно удобно и универсально.

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

Подводя итог: как правило, review-окружение — это «маленькое», условно однопользовательское окружение, урезанное по ресурсам. Оно быстро разворачивается в кластер, недолго живет и удаляется за ненадобностью. Для таких окружений очень важна воспроизводимость. Это очень важный момент, т.к. мы должны быть уверены в том, что код и образ, который проходил все этапы (отладку, тестирование, оптимизацию…), будет задеплоен в production в точно таком виде, а результат работы приложения будет предсказуем.

Для достижения максимальной воспроизводимости мы у себя используем утилиту werf. Если приложение и его зависимости выкатываются в кластер со всеми настройками, переменными окружения и т.п., а Docker-образы собираются и тегируются с использованием content-based-тегов (актуально при использовании werf 1.1 и выше), то нужная воспроизводимость достигается.

NB. К слову, чтобы быть уверенными в результате работы приложения еще больше, в werf версии 1.2 гарантии воспроизводимости расширены благодаря фиче под названием гитерминизм (подробнее см. в документации).

«Неполные» review-окружения

Зачастую используется неполное review-окружение. Под ним подразумевается такое окружение, которое урезано не только по ресурсам (CPU, память, диск) относительно других, но и по функциональности (т.е. инфраструктурным компонентам).

Пример из жизни. Разработчик дорабатывает ту часть приложения, которая взаимодействует только с MySQL, и ему совершенно не важна часть, задействующая Redis и другую инфраструктуру. Тогда достаточно задеплоить в кластер MySQL и само приложение, а также (скорее всего) Ingress для внешнего взаимодействия по HTTP/HTTPS.

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

Поскольку review-окружения обычно single user (к задеплоенному приложению обращается один или несколько разработчиков), то на них не требуется выделять большое количество ресурсов: реально необходимое может в десятки раз(!) отличаться от production и даже stage-окружений, а разработчик счастлив.

Практика. Реализуем review-окружения в GitLab

Разберем конкретный пример workflow и работу с review-окружениями в системе GitLab, которая у нас принята за стандарт, но не исключает использование других CI/CD систем.

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

Создание репозитория и pipeline с review-окружением

Pipeline в GitLab описывается в файле .gitlab-ci.yml . Создаем репозиторий в нужном проекте в GitLab и описываем наш pipeline в .gitlab-ci.yml в корне этого репозитория. Вот содержимое файла с подробными комментариями:

# Указываем "глобальные" для пайплайна переменные. # В данном случае это только версия утилиты werf, которую используем для сборки необходимых образов, деплоя всего окружения и его удаления из кластера variables: WERF_VERSION: "1.2 beta" # Описываем стадии, на которые логически разделен pipeline: stages: # стадия сборки образов - build # деплой/запуск и остановка review-окружения - review before_script: # проверяем наличие утилиты multiwerf и, если успешно, то просим её «подгрузить» нужную версию werf - type multiwerf && source <(multiwerf use $) # генерируем и добавляем переменные окружения, которые необходимы для работы werf с GitLab - type werf && source <(werf ci-env gitlab) Build: stage: build # в секции script — список команд, выполняемых на gitlab-runner при запуске стадии script: # запускаем сборку образов, описанных в werf.yaml - werf build # исключаем запуск стадии из планировщиков except: - schedules # указываем тег gitlab-runner’а(ов), на которых может быть запущена стадия tags: - werf # описываем шаблон деплоя review-окружения Deploy Review: stage: review script: # вызываем werf converge для деплоя чарта в кластер. Опция --skip-build, пропускает сборку, т.к. стадия сборки образов у нас вынесена отдельно - werf converge --skip-build # указываем что этот шаг, зависит от шага Build, т.е. если на этапе сборки образов что-то пошло не так, шаг с деплоем будет недоступен needs: - Build tags: - werf # задаем доп. переменные окружения environment: name: review/$/$ # указываем, какой шаг вызывается для остановки/удаления review-окружения on_stop: Stop Review # указываем период, через который GitLab автоматически удалит review-окружение auto_stop_in: 1 day # шаг может быть вызван только из какой-то ветки only: - branches # … и запущен вручную when: manual # исключаем запуск планировщиком except: - schedules # описываем остановку/удаление review-окружения: Stop Review: # стадия, к которой относится шаг stage: review # вызываем werf dismiss с указанием окружения, namespace и именем helm-release для удаления, # а также с опцией для удаления не только ресурсов Helm release, но и namespace, куда он был задеплоен script: - werf dismiss --with-namespace when: manual environment: name: review/$/$ action: stop only: - branches tags: - werf except: - schedules

Готово! Так мы создали pipeline, в котором запускается процесс сборки образов, после чего, если сборка прошла успешно, будут доступны deploy в кластер и возможность остановки/удаления всего задеплоенного Helm-релиза.

А вот как описанный выше pipeline будет представлен в GitLab визуально:

Потенциальные проблемы и способы решения

За время использования review-окружений мы столкнулись и с рядом сложностей. Вот главные из них.

1. Чрезмерное потребление ресурсов

Поскольку за сутки разработчики могут поработать с большим количеством веток, сделать множество merge requests и выкатить много review-окружений, становится обыденной проблема чрезмерного использования ресурсов.

Текущая версия пайплайна, выше реализованного в GitLab, предусматривает удаление окружений, запущенных более одного дня, и ручное удаление/остановку.

NB. Стоит отметить, что из-за ограниченности ресурсов GitLab фоновый worker, который останавливает окружения автоматически, запускается только раз в час. Это означает, что ваше review-окружение может не остановиться сразу по истечении времени, которое указано в auto_stop_in . Остановка сработает только когда «worker-часовой» обнаружит, что у вашего окружения истек срок действия.

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

Итак, пример .gitlab-ci.yml с лимитом на максимальное число review-окружений:

variables: WERF_VERSION: "1.2 beta" Deploy to Review: before_script: - type multiwerf && source <(multiwerf use $) - type werf && source <(werf ci-env gitlab) # максимальное кол-во review окружений - export MAX_REVIEW=$# берем имя проекта из werf.yaml - "export PROJNAME=$(cat werf.yaml | grep project: | awk '')" # проверяем сколько review-окружений задеплоено - export DEPLOYED_REVIEW=$(helm ls -A -a | grep -F "$PROJNAME-review" | sort | uniq | grep -cv "$PROJNAME-$CI_ENVIRONMENT_SLUG") || export DEPLOYED_REVIEW=”0” - if (( "$DEPLOYED_REVIEW" >= "$MAX_REVIEW" )) && [[ "$CI_ENVIRONMENT_SLUG" =~ "review" ]]; then ( echo "Максимальное кол-во ревью окружений $MAX_REVIEW достигнуто, необходимо остановить неиспользуемые" && exit 1; ); fi script: - werf converge --skip-build - echo "DYNAMIC_ENVIRONMENT_URL=http://$-$.kube.some.domain" >> deploy.env artifacts: reports: dotenv: deploy.env needs: - Build tags: - werf stage: review allow_failure: false environment: name: review/$/$ url: $DYNAMIC_ENVIRONMENT_URL on_stop: Stop Review auto_stop_in: 1 day only: - branches except: - develop - master - schedules when: manual Stop Review: stage: review script: - werf dismiss --with-namespace environment: name: review/$/$ when: manual except: - develop - master - schedules tags: - werf stages: - build - review

Такой подход позволит заранее планировать необходимое количество серверных мощностей, делает прогнозируемой оплату за инфраструктуру и не позволяет «захламить» кластер ненужными релизами.

В дополнение к нему неплохо бы иметь выделенные узлы под различные не-production-окружения. Во-первых, это позволит не влиять на production при сбоях/чрезмерном потреблении ресурсов, а во-вторых, гарантирует на уровне железа, что тестовые/review/stage-окружения не будут потреблять слишком много ресурсов, что дает еще большую прогнозируемость затрат на инфраструктуру.

2. Лимиты Let’s Encrypt

Второй и не самой очевидной проблемой может стать использование LE-сертификатов для доменов review-окружений. В зависимости от рабочих процессов review-окружений может оказаться огромное количество. В каждом из них есть домены/поддомены, для которых всякий раз при выкате заказываются SSL-сертификаты… И вот в какой-то момент можно получить неприятную новость о том, что превышен тот или иной лимит.

Самый простой вариант решения — это wildcard-сертификат для всех поддоменов. Он заказывается отдельно от review-окружений и каким-то автоматизированным образом* копируется в namespace созданного review-окружения.

* В нашей Kubernetes-платформе (Deckhouse) для этого есть secret-copier. Его успех легко повторить в нужном вам виде с помощью shell-operator.

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

3. Pull-лимиты Docker Hub

Относительно недавно стала возникать неприятная порой ситуация, когда при большом количестве деплоев в течение дня можно упереться в лимит на pull образов с Docker Hub и завалить деплой. Причина всем известна:

The rate limits of 100 container image requests per six hours for anonymous usage, and 200 container image requests per six hours for free Docker accounts are now in effect. Image requests exceeding these limits will be denied until the six hour window elapses.

Чтобы не столкнуться с этой проблемой (или минимизировать её), достаточно:

  1. Следить за тем, чтобы в описании контейнера без необходимости не было указано imagePullPolicy: Always (впрочем, за этим параметром стоит следить и по ряду других причин).
  2. Кэшировать требующиеся образы в свой registry и использовать для контейнеров уже их.
4. Multipipeline

Multipipeline — это про построение пайплайна с использованием зависимостей или связей с другим репозиториями. Когда это может быть нужно? Вот простой пример: вы выкатываете review-окружение с приложением, но приложению, помимо прочего, нужны еще и инфраструктурные сервисы вроде Redis и MySQL (их деплой описан в отдельных репозиториях) с возможностью деплоить в разные namespace и окружения.

Что будет, если не использовать multipipeline? После выката review-окружения или перед ним потребуется вручную «пойти» в репозитории инфраструктурных компонентов и задеплоить их в нужный namespace и окружение. Кроме того, зависимость приложения от других компонентов не всегда очевидна, а это сильно усложняет процесс деплоя приложения.

С multipipeline можно вызывать (trigger’ить) job’ы из других репозиториев, т.е. можно написать пайплайн, который при выкате приложения приведет к деплою компонентов, от которых зависит это приложение. Не нужно «бегать» по соседним репозиториям и вспоминать зависимости приложения — деплой происходит одним нажатием или автоматически.

Multipipeline в GitLab стал бесплатным начиная с версии 12.8, а документация по нему доступна здесь. Также есть полезное видео, где рассказывают о базовых возможностях и вариантах применения. И не забудьте обратить внимание на существующие ограничения.

Заключение

Подводя итог, можно с уверенностью сказать, что review-окружения в современной, динамичной разработке являются практически неотъемлемой частью. Именно поэтому большинство наших клиентов этот тип окружений использует с самого начала. А те немногие проекты, где review-окружений нет, либо действительно не испытывают в них надобности, либо еще не осознали всю пользу такого подхода.

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

P.S.

Тот факт, что мы рассмотрели работу с review-окружениями конкретно в GitLab, вовсе не говорит о невозможности их реализации и в других CI-системах.

ДОБАВЛЕНО (8 ноября): Пример альтернативного взгляда на реализацию review-окружений — недавняя статья коллег из Typeable.

P.P.S.

Читайте также в нашем блоге:

  • «GitLab CI для непрерывной интеграции и доставки в production. Часть 1: наш пайплайн»;
  • «Динамическая сборка и деплой Docker-образов с werf на примере сайта версионированной документации»;
  • «Сборка и деплой однотипных микросервисов с werf и GitLab CI».

Учебники. Программирование для начинающих.

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

Программирование — в обычном понимании, это процесс создания компьютерных программ.
В узком смысле (так называемое кодирование) под программированием понимается написание инструкций — программ — на конкретном языке программирования (часто по уже имеющемуся алгоритму — плану, методу решения поставленной задачи). Соответственно, люди, которые этим занимаются, называются программистами (на профессиональном жаргоне — кодерами), а те, кто разрабатывает алгоритмы — алгоритмистами, специалистами предметной области, математиками.
В более широком смысле под программированием понимают весь спектр деятельности, связанный с созданием и поддержанием в рабочем состоянии программ — программного обеспечения ЭВМ. Более точен современный термин — «программная инженерия» (также иначе «инженерия ПО»). Сюда входят анализ и постановка задачи, проектирование программы, построение алгоритмов, разработка структур данных, написание текстов программ, отладка и тестирование программы (испытания программы), документирование, настройка (конфигурирование), доработка и сопровождение.

Delphi для профессионалов

Глава 28. Динамические библиотеки

Динамические библиотеки

Динамические библиотеки (DLL, Dynamic Link Library) играют важную роль в функционировании ОС Windows и прикладных программ. Они представляют собой файлы с откомпилированным исполняемым кодом, который используется приложениями и другими DLL. Реализация многих функций ОС вынесена в динамические библиотеки, которые используются по мере необходимости, обеспечивая тем самым экономию адресного пространства. DLL загружается в память только тогда, когда к ней обращается какой-либо процесс.

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

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

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

Разновидностью динамических библиотек являются пакеты Delphi, предназначенные для хранения кода компонентов для среды разработки и приложений.

Применение динамических библиотек позволяет добиться ряда преимуществ:

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

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

В этой главе рассматриваются следующие вопросы:

  • структура файла DLL;
  • инициализация DLL;
  • явная и неявная загрузка;
  • вызовы функций из динамической библиотеки;
  • ресурсы в динамических библиотеках.

Для чего могут использоваться динамические среды разработки

Изготовление исполнимых программ из исходных текстов выполняется с помощью компиляторов, переводящих исходный текст программы в эквивалентную ей результирующую программу на языке машинных команд. Основными языками программирования на высокопроизводительных вычислительных системах являются С/C++ и Фортран . Язык С создавался как язык для написания системных приложений, однако в последнее время широко применяется и для написания вычислительных программ. Язык программирования Фортран изначально разрабатывался для написания вычислительных программ. Для него разработано множество библиотек прикладных подпрограмм, в которых реализованы различные вычислительные алгоритмы. Например, библиотека LAPACK содержит широчайший набор подпрограмм для решения различных задач линейной алгебры.

Синтаксис команды компиляции имеет вид:

компилятор [опции] файлы [библиотеки]

  • Здесь компилятор - команда вызова компилятора;
  • основные опции:
  • -o - создать выходной файл с заданным именем (без опции создается a.out);
  • -c - не изготавливать исполнимый модуль (при компиляции подпрограмм);
  • -O -O1,-O2,-O3 - задание уровня оптимизации;
  • -g - выполнить компиляцию в отладочном режиме;
  • файлы - компилируемые файлы;;
  • библиотеки - подключаемые библиотеки.

В квадратных скобках указываются необязательные компоненты команды.

На UNIX-подобных системах имеется множество компиляторов. Большая часть из них является коммерческими продуктами. Для систем Linux пакет GCC является неотъемлемой частью дистрибутивов, поскольку является базовым компилятором сборки ядра системы и всех ее утилит.

Пакет компиляторов GCC

В него входят компиляторы:

  • gcc - компилятор языка С;
  • g++ - компилятор языка С++;
  • gfortran - компилятор языка Фортран95.

Компиляторы GCC оптимизирующие, поддерживающие три уровня оптимизации (опции -O1, -O2, -O3). На разных программах более эффективной может оказаться та или другая опция. В большинстве случаев наиболее приемлемой бывает опция -O2, при этом ускорение программы может достигать 2-3 раз. Типичные команды компиляции:

  • gcc -O2 -o prog prog.c - для языка С;
  • gfortran -O2 -o prog prog.f - для языка Фортран.

Помимо этого, на Linux кластерах, являющихся сегодня основным видом высокопроизводительных вычислительных систем, широко используется пакет компиляторов Intel Compiler, наилучшим образом оптимизированный под платформу x86-64, являющуюся основной при построении вычислительных кластеров. Это коммерческй продукты и он приобретен Вычислительным центром СПбГУ.

Пакет компиляторов Intel
  • icc - компилятор языка С;
  • icpc - компилятор языка С++;
  • ifort - компилятор языка f77, f90, f95.

Компиляторы также поддерживают три уровня оптимизации (опции -O1, -O2, -O3, задание опции -O соответствует уровню -O2). Сочетание опций -fast -On, задает режим максимального ускорения программы на соответствующем уровне оптимизации. Для отлаженных программ включение оптимизации обязательно. В большинстве случаев ускорение работы программы может достигать 2-3 раз.

  • icc -O2 -o prog prog.c - для языка С;
  • ifort -O2 -o prog prog.f - для языка Фортран.

Рассмотрим подробнее работу с компилятором gcc.

Создадим файл с именем ex1.c с помощью команды touch. Откроем его в текстовом редакторе и наберем текст программы на языке С.

Программа ex1.c #include int main(int argc, char* argv[])

Далее следует скомпилировать программу, т.е. перевести в исполнимый код. Для этого выполним следующую команду.

Если программа написана без ошибок, то никакой выдачи информации на терминал не будет, а в рабочем каталоге появится файл с именем a.out. Это исполнимый файл, полученный в результате компиляции программы. Его можно запустить на исполнение(поэтому файлы и называются исполнимыми), набрав в командной строке:

На терминал будет напечатана строка "Hello word".

Для того чтобы поменять имя создаваемого файла c a.out на любое другое необходимо использовать опцию -o:

gcc -o ex1 ex1.c

В результате будет создан исполнимый файл с именем ex1.

Приведем несколько важных опций компилятора gcc (они справедливы и для icc)

  • -o файл - Поместить вывод в файл 'файл'. Эта опция применяется вне зависимости от вида порождаемого файла, является ли это выполнимый файл, объектный файл, ассемблерный файл или препроцессированный C код. Если '-o' не указано, по умолчанию выполнимый файл помещается в 'a.out', объектный файл для 'исходный.суффикс' - в 'исходный.o', его ассемблерный код в 'исходный.s' и все препроцессированные C файлы - в стандартный вывод.
  • -c - Компилировать или ассемблировать исходные файлы, но не линковать. Стадия ликовки просто не выполняется. Конечный вывод происходит в форме объектного файла для каждого исходного файла.
  • -g - Порождает отладочную информацию.
  • -O,-O1,-O2,-O3 - Задание уровня оптимизации оптимизации
  • -Iдиректория - Добавляет каталог 'директория' в начало списка каталогов, используемых для поиска заголовочных файлов. Ее можно использовать для подмены системных заголовочных файлов, подставляя ваши собственные версии, поскольку эти директории просматриваются до директорий системных заголовочных файлов. Если используется более чем одна опция '-I', директории просматриваются в порядке слева на право; стандартные системные директории просматриваются последними.
  • -Lдиректория - Добавляет каталог 'директория' в начало списка каталогов, используемых для поиска библиотек
  • -lбиблиотека - Подключает библиотеку с именем lib'библиотека'.so

Рассмотрим назначение опций более подробно на примерах.

В программах часто используются уже написанные ранее функции. Например, в приведенной выше программе, применялась системная функция вывода информации в стандартный поток printf. Для того чтобы транслятор на этапе создания программы, мог правильно обработать внешнюю функцию необходимо ее предварительно описать, либо внутри программы, либо в специальном заголовочном файле. Такие файлы еще называют include файлами, в языке С они подключаются с помощью специальной директивы #include. На первом этапе трансляции программы, запускается так называемый препроцессор, он находит файл с именем stdio.h, и вставляет его содержимое внутрь программы. Пути поиска задаются с помощью опции

-Iдиректория,
где директория - путь к каталогу, в котором расположен данный файл.

Если используется стандартный заголовочный файл, то опцию -I для его поиска в командной строке компиляции программы указывать необязательно. Существует специальный каталог, где располагаются стандартные заголовочные файлы. Препроцессор автоматически просматривает его при поиске заголовочных файлов. Все сказанное в полной мере относится и к компилятору с языка Фортран. Отличие состоит в синтаксисе подключения include файла:

include 'файл.h'

Если в команде компиляции не указана опция -c, то компилятор автоматически выполняет операцию компоновки, т.е. изготовление исполнимой программы. В примере для вывода строки "Hello word" применялась стандартная функция printf, следовательно, код этой функции должен быть вставлен в программу. Операцию объедения кода программы и кода внешних функций выполняет компоновщик. Компоновщик (или линковщик - linker) - программа, которая производит компоновку, принимает на вход один или несколько объектных модулей и собирает из них исполняемый модуль. Объектный модуль (или объектный файл - object file) - это файл с промежуточным представлением отдельного модуля программы, полученный в результате обработки исходного кода компилятором. Объектный файл содержит в себе особым образом подготовленный код (часто называемый бинарным), который может быть объединён с другими объектными файлами при помощи редактора связей (линковщика) для получения готового исполняемого модуля либо библиотеки.

В рассмотренном примере используется функция printf, находящаяся в стандартной библиотеке с именем libc. Для программ на языке С эта библиотека автоматически подключается к любой программе, поэтому не потребовалось подключать ее с помощью опций. В тех случаях, когда в программе используются функции входящие в другие библиотеки, то эти библиотеки необходимо указывать компоновщику, иначе компоновщик не сможет собрать исполнимый файл. Рассмотрим следующий пример.

Программа ex2.c #include #include int main(int argc, char *argv[])

Эта программа вычисляет результат возведения в степень 0.1 числа 2 и присваивает результат переменной res и затем выводит ее значение на стандартный поток вывода. Возведение в степень осуществляет функция pow. Заголовочный файл, в котором описан заголовок для этой функции, подключается директивой #include , являющимся стандартным заголовочным файлом для библиотеки математических подпрограмм.

Попробуем скомпилировать программу командой:

gcc -o ex2 ex2.c

В результате получим следующие сообщение об ошибке:

/tmp/ccgSk9AB.o(.text+0x49): In function `main':
ex2.c: undefined reference to `pow'
collect2: ld returned 1 exit status

Это сообщение говорит, что в функции main, файла ex2.c вызывается функция pow, для которой не найден машинный код на этапе сборки программы. Для того чтобы программа скомпоновалась, необходимо указать компилятору в какой библиотеке следует искать объектный код функции pow. Правильная строка компиляции будет выглядеть следующим образом.

gcc -o ex2 ex2.c -lm

В результате будет создана программа с именем ex2, которая при запуске напечатает:

Подключение библиотеки было выполнено с помощью опции -lm. Файл этой библиотеки находится в каталоге /usr/lib. Полное его название libm, имена файлов библиотек подпрограмм всегда начинаются с префикса lib, за которым идет название библиотеки. При подключении библиотеки к программе в строке компилятора префикс lib заменяется на -l. Таким образом, подключение библиотеки libm осуществляется опцией -lm. Поскольку библиотека стандартная, находится в специальном каталоге, то нет необходимости указывать путь поиска файла библиотеки математических подпрограмм с помощью опции -L. Компилятор сам найдет его в директории /usr/lib. Работа с библиотеками имеет ряд аспектов, которые нуждаются в более подробном рассмотрении.

В рассмотренном ранее примере было упомянуто, что стандартная математическая библиотека находится в системном каталоге /usr/lib. Однако если перейти в каталог /usr/lib, и попробовать найти там файл с именем libm, то такого файла там нет. Зато есть два файла с именами libm.a и libm.so. Почему два и с разными расширениями? Потому что большинство UNIX-подобных систем поддерживают два типа компоновки - статическую и динамическую.

Динамические библиотеки, называемые также библиотеками общего пользования или разделяемыми библиотеками (shared library), загружаются на этапе выполнения программы. Код вызываемых функций не встраивается внутрь исполняемой программы, а вызывается по мере необходимости при запуске программы на исполнение. Такой подход позволяет создавать программы значительно меньшего объема. Динамические библиотеки хранятся обычно в определенном месте и имеют стандартное расширение. В ОС Windows файлы библиотек общего пользования имеют расширение .dll, а в UNIX-подобных системах .so. Если на этапе загрузки программы система не смогла найти необходимый код, то программа не запустится. Будет выдано сообщение об ошибке:

error while loading shared libraries: libxxx.so: cannot open shared object file: No such file or directory

Статические библиотеки в виде пакетов объектных файлов, присоединяются (линкуются) к исполнимой программе на этапе компиляции (в Windows такие файлы имеют расширение .lib, а в UNIX-подобных .a). В результате этого программа включает в себя все необходимы функции, что делает её автономной, хорошо переносимой, но увеличивает размер.

Статическая библиотека создается специальной командой:

ar rc libимя.a список_объектных_файлов

Объектные файлы создаются компиляцией функций с опцией -c. Рекомендуется каждую функцию (или подпрограмму в Фортране) оформлять в отдельном файле.

Динамическая библиотека создаются компилятором:

gcc -shared -o libимя.so список_объектных_файлов

Для создания объектных файлов компиляция выполняется с опциями -fPIC -c. Опция -fPIC (PIC - Position Independent Code) означает создание позиционно-независимого кода.

Все библиотеки обычно хранятся в каталоге lib. Если с одним и тем же именем имеется две библиотеки и статическая и динамическая, то по умолчанию линковщик будет использовать динамическую библиотеку. Предположим, что в домашнем каталоге пользователя имеется подкаталог lib и в нем находятся два библиотечных файла: libmy.a и libmy.so. Подкаталог includeсодержит заголовочный файл. Тогда команда компиляции
gcc -o prog_shared prog.c -I~/include -L~/lib -lmy
будет использовать динамическую библиотеку.

Для создания исполнимого файла со статической библиотекой потребуется команда:

gcc -static -o prog_static prog.c -I~/include -L~/lib -lmy

Мы создали две версии программы: с использованием динамической и статической библиотек. Во втором случае использовалась опция -static, чтобы компилятор использовал статическую библиотеку libmy.a. Если бы динамической версии библиотеки не было, то эту опцию можно было бы не указывать. Компилятор, не найдя динамической библиотеки автоматически подключает статическую библиотеку. Опция -I~/include заставляет искать заголовочные файлы в пользовательском подкаталоге include. Заметим, что в Фортране использование заголовочных файлов не требуется, и include файлы используются для других целей - определения констант и параметров. Опция -L~/lib указывает компилятору, что при сборке программы, помимо стандартных путей, следует искать библиотеки и в директории lib домашнего каталога пользователя.

При запуске на исполнение разные версии программы, скорее всего, поведут себя по-разному:

  • команда
  • ./prog_static - выполнится без проблем;
  • а при запуске
  • ./prog_shared - программа завершится с ошибкой:
  • prog_shared: error while loading shared libraries: libmy.so: cannot open shared object file: No such file or directory

Дело в том, что в момент загрузки программы, система ищет необходимые для запуска программы разделяемые библиотеки, чтобы собрать исполнимую программу. Поиск идет по заранее установленному списку директорий. Имена директорий перечислены в системном файле /etc/ld.so.conf. Очевидно, что в этот файл невозможно занести все индивидуальные каталоги пользователей. В этой ситуации на помощь приходят переменные окружения. Как уже говорилось ранее, в UNIX системах существует специальная переменная LD_LIBRARY_PATH, в которой каждый пользователь может перечислить директории для поиска разделяемых библиотек. Добавим к переменной LD_LIBRARY_PATH путь к директории lib, где находится библиотека libmy.so. Делается это командой

export LD_LIBRARY_PATH=$:~/lib (bash)
setenv LD_LIBRARY_PATH $:~/lib (tcsh)

Данной командой мы к ранее установленному значению добавили путь к персональному каталогу пользователя с библиотечными файлами. Если теперь запустить программу
./prog_shared
то она сработает корректно.

Предпочтительное использование динамических библиотек обусловлено тем, что размеры исполнимых модулей в десятки раз меньше, чем у статических. Все системные утилиты собираются с использованием динамических библиотек. А поскольку в системе их несколько тысяч, то экономятся гигантские объемы дискового пространства. Кроме того, исполнимые файлы с использованием динамических библиотек более мобильны. В качестве примера рассмотрим типичную ситуацию. В организации имеется два кластера с различной коммуникационной средой - Ethernet и Infiniband. Если использовать статические MPI библиотеки, то для каждого кластера нужно иметь свою версию программы, а если использовать динамические библиотеки, то программы становится совместимыми. При запуске программы на каждом кластере будет вызываться своя версия коммуникационной библиотеки. Еще одно преимущество динамических библиотек состоит в том, что при обновлении системной библиотеки не потребуется пересборка всех системных утилит и программ пользователей.

При использовании большого количества библиотек и include файлов команда компиляции может оказаться довольно длинной. Чтобы упростить компиляцию, часто используют командные файлы (скрипты), выступающих в качестве интерфейсов к стандартным компиляторам. Такой подход используется в пакете MPI. При сборке библиотек формируются командные файлы для вызова тех или иных компиляторов. Компиляция параллельных MPI-программ выполняется командами:

  • mpif77 -O -o progname progname.f - на языке Фортран
  • mpicc -O -o progname progname.c - на языке С
  • mpicxx -O -o progname progname.cc - на языке С++

Здесь mpif77, mpicc, mpicxx - командные скрипты, вызывающие стандартные компиляторы с настройкой путей к необходимым include-файлам и подключением всех необходимых коммуникационных библиотек библиотек. Использование таких скриптов, в свою очередь, порождает некоторые проблемы. Дело в том, что практически на любом вычислительном кластере имеется множество версий коммуникационных библиотек. Это, во-первых, связано с необходимостью обновления установленных версий, а, во-вторых, с тем, что поставщики коммуникационного программного обеспечения, как правило, предоставляют множество реализаций коммуникационных библиотек. Например, в состав коммуникационного пакета OFED входят три различных реализации MPI (MVAPICH, MVAPICH2, OpenMPI), которые к тому же собираются всеми имеющимися в системе компиляторами. К сожалению, все эти версии не совместимы друг с другом, и поэтому очень важно при работе с MPI программами соблюсти синхронность в использовании коммуникационных библиотек. Это означает, что если программа откомпилирована с использованием некоторой версии MPI, то нужно быть уверенным, что при запуске программы на выполнение будет использована та же самая версия MPI, т.е. будет использована команда mpirun из этой же версии пакета, и будут подключены нужные версии динамических библиотек. Это достигается соответствующими настройками переменных окружения PATH и LD_LIBRARY_PATH.

10 лучших IDE

10 лучших IDE

IDE (Integrated Development Environment) – это интегрированная, единая среда разработки, которая используется разработчиками для создания различного программного обеспечения. IDE представляет собой комплекс из нескольких инструментов, а именно: текстового редактора, компилятора либо интерпретатора, средств автоматизации сборки и отладчика. Помимо этого, IDE может содержать инструменты для интеграции с системами управления версиями и другие полезные утилиты. Есть IDE, которые предназначены для работы только с одним языком программирования, однако большинство современных IDE позволяет работать сразу с несколькими.

Сегодня я расскажу вам о 10 лучших средах разработки как в платном, так и в бесплатном сегменте.

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

Чем IDE отличается от текстового редактора?

IDE представляет собой более сложный инструмент, чем обычный текстовый редактор. Несмотря на то что в текстовых редакторах есть масса полезных функций вроде подсветки синтаксиса, единственная их задача – обеспечивать работу с кодом. То есть для полноценной разработки вам понадобится еще хотя бы компилятор и отладчик.

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

Какая среда разработки подойдет конкретно для вас? В этом вопросе надо основываться на четырех факторах:

  • язык разработки;
  • простота использования;
  • на каких платформах работает;
  • стоимость.

Итак, давайте рассмотрим лучшие среды разработки.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *