Проект «SnakeProject» Михаила КозловаРегистрация

Навигация
⇒FreeBSD and Nix⇒

⇐CISCO
⇐Voice(Asterisk\Cisco)
⇐Microsoft
⇐Powershell
⇐Python
⇐SQL\T-SQL
⇐1С
⇐Общая
⇐WEB Разработка
⇐ORACLE SQL \ JAVA
⇐Мото

Примеры работы с 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


================================================================================================
Классическая первая программа:
hello.cpp:

# g++ hello.cpp -o hello

# ./hello
Hello, World!

В программе использовалась библиотека стандартного вывода <iostream>
namespace std - избавляет от необходимости указания префикса имен, который соответствует стандартной библиотеке
Т.е. в строке cout << "Hello, World!\n"; необходимо было бы указать std::cout << "Hello, World!\n";


================================================================================================
Азы языка, циклы while и do while, условие if, выход из цикла break:

cycles.cpp:


# 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
Скомпилируем их и запустим свою первую программу


main.cpp:


body.cpp:


body.hpp:


# 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)


================================================================================================
Создание динамической библиотеки пример 1 (gcc для языка С)

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

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


body.h:


body.c:


main.c:


Наименование состоит из 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!


Если получаем ошибку - 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)


================================================================================================
Создание динамической библиотеки пример 2 (g++ для языка С++)


body.h:


body.cpp:


main.c:


Создадим файл с объектным кодом динамической библиотеки:
# 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!

Return code: 0

 

================================================================================================
Зависимости (утилита make)


Нарисуем схему зависимостей файлов:
utility ---> main.c ---> body.h <--- body.cpp <--- libBody.so


Утилита make читает специальный файл, в котором описаны зависимости

В фале описывается:
Зависимый файл или объект, далее :, далее имена объектов, от которых он зависит
Следующая строка - табуляция и команда, которая из списка зависимостей делает файл из левой части

Пример make файла по нашей схеме зависимостей

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!

Return code: 0

# 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: - откроется в бинарном режиме

По дефолту предполагаются режимы:
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()

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

file.cpp:


Скомпилируем и запустим:
# 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 ()


Пример дебага с точкой останова в функции:
# 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 <print_arg(int)+4>


Выполним - 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" - завершит работу


server.cpp:


Создаем утилиту:
# 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

 


Комментарии пользователей

Эту новость ещё не комментировалиНаписать комментарий
Анонимам нельзя оставоять комментарии, зарегистрируйтесь!

© Snakeproject.ru создан в 2013 году. При копировании материала с сайта - оставьте ссылку.


Яндекс.Метрика

Goon Каталог сайтов Рейтинг@Mail.ru