Pull to refresh

Взгляд снизу вверх или Ubuntu Server для разработчика электроники. Часть 2

Reading time 7 min
Views 2.4K
Продолжаем развивать тему использования компьютера с ubuntu server в качестве устройства для связи мира микроконтроллеров с миром персональных компьютеров.

Ссылка на часть 1
image
Напомню, что статья описывает практику применения описанной техники и не ставит целью охватить все глубины и возможности современной аппаратуры. Это один из вариантов решения поставленной задачи.

В этой части я покажу, как проходить следующие моменты:

Статический IP + DHCP сервер
В локальной сети на данный момент предполагается всего два устройства. Сам сервер и управляющая машина. Исходя из данной простоты настроим Ubuntu server.
Статический IP
Опорная информация
1. Чтобы узнать логическое имя (logical name) сетевого интерфейса делаем:

sudo lshw -C network
и ищем строчку
 logical name: enp3s0

Вариант использования функции ifconfig (как аналог ipconfig в windows) иногда может дать только loop 127.0.0.1. Название интерфейса зависит от аппаратуры. На двух разных машинах ставил ubuntu с одной флэшки и были разные имена сетевых интерфейсов. enp3s0 — это классификация по шине и номеру устройства, как я понимаю. Бывают ещё из такой серии eth0 и такой ens35. Не пугайтесь.

2. Изменение файла конфигурации сети

sudo nano /etc/network/interfaces
(nano — эта маленький текстовый редактор)
Должно быть так:

	# This file describes the network interfaces available on your system
	# and how to activate them. For more information, see interfaces(5).

	source /etc/network/interfaces.d/*

	# The loopback network interface
	auto lo
	iface lo inet loopback

	# My local network.
	allow-hotplug enp3s0
	iface enp3s0 inet static
	address 172.16.55.1
	netmask 255.255.255.0
	gateway 172.16.55.1

Нажимаем cntrl-x — для выхода из редактора
Нажимаем y — для сохранения изменений

3. Перезагружаем сетевой сервис:
service networking restart


DHCP сервер
Опорная информация
1. Редактируем файлы для работы DHCP сервера
sudo nano /etc/default/isc-dhcp-server
найдите строчку
INTERFACES=""
— это название интерфейса для DHCP сервера. В нашем случае должно быть так:

INTERFACES="enp3s0"

В файле
sudo nano /etc/dhcp/dhcpd.conf
нужно добавить
subnet 172.16.55.0 netmask 255.255.255.0{
 range 172.16.55.2 172.16.55.100;
}
В рамках этой подсети DHCP сервер будет выдавать адреса в диапазоне от 2 до 100.

2. Если будете использовать функцию gethostbyname(), то в файле
sudo nano /etc/hosts
должно быть прописано имя пк и IP. Возможно есть более удобный способ получить свой собственный IP, но мне он не поддался.
127.0.0.1	localhost
172.16.55.1	ubuntu

В файле /etc/hostname хранится имя пк (ubuntu). Можно посмотреть через
sudo cat /etc/hostname
и ещё миллионом способов.

3. Перезагружаем сетевой сервис:
service networking restart

SSH доступ
По идее, подключив теперь стационарный компьютер, мы получи для него IP и сможем иметь доступ по SSH. Для доступа из Windows я использовал утилиту PuTTY. Там же, в программных файлах PuTTY, есть утилита для передачи данных по сети.
Вот состав батника для передачи файлов с Ubuntu на Windows.
d:\"Program Files"\PuTTY\pscp.exe ubuntu@172.16.55.1:/home/ubuntu/tool/* "F:/WORK/SERVER/tool/"

И для закачки на сервер
d:\"Program Files"\PuTTY\pscp.exe "F:/WORK/SERVER/tool/*" ubuntu@172.16.55.1:/home/ubuntu/tool/


Ставим драйвер от FTDI
Драйвера от FTDI поставляются в сжатом виде. Содержатся в пакете сами драйвера, библиотеки, примеры использования и README.pdf файл, который больше всего был интересен. Из файла D3XX Programmers Guide ни разу не ясно, как использовать драйвер для Linux. Пришлось даже написать в тех поддержку FTDI, благо они своевременно и по делу отвечают. Можно было бы и не писать, если бы я сразу раскрыл этот архив. Но полезными оказались ссылки, которые они прислали, примеры разводки и схемотехники.
Как показано выше, с помощью утилиты pscp из комплекта PuTTY, копируем на сервер драйвер. У меня был d3xx-linux-i686-0.5.0.tar.bz2. Заходим в эту папку на сервере и делаем
tar -xvf d3xx-linux-i686-0.5.0.tar.bz2

Далее, по инструкции FTDI исполняем:
cd linux-i686
sudo rm -f /usr/lib/libftd3xx.so
sudo cp -f libftd3xx.so /usr/lib
sudo cp -f libftd3xx.so.0.5.0 /usr/lib
sudo cp -f 51-ftd3xx.rules /etc/udev/rules.d
sudo udevadm control --reload-rules


Компилируем программу
Программу я писал в codeBlocks и она, за исключением некоторых моментов, в том числе и из-за FTDI, совместима с Windows. Перестала она компилироваться для Windows, когда я поставил туда h файл из драйвера для linux D3XX (c D2XX и FT232RL чипом не было проблем).
Теперь makefile. Программисты и так знают зачем он нужен, просто укажу особенности:
STATLIB=libftd3xx.a — указываем название библиотеки для Linux
CFLAGS=$(DEPENDENCIES) -Wall -Wextra -std=c++11 — разрешаем c++11
пример makefile для одного файла проекта main.cpp
CC=g++
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
	DEPENDENCIES := -lpthread -ldl -lobjc -framework IOKit -framework CoreFoundation
else
	DEPENDENCIES := -lpthread -ldl -lrt
endif
CFLAGS=$(DEPENDENCIES) -Wall -Wextra -std=c++11
STATLIB=libftd3xx.a
APP = prgr

all: $(APP)

$(APP): main.o 
	$(CC) -o $(APP) main.o $(STATLIB) $(CFLAGS)

main.o: main.cpp
	$(CC) -c -o main.o main.cpp $(CFLAGS)

clean:
	rm -f *.o ; rm $(APP)

Таким образом, в папке проекта лежат файлы main.c ftd3xx.h makefile libftd3xx.a и прочие файлы проекта. И для компиляции проекта теперь нужно зайти в эту папку и сделать
make


daemon
Роль демонов в Linux похожа на роль служб в Windows. Это программы, которые живут в фоновом режиме и делают всякую периферическую работу. Всё начинается с systemd — программы, которая запускается первой, имеет соответственно PID=1, и запускает все другие фоновые задачи через скрипты. Поковырявшись в Ubuntu, я удивился насколько много всего внутри построено на скриптах. Вы уже видели, как меняется статический IP, настраиваются программы, простой коррекцией файлов в текстовом редакторе.

Выполнив
ps -el
Вы увидите список запущенных программ с их PID. PID — это обязательный идентификатор запущенной программы, по которому за этой программой следит ОС. В крайней ситуации, можно грохнуть программу по этому PID из терминала — пишите kill и PID (может пригодиться для отладки).

Так вот, при запуске демона исполняется скрипт, в котором указано когда и как программу нужно запустить, как её остановить или перезапустить. Чтобы демон работал нормально, программа при запуске должна сохранить в файл, путь к которому указан в скрипте, свой PID и перейти в другой поток (в фоновый режим).

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

код daemon
void SetPidFile(char* Filename)
{
    FILE* f;
    f = fopen(Filename, "w+");
    if (f)
    {
        fprintf(f, "%u", getpid());
        fclose(f);
    }
}


int main( int argc, char** argv )
{
    char toolfile[32];
    char folder[32];
    intptr_t ret;
    FILE* logfile;

    if( argc!=3 )
    {
        printf( "Write address of the program and name: /home/ubuntu/tool/ tool\n");
        return -1;
    }
    pid_t pid, sid;

    pid = fork();
    if (pid < 0)
    {
        sleep(1);
        return -1;
    }
    //We got a good pid, Close the Parent Process
    if (pid > 0) {
        return 0;
    }
    //Create a new Signature Id for our child
    sid = setsid();
    if (sid < 0)
    {
        return -1;
    }
    //Change File Mask
    umask(0);
    //Save PID

    memcpy( toolfile, argv[0], strlen(argv[0]) );
    memcpy( toolfile + strlen(argv[0]), ".log", 4 );
    memset( toolfile + strlen(argv[0]) + 4, 0, 1 );
    printf( "Daemon:: log to:%s\n", toolfile );
    logfile = fopen( toolfile, "w" );

    fprintf( logfile, "Daemon:: started with 0=%s 1=%s 2=%s\n", argv[0], argv[1], argv[2] );

    memset( toolfile, 0, 32 );
    memcpy( toolfile, argv[0], strlen(argv[0]) );
    memcpy( toolfile + strlen(argv[0]), ".pid", 4 );
    memset( toolfile + strlen(argv[0]) + 4, 0, 1 );
    SetPidFile( toolfile );
    fprintf( logfile, "Daemon:: PID=%u saved in the %s\n", getpid(), toolfile );

    memset( folder, 0, 32 );
    memcpy( folder, argv[1], strlen(argv[1]) );
    fflush ( logfile );

    memset( toolfile, 0, 32 );
    memcpy( toolfile, folder, strlen(argv[1]) );
    memset( toolfile + strlen(argv[1]), '/', 1 );
    memcpy( toolfile + strlen(argv[1]) + 1, argv[2], strlen(argv[2]) );

    //Change Directory
    //If we cant find the directory we exit with failure.
    if ((chdir(folder)) < 0)
    {
        fprintf( logfile, "Daemon:: Program folder was not found:%s\n", folder );
        fclose( logfile );
        return -1;
    }

    //Close Standard File Descriptors
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);

    fprintf( logfile, "Daemon:: Program started\n" );
    fflush ( logfile );
    ret = execl( toolfile, folder, NULL );
    if( ret==-1 )
    {
        fprintf( logfile, "Daemon:: execl error: %s. File=%s Folder=%s\n", strerror(errno), toolfile, folder );
        fclose( logfile );
        return -1;
    }

    fprintf( logfile, "Daemon:: closed\n" );
    fclose( logfile );
    return 0;
}

Компилируем демона.

На данный момент в нашем рабочем каталоге, допустим /home/ubuntu/daemon/, имеем:

daemon — программа демона
prgr — программа
prgr.strt — скрипт, который нужно выполнить до запуска программы (если надо)
prgr.stop — скрипт, который нужно выполнить для остановки программы (если надо)

Нужно разрешить программам работать:

sudo chmod 755 ./tool
sudo chmod 755 ./daemon

Теперь нужен скрипт для systemd
Делаем
sudo nano /etc/systemd/system/mydaemon.service
и записываем туда наш скрипт.

mydaemon.service
[Unit]
Description=my service
After=network.target
After=isc-dhcp-server.service

[Service]
Type=forking
PIDFile=/home/ubuntu/daemon/daemon.pid
ExecStartPre=/bin/sh /home/ubuntu/daemon/prgr.strt
ExecStart=/home/ubuntu/daemon/daemon /home/ubuntu/daemon/prgr prgr
ExecStop=/bin/sh /home/ubuntu/daemon/prgr.stop
Restart=always
TimeoutSec=5

[Install]
WantedBy=multi-user.target

В скрипте кратко:
Description — краткое название-описание
After — что требуется для запуска
Type=forking — systemd предполагает, что служба запускается однократно и процесс разветвляется с завершением родительского процесса.
PIDFile — файл с PID программы из демона
ExecStartPre — до запуска программы
ExecStart — программа с параметрами
ExecStop — как остановить
WantedBy — уровень, на котором запускать

Теперь можно попробовать всё запустить.
Пишем
systemctl daemon-reload
systemctl status mydaemon

Если всё без ошибок, то запускаем:
systemctl start mydaemon

После этого смотрите лог демона, всё должно поехать.
Чтобы работала автозагрузка
systemctl enable mydaemon


Друзья, жду Ваших интересных комментариев и ценных советов. Если где соврал — сильно не гневайтесь, просто указывайте, что можно сделать лучше. Надеюсь, появится 3 часть про большой скрипт, автоустановку… может ещё что.
Tags:
Hubs:
0
Comments 3
Comments Comments 3

Articles