Язык си для линукса

Примеры работы с C и C++ в ос Linux или FreeBSD

Примеры работы с C и C++ в ос Linux или FreeBSD

Сразу оговорюсь, это не учебник и т.п. .
Необходимы базовые знания языка c++
В данном гайде будут лишь некоторые примеры работы с языком C и C++ в unix подобных системах

Установка компилятора для centos\debian\freebsd:
# yum -y install gcc gcc-c++
# apt-get install gcc gcc++
# pkg install gcc

Проверка версии:
# gcc -v

# g++ hello.cpp -o hello

# ./hello
Hello, World!

В программе использовалась библиотека стандартного вывода
namespace std — избавляет от необходимости указания префикса имен, который соответствует стандартной библиотеке
Т.е. в строке cout ================================================================================================
Азы языка, циклы while и do while, условие if, выход из цикла break:

# g++ cycles.cpp -o cycles

# ./cycles
while: 5
while: 10
while: 20
in if: 20
do while: 5

================================================================================================
Программа, состоящая из нескольких файлов:
Создадим 3 файла: main.cpp, body.cpp, body.hpp
main.cpp — точка входа, вызывает функцию из файла body.cpp
body.cpp — описывает функцию с логикой
body.hpp — объявляет интерфейс, функцию из файла body.cpp, подключается в main.cpp
Скомпилируем их и запустим свою первую программу

# g++ body.cpp main.cpp -o utility
# ./utility
6

================================================================================================
Компиляция, линковка, запуск программы
g++ — обертка над предпроцессором, компилятором и линковщиком

1й этап компиляции:
Пример компиляции файлов с исходным кодом в файлы предпроцессора:
# g++ -E body.cpp -o body_preprocessed.cpp
# g++ -E main.cpp -o main_preprocessed.cpp

2й этап компиляции:
Пример компиляции файлов с исходным кодом в файлы objective (в бинарном виде)
Скомпилируем файлы с исходным кодом:
# g++ -c body.cpp -o body.o
# g++ -c main.cpp -o main.o

3й этап компиляции (с линковкой в единый исполняемый модуль):
Создадим программу из бинарных файлов и запустим
# g++ body.o main.o -o utility
# ./utility
6

Компилятор понимает сразу указание файлов cpp:
# g++ body.cpp main.cpp -o utility
# ./utility
6

Показать файл в виде ассемблера:
# g++ -S body.cpp && cat body.s
# g++ -S main.cpp && cat main.s

В выводе мы увидим закодированное название нашей функцмм func:
call _Z4funci
_Z — префикс
4 — количество символов в названии функции
func — имя
i — первая буква от параметра integer

Вывести начальную сигнатуру функции можно так:
# c++filt -n _Z4funci
func(int)

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

Имеем немного переделанный код нашей предыдущей программы

Наименование состоит из lib + имя библиотеки + .so

На примере нашей разделенной программы создадим библиотеку
shared — указывает создать динамическую (разделяемую) библиотеку

Для х32:
# gcc -o libBody.so -shared body.c

Для х64:
# gcc -o libBody.so -shared -fPIC body.c

Скомпилируем утилиту, ссылающуюся на созданную нами библиотеку

Для х32:
# gcc main.c -L. -lBody -o utility

Для х64:
# gcc main.c -fPIC -L. -lBody -o utility

Где:
-L. — путь, где искать библиотеки (т.е. текущий каталог)
-lbody — ключ + имя библиотеки исключая префикс и расширение

Если мы запустим нашу утилиту — ./utility, то она выдаст ошибку:
Shared object «libBody.so» not found, required by «utility»

Это происходит потому, что сервис поиска библиотек ld не знает пути к нашему каталогу
Можно поместить библиотеку в стандартный каталог для библиотек (/lib или /usr/lib)
Или добавить наш каталог в переменную окружения:
# bash
# export LD_LIBRARY_PATH=.
# ./utility
Hello from func!

Читайте также:  Ntp сервер windows что это

Если получаем ошибку — Segmentation fault (core dumped), выручит генерация дампов ядра:
# ulimit -c unlimited
# ./utility

