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

Как программировать на линуксе

  • автор:

Как начать писать программный код Си в ОС Linux (Руководство для совсем начинающих)

Добрый день. Этот материал рассчитан на людей, будущих программистов, которые только начинают разбираться в программировании под ОС Linux. Я попробую здесь показать прямое руководство к действию на примере тех простых инструментов, которые использовал некогда сам при изучении Си в процессе знакомства с Linux. На самом деле, с теми или иными поправками, это руководство можно использовать в большинстве дистрибутивов. Руководство однозначно подходит для всех deb-based дистрибутивов.

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

Итак: у Вас сейчас установлен дистрибутив ОС, как говорится, «из коробки». Перед глазами пособие для разработчика/учебник/просто_хорошая_книга по «Языку программирования Си». И никакой вменяемой, полноценной подробной информации о том, как же собственно откомпилировать и выполнить, написанный в книге, исходный код. Быстрый осмотр тематических ресурсов уже показал Вам, что, необходимо установить компилятор Си, запустить его с нужными параметрами и потом запустить компилированный бинарный код. Примерно с этого момента мы и начнём.

Установка компилятора

Я имею ввиду, что Вы скорее всего (бывший) пользователь ОС Windows и действия в чёрном/синем окошке при помощи клавиатуры оканчивались где-то на команде ping, кажутся неким таинством. Однако отмечу, что всё банально просто и текстовой интерфейс предоставляет намного более гибкие возможности (скорее всего Вы неоднократно Вы слышали это ранее). Приступим:

Я подразумеваю, что с понятием компиляции и о том что такое компилятор Вас уже познакомила правильная книга.

На этом этапе всё будет очень быстро и просто. Открываем терминал и пишем:

(На всякий случай: вставка в gnome-terminal ctrl+shift+v)

Сразу поясню, что текст слева от курсора — это приглашение командного интерпретатора и оно выглядит следующим образом:

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

Данная строка «говорит» интерпретатору: «от имени суперпользователя запустить менеджер пакетов для установки пакета gcc».

Система попросит Вас ввести пароль суперпользователя и приступит к установке компилятора.

Если же он уже установлен, то менеджер пакетов apt просто укажет на это примерно следующим образом:

Установка редактора

Обычно с дистрибутивом Ubuntu поставляется весьма интересный текстовой редактор gedit . Однако в других дистрибутивах возможно придётся установить этот редактор:

Создание файла с исходным кодом

Теперь пришло то самое время нашего классического «hello world»! Давайте сделаем это в стиле linux. Просто наберите в консоли:

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

И далее наш программный код на языке Си в редакторе:

(Стоит отметить, что в редакторе gedit есть подсветка синтаксиса для различных языков программирования. Переключить режимы подсветки можно в нижней части окна редактора.)

Не забываем сохранить изменения нажатием ctrl+s. Обратите внимание, что вопросов об имени файла не последовало, так как имя было уже указано параметром при запуске редактора из командной строки терминала.

Компиляция и запуск

Закрываем окно редактора нажатием Alt+F4 и запустим же то сокровенное ради чего все тут и собрались:

И в ответ только новое приглашение. В отличие от стиля в ОС Windows, когда консоль, жутко подробно по-умолчанию, комментирует выполняемые действия — большинство программ в ОС семейства *nix сообщают только об исключительных ситуациях, ошибках и тому подобных вещах. То есть если «в ответ тишина» — то всё прошло хорошо.

Теперь в домашнем каталоге у нас появился файл a.out — он и есть файл с исполнимым кодом.

Для запуска этого файла на исполнение — назначим ему атрибут: «исполнимый»:

и теперь запустим получившееся приложение:

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

/a.out Это не имеет значения здесь, так как файл создан в домашнем каталоге пользователя.)

И мы получаем вывод в терминале:

Для выполнения всех повторных действий: изменение кода и снова компиляция, — Вы можете не вводить все эти команды каждый раз заново, а использовать стрелки вверх и вниз, для быстрого выбора команд из истории. И, кстати, вывод списка истории всех введённых команд можно выполнить командой (на самом деле программой) history .

Минутка автоматизации

Теперь приступим к очень интересному моменту связанному с творчеством в духе *nix. Каждый раз вводить много скучных команд неинтересно, возможно, даже вредно. Мы расширим функционал редактора gedit и доработаем его «напильником» до состояния примитивной среды разработки: запустим gedit и откроем меню параметров,

Главное меню gedit

Главное меню gedit

где на вкладке «Расширения» добавляем «Внешние инструменты»

"Настройки" - "Расширения"

«Настройки» — «Расширения»

И затем, из того же главного меню gedit выбираем «Управление внешними инструментами».

"Управление внешними инструментами"

«Управление внешними инструментами»

Как Вы уже поняли — здесь можно выполнить доработку функциональности текстового редактора. Создадим новый инструмент: «Компиляция и запуск», В качестве вывода используем нижнюю область редактора. Инструмент назначим для файлов C и C++. Назначим клавишу F5 (дело вкуса) на применение инструмента и собственно сам код инструмента в виде скрипта bash:

Разберёмся в том, что тут происходит:

#!/bin/bash — указание командного интерпретатора для выполнения скрипта.

gcc -o a.out $GEDIT_CURRENT_DOCUMENT_NAME — здесь мы запускаем компилятор, где в параметре -o указываем имя выходного файла. Пускай он будет таким же как и по-умолчанию.

$GEDIT_CURRENT_DOCUMENT_NAME — через эту переменную gedit передаёт имя файла.

Дальше Вы уже знаете — назначение атрибута «исполнения», запуск файла и потом:

rm ./a.out — удаление созданного исполнимого файла.

Попробуем инструмент в деле:

Теперь можно продолжать изучать пособие для разработчика/учебник/просто_хорошую_книгу по «Языку программирования Си» на практике.

Заключение

На самом деле в ОС Linux полно возможностей по доработке и использованию различного ПО. Само ПО является максимально гибким. Необязательно использовать предложенные мною средства, скорее методы, разработки.

В дальнейшем Вам обязательно понадобятся более серьёзные средства. А на первое время Вы можете дополнительно посмотреть другие редакторы, вплоть до больших и серьёзных сред разработки. Однако обязательно обратите внимание на редактор vim.

Программирование под Linux

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

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

1. На чем пишут программы?

Исторически сложилось так, что ядро Unix было написано на языке Си. Даже более того, этот язык был создан для написания ядра Unix. Поскольку ядро Linux было основано на ядре Minix (версии Unix), то оно тоже было написано на Си. Поэтому можно сказать, что основной язык программирования для Linux это Си и С++. Такая тенденция сохранялась на протяжении долгого времени.

А вообще, писать программы для Linux можно почти на любом языке начиная от Java и Python и заканчивая С# и даже Pascal. Для всех языков есть компиляторы и интерпретаторы. Писать программы на С++ сложно, а Си многими уже считается устаревшим, поэтому множество программистов используют другие языки для написания программ. Например, множество системных инструментов написаны на Python или Perl. Большинство программ от команды Linux Mint, установщик Ubuntu и некоторые скрипты apt написаны на Python. Множество скриптов, в том числе простые скрипты оптимизации написаны на Perl. Иногда для скриптов используется Ruby. Это скрипты OpenShift или, например, фреймворк Metasploit. Некоторые разработчики кроссплатформенных программ используют Java. Но основные компоненты системы написаны все же на Си.

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

2. Библиотеки

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

Библиотеки делятся на два типа:

  • Статические — они связываются с программой на этапе компиляции, они связываются и после этого все функции библиотеки доступны в программе как родные. Такие библиотеки имеют расширение .a;
  • Динамические — такие библиотеки встречаются намного чаще, они загружены в оперативную память, и связываются с программной динамически. Когда программе нужна какая-либо библиотека, она просто вызывает ее по известному адресу в оперативной памяти. Это позволяет экономить память. Расширение этих библиотек — .so, которое походит от Shared Object.

Таким образом, для любой программы на Си нужно подключать библиотеки, и все программы используют какие-либо библиотеки. Также важно заметить, на каком языке бы вы не надумали писать, в конечном итоге все будет сведено к системным библиотекам Си. Например, вы пишите программу на Python, используете стандартные возможности этого языка, а сам интерпретатор уже является программой на Си/С++, которая использует системные библиотеки для доступа к основным возможностям. Поэтому важно понимать как работают программы на Си. Конечно, есть языки, вроде Go, которые сразу переводятся на ассемблер, но там используются принципы те же, что и здесь. К тому же системное программирование linux, в основном, это Си или С++.

3. Процесс сборки программы

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

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

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

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

А теперь, давайте рассмотрим весь єтот процесс на практике с использованием компилятора GCC.

4. Как собрать программу

Для сборки программ в Linux используется два типа компиляторов, это Gcc и Clang. Пока что GCC более распространен, поэтому рассматривать мы будем именно его. Обычно, программа уже установлена в вашей системе, если же нет, вы можете выполнить для установки в Ubuntu:

sudo apt install gcc

Перед тем как мы перейдем к написанию и сборке программы, давайте рассмотрим синтаксис и опции компилятора:

$ gcc опции исходный_файл_1.с -o готовый_файл

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

  • -o — записать результат в файл для вывода;
  • -c — создать объектный файл;
  • -x — указать тип файла;
  • -l — загрузить статическую библиотеку.

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

include <stdio.h> #include <math.h>

Я специально добавил функцию корня чтобы показать как работать с библиотеками. Сначала нужно собрать объектный файл. Перейдите в папку с исходниками и выполните:

gcc -c program.c -o program.o

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

gcc -lm program.o -o program

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

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

Это две библиотеки загрузчика, стандартная libc и libm, которую мы подключили.

5. Автоматизация сборки

Когда мы рассматриваем программирование под Linux невозможно не отметить систему автоматизации сборки программ. Дело в том, что когда исходных файлов программы много, вы не будете вручную вводить команды для их компиляции. Можно записать их один раз, а затем использовать везде. Для этого существует утилита make и файлы Makefile. Этот файл состоит из целей и имеет такой синтаксис:

цель: зависимости <Tab> команда

В качестве зависимости цели может быть файл или другая цель, основная цель — all, а команда выполняет необходимые действия по сборке. Например, для нашей программы Makefile может выглядеть вот так:

program: program.o gcc -lm program.o -o program

program.o: program.c gcc -c program.c -o program.o

Затем вам достаточно выполнить команду make для запуска компиляции, только не забудьте удалить предыдущие временные файлы и собранную программу:

Программа снова готова и вы можете ее запустить.

Выводы

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

Name already in use

Cplusplus / Основы программирования для Linux / README.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink
  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

Основы программирования для Linux

  • Файлы main.chello.hhello.cMakefile
  • Файл main_runtime.c

1.1 Основной инструментарий разработчика Linux

Инструменты разработки в Ubuntu есть в пакете biuld_essential, компиляторы C , C++ , утилита make и некоторые другие.

Текстовый редактор, например vim

Файловый менеджер mc

Настройки многих программ сохраняются в виде файла (иногда подкаталога) в домашнем каталоге пользователя. Обычно имя этого файла начинается с точки, а заканчивается буквами rc между которыми собственно записано имя программы. Так для текстового редактора vim настройки сохраняются в файле .vimrc. Настройки командной оболочки bash хранятся в файле .bashrc. Увидеть такие файлы и каталоги можно командой

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

Есть два файла hello.h и hello.c

Динамические библиотеки собираются с флагом -shared и расширением .so . По соглашению именований динамических библиотек, сначала идет префикс lib , потом имя библиотеки Hello , а потом расширение .so . Версии библиотеки указываются после расширения, например .so.1 .so.1.1 .so.2.1.10 .

Ключ -fPIC указывает компилятору, генерировать код с относительной адресацией Generate position-independent code (PIC). По моему мнению это упрощает загрузку динамической библиотеки в произвольное место в адресном пространстве вызывающего её процесса(может я не прав или не совсем прав).

Функции из динамических библиотек вызываются как обычно (main.c)

Для сборки исполняемого файла, который использует функцию(ии) из динамической библиотеки нужен ключ -l , после которого иимя библиотеки. Если библиотека расположена не в стандартных директориях, например стандартная /usr/lib , то нужен ключ -L и путь, где искать библиотеку. В данном примете будет так, потому что библиотека находится в директории сборки.

Однако при попытки сборки получим ошибку, потому что пециальный сервис ld (динамический линковщик) ищет бибилиотеки в строго определенных местах, есть минимум два решения:
1) копировать библиотеку в стандартное хранилище библиотек, например в /usr/lib
2) установить специальную переменную окружения LD_LIBRARY_PATH , в которой прописать путь, этот путь для сервиса ld будет смотреть на временные дополнительные каталоги для поиска динамических библиотек.

Переменная окружения LD_PRELOAD в сервисе ld указывает на стандартные пути, её изменять не следует

1.2.2 Экспортируемые имена функций в программах на C++

Если библиотека написана на C , приложение на C++ , то код не соберется, потому что имена функций в C и С++ различны, эта разница объясняется тем, что в C++ есть перегрузка функций, в чистом C нет. Решение — использовать специальный макрос __cplusplus .

Посмотреть экспортируемые символы библиотеки

Узнать по закодированному имени функции её исходный прототип можно воспользоваться утилитой c++filt

Утилита ldd показывает от каких динамических библиотек зависит определенное приложение или библиотека.

Задача. Создание динамической библиотеки

В этом задании Вам необходимо создать разделяемую динамическую библиотеку libsolution.so в которой реализовать функцию со следующим прототипом

Функция возвращает значение длины строки string, умноженной на multiplier, и увеличивает на 1 значение, на которое указывает count.

Представление решения
Решение предоставляется в виде двух файлов solution.c и Makefile, в последнем предполагается цель по умолчанию, которая приводит к сборке Вашей библиотеки.

Вывод
Программа(функция в библиотеке) ничего не выводит на консоль.
Файлы в папке mod_1_step_2: solution.c Makefile

1.3. Runtime линковка (libdl)

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

Для загрузки динамических библиотек и вызова, расположенных там функций, непосредственно в процессе исполнения программы служит специальный системный интерфейс libdl (динамическая библиотека libdl.so ). Это набор функций при помощи которых можно загрузить произвольную библиотеку в память, просмотреть какие символы и функции есть в загружаемой библиотеке, и вызвать эти функции.

Константы в dlopen() :
1) RTLD_NOW — библиотека будет загружена в память непосредственно в момент вызова функции dlopen()
2) RTLD_LAZY — библиотека будет загружена только тогда, когда программа попытается обратиться к её содержимому(функции)

Приложение нужно собирать с динамической библиокой libdl

Задача. Run-time компоновка

Разработать программу solution, обеспечивающую загрузку динамической библиотеки в режиме исполнения. При запуске, программе передается имя динамической библиотеки, имя функции которая должна быть вызвана, и её фргумент. Функция в библиотеке принимает на вход int и возвращает int. Результат, возвращаемый функцией должен быть выведен отдельной строкой(оканчивающийся символом новой строки \n) в stdout

Пример вызова

В примере, должна быть загружена библиотека libShared.so, в которой вызвана функция someSecretFunctionPrototype с аргументом 8. Результат (16) выведен в stdout

Представление решения
Решение предоставляется в виде двух файлов solution.c и Makefile, в последнем предполагается цель по умолчанию, которая приводит к сборке Вашей библиотеки.

Вывод Программа выводит в стандартный поток вывода число, полученное после вызова функции, и символ новой строки \n.
Файлы в папке mod_1_step_3: solution.c Makefile

2.1 Базовые функции работы с файлами

Общее определение. Файл — это поименованная последовательность данных на жестком диске. У каждого файла есть набор атрибутов: время, размер, права доступа и т.д.

В мире Linux понятие файла шире — это что угодно куда можно записывать и считывать байты(файл, директория, монитор, мышь, клавиатура, принтер и т.д.). Соответсвенно, что попадает под опредение файла, то автоматически имеет файловый интерфейс(поддержтвает операции ввода/вывода). Базовые опреации работы с файлами в языке C обеспечивает набор функций fopen() , fread() , fwrite() , fclose() , fflush() и другие. Так же работать с файлами можно через функции ореционной системы или как их еще называют системные вызовы( open() , read() , write() , close() . ). Разница между функциями языка и системными вызовами заключается в том, что у последних больше возможностей. Рассмотрим несколько системных вызовов.

pathname — имя или путь+имя файла
flags — флаги
Возвращает файловый дескриптор — неотрицательное целое число. Он нужен для последующей работы с файлом.

Как правило в системных вызовах работы с файлами первым параметром является как раз файловый дескриптор.

Подробную информацию о системных вызовах можно почитать на второй ман странице.

Управление самим файловым дескриптором осуществляется через интерфейс fcntl()( file control ). Позволяет устаналвивать флаги файла, блокировки файла, владение файловым дескриптором и далее.

2.2 Работа с каталогами и путями

Описателем каталога является структура DIR

Открыть каталог можно двумя функциями:

После того, как открыли каталог и получили указатель на структуру DIR, можно обойти все элементы каталога используя специальную структуру dirent , которая содержит информацию о элементе каталога, и функцию readdir()

Процессы-демоны Создать демона:
1 fork()
2 в демоне перейти в каталог /
3 установить идентификатор сессии setsid(2)
4 закрыть файловые дескрипторы стандартного ввода/вывода stdin, stdout, stderr

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

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