Используем полученный core дамп-файл:
# gdb utility utility.core
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type «show copying» to see the conditions.
There is absolutely no warranty for GDB. Type «show warranty» for details.
This GDB was configured as «amd64-marcel-freebsd». (no debugging symbols found).
Core was generated by `./utility’.
Program terminated with signal 11, Segmentation fault.
Reading symbols from ./libBody.so. (no debugging symbols found). done.
Loaded symbols for ./libBody.so
Reading symbols from /lib/libc.so.7. (no debugging symbols found). done.
Loaded symbols for /lib/libc.so.7
Reading symbols from /libexec/ld-elf.so.1. (no debugging symbols found). done.
Loaded symbols for /libexec/ld-elf.so.1
#0 0x0000000800b7b1bf in strlen () from /lib/libc.so.7
(gdb)

Какие имена обращения доступны в нашей библиотеке:
# nm libBody.so

Видим имя функции «func» для обращения:
.
00000000000004e5 T func
.

Формат вывода можно вывести так:
# man nm

Где — T — тип «текст программы»

Какие библиотеки использует программа:
# ldd ./utility
./utility:
libBody.so => ./libBody.so (0x800821000)
libc.so.7 => /lib/libc.so.7 (0x800a22000)

Создадим файл с объектным кодом динамической библиотеки:
# g++ -fPIC -c body.cpp -o body.o

Создадим из объектного файла библиотеку:
# g++ -shared -o libBody.so -fPIC body.o

Создадим утилиту:
# g++ main.c -fPIC -L. -lBody -o utility

Проверяем:
# ./utility
Hello world!

Нарисуем схему зависимостей файлов:
utility —> main.c —> body.h Makefile:

Т.е.
Цель all зависит от bin lib
Цель bin зависит от body.h main.c lib, собирает исполняемый файл
Цель lib зависит от body.h body.cpp, собирает файл библиотеки
Цель clean не имеет зависимостей, очищает указанные файлы, — заставляет не учитывать результат команды

Файлы изначально:
# ls
Makefile body.cpp body.h main.c

По умолчанию отрабатывает цель all:
# make
g++ -fPIC -c body.cpp -o body.o && g++ -shared -o libBody.so -fPIC body.o
g++ main.c -fPIC -L. -lBody -o utility

Проверяем утилиту:
# ./utility
Hello world!

# ldd ./utility
./utility:
libBody.so => ./libBody.so (0x800821000)
libstdc++.so.6 => /usr/local/lib/gcc8/libstdc++.so.6 (0x800a22000)
libm.so.5 => /lib/libm.so.5 (0x800db7000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x800fe4000)
libc.so.7 => /lib/libc.so.7 (0x8011f3000)

Проверим цель clean:
# make clean
rm utility libBody.so *.o 2>/dev/null

Файлы после очистки:
# ls
Makefile body.cpp body.h main.c

Типы работы с файлами:
ifstream — чтение файла
ofstream — запись в файл
fstream — запись и чтение

Для операций выше необходимо открыть файл с помощью функции open ( open(path) или open(path, flags) ):
int open(const char *path, int flags, . ));

Где:
path — имя или полный путь до файла
flags — флаги
Возвращает файловый дескриптор (неотрицательное целое число), который необходим для работы с файлом

Возможные режимы открытия файла:
ios::in: — ввод (чтение) ( только для объектов ifstream или fstream )
ios::out: — вывод (запись) ( старые данные удаляются, только для объектов ofstream или fstream )
ios::app: — дозапись ( старые данные не удаляются )
ios::ate: — указатель переходит в конец файла
ios::trunc: — файл усекается при открытии, может быть установлен с режимом out
ios::binary: — откроется в бинарном режиме

Читайте также:  Installing lvm on linux

По дефолту предполагаются режимы:
ifstream — ios::in
ofstream — ios::out
fstream — ios::out и ios::in

Проверить, окрыт ли файл — is_open(), если открыт — вернет true:
std::ifstream in1;
in1.open(«file.txt»);
if ( in1.is_open() )

Примеры

Поток для записи, окрытие файла для записи:
std::ofstream out1;
out.open(«file.txt»);

Поток для записи, окрытие файла для дозаписи:
std::ofstream out2;
out2.open(«file.txt», std::ios::app);

Вариант с помощью вызова конструктора с записью:
std::ofstream out3(«file.txt»);

Поток для чтения, окрытие файла для чтения:
std::ifstream in1;
in.open(«file.txt»);

Вариант с помощью вызова конструктора с чтением:
std::ifstream in2(«file.txt»);

Вариант с помощью вызова конструктора с дозаписью:
std::fstream fs(«file.txt», std::ios::app);

По окончанию работы необходимо закрыть файл с помощью функции close()

Напишем программу с примерами записи и чтения файлов

Скомпилируем и запустим:
# g++ file.cpp -o file

# ./file
Line in the sand
Double trouble
Line in the sand
Double trouble

Есть пара утилит, которые могут детально паказать вызовы и подноготную работы программ

В Linux можно воспользоваться утилитами:

Для трассировки библиотечных вызовов
ltrace

Для перехвата и записи системных вызовов, выполненяемых процессом
strace

В FreeBSD можно воспользоваться утилитами:

Для трассировки библиотечных вызовов
pkg install ltrace

Отобразить системные вызовы команды, запущенного процесса
truss

Пример запуска утилит:
hello.cpp:

# g++ hello.cpp -o hello

# ./hello
Hello, World!

Некоторые опции ltrace

-c
Время графа и призывы к каждой библиотеке называют и сообщают о резюме на выходе программы

-C, — demangle
Расшифруйте (demangle) имена символа низкого уровня на имена уровня пользователя
Помимо удаления любой начальной буквы подчеркивают префикс, используемый системой, делает имена функции C удобочитаемыми

# ltrace ./hello
# ltrace -c ./hello
# ltrace -C ./hello

Некоторые опции truss

-c
Не показывает отдельные системные вызовы или сигналы
Вместо этого перед переходом напечатает резюме, содержащее для каждого системного вызова:
Полное системное используемое время, вызовы, и количество вызовов с возвратом ошибки

# truss ./hello
# truss -c ./hello

Отладочную информацию gcc при компиляции можно вывести с помощью уровней от 0 до 3
Примеры работы с gcc для программ на с:
# gcc -g3 hello.c -o hello
# gcc -ggdb hello.c -o hello

С core файлом мы встречались ранее, чтоб его создавать, нужно применить уже знакомую команду:
# ulimit -c unlimited

Сломаем нашу программу hello.cpp:

Пару слов о printf:
%d — Десятичное число целого типа со знаком

Компилируем:
# g++ hello.cpp -o hello

Получаем ошибку при выводе:
# ./hello
Floating point exception (core dumped)

Появился файл дампа — hello.core

Проведем дебаг (Видим Arithmetic exception в print_arg ()):
# gdb ./hello hello.core
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type «show copying» to see the conditions.
There is absolutely no warranty for GDB. Type «show warranty» for details.
This GDB was configured as «amd64-marcel-freebsd». (no debugging symbols found).
Core was generated by `./hello’.
Program terminated with signal 8, Arithmetic exception.
Reading symbols from /usr/local/lib/gcc8/libstdc++.so.6. (no debugging symbols found). done.
Loaded symbols for /usr/local/lib/gcc8/libstdc++.so.6
Reading symbols from /lib/libm.so.5. (no debugging symbols found). done.
Loaded symbols for /lib/libm.so.5
Reading symbols from /lib/libgcc_s.so.1. (no debugging symbols found). done.
Loaded symbols for /lib/libgcc_s.so.1
Reading symbols from /lib/libc.so.7. (no debugging symbols found). done.
Loaded symbols for /lib/libc.so.7
Reading symbols from /libexec/ld-elf.so.1. (no debugging symbols found). done.
Loaded symbols for /libexec/ld-elf.so.1
#0 0x000000000040080a in print_arg ()

Читайте также:  Что делать если при обновлении windows выключил компьютер

Пример дебага с точкой останова в функции:
# gdb ./hello
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type «show copying» to see the conditions.
There is absolutely no warranty for GDB. Type «show warranty» for details.
This GDB was configured as «amd64-marcel-freebsd». (no debugging symbols found).

Делаем точку останова в бажной функции с именем print_arg:
# (gdb) b print_arg
Breakpoint 1 at 0x4007f9

# (gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004007f9

Выполним — r (т.е. — run):
# (gdb) r
Starting program: /root/test/hello
(no debugging symbols found). (no debugging symbols found). (no debugging symbols found).
Breakpoint 1, 0x00000000004007f9 in print_arg ()

Более подробный вывод задаем опцией -g:
# g++ -g hello.cpp -o hello

Если выйдет ошибка — Dwarf Error: wrong version in compilation unit header (is 4, should be 2)
Дайте команду (определенная версия DWARF 2):
# g++ -g -gdwarf-2 hello.cpp -o hello

# gdb ./hello
# (gdb) b print_arg
# (gdb) r
Starting program: /root/test/hello

Breakpoint 1, print_arg (arg1=0) at hello.cpp:6
6 arg2 = arg1 + arg2;

Если дать эту команду без параметров, она выведет первые 9 строк кода или переместит на указанное число:
# (gdb) list
# (gdb) list -10

Команду можно вводить еще. Пока код не закончится

Поставить точку останова на строке кода очень просто, например на 4й строке:
# (gdb) b 4

Вывести локальные переменные в текущем фрейме:
# (gdb) info locals

Вывести локальные аргументы в текущем фрейме:
# (gdb) info args

Удаление, включение, выключение breakpoint по номеру из info breakpoints по номеру:
# (gdb) d 1
# (gdb) enable 1
# (gdb) disable 1

Вывести значение переменной:
# (gdb) p arg1

Напишем простую утилиту, которая по протоколу UDP будет принимать текстовые сообщения

При запуске будет передавать номер порта, на котором оно будет работать

При получении сообщения «off» — завершит работу

Создаем утилиту:
# g++++ ./server.cpp -o server

Запускаем с указанием порта:
# ./server 777

Пошлем несколько сообщений:
# echo -n «ac» | nc -4u -w1 127.0.0.1 777
# echo -n «on» | nc -4u -w1 127.0.0.1 777
# echo -n «off123» | nc -4u -w1 127.0.0.1 777
# echo -n «off» | nc -4u -w1 127.0.0.1 777

Источник

Оцените статью