DNK_C_C++_Go_Rust
45 subscribers
14 photos
45 links
DNK - дневник кодера С и С++
Download Telegram
29. Стремитесь, чтобы программа была безопасна относительно исключений.
CЛЕДУЕТ ПОМНИТЬ:
— Безопасные относительно исключений функции не допускают утечки ресурсов и повреждения структур данных, даже в случае возбуждения исключений.
Такие функции предоставляют базовую гарантию, строгую гарантию либо гарантию полного отсутствия исключений.

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

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



30. Тщательно обдумывайте использование встроенных функций.
CЛЕДУЕТ ПОМНИТЬ:
— Делайте встраиваемыми только небольшие, часто вызываемые функции.
Это облегчит отладку, даст возможность выполнять обновления библиотек на двоичном уровне, уменьшит эффект "разбухания" кода и поможет повысить быстродействие программы.

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



31. Уменьшайте зависимости файлов при компиляции.
CЛЕДУЕТ ПОМНИТЬ:
— Основная идея уменьшения зависимостей на этапе компиляции состоит в том, чтобы заменить зависимость от определения зависимостью от объявления.
Эта идея лежит в основе двух подходов: классов-дескрипторов и интерфейсных классов.

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



32. Используйте открытое наследование для моделирования отношения «является».

CЛЕДУЕТ ПОМНИТЬ:
— Открытое наследование означает «является». Все, что применимо к базовому классу, должно быть применимо также и к производным от него классам, потому что каждый объект производного класса является также объектом базового класса.



33. Не скрывайте унаследованные имена.
CЛЕДУЕТ ПОМНИТЬ:
— Имена в производных классах скрывают имена из базовых классов.
При открытом наследовании это всегда нежелательно.

— Чтобы сделать скрытые имена видимыми, используйте using-объявления либо перенаправляющие функции.



34. Различайте наследование интерфейса и наследование реализации.
CЛЕДУЕТ ПОМНИТЬ:
— Наследование интерфейса отличается от наследования реализации.
При открытом наследовании производные классы всегда наследуют интерфейсы базовых классов.

— Чисто виртуальные функции означают, что наследуется только интерфейс.

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

— Невиртуальные функции означают, что наследуются интерфейс и обязательная реализация.



35. Рассмотрите альтернативы виртуальным функциям.

CЛЕДУЕТ ПОМНИТЬ:
— К числу альтернатив виртуальным функциям относятся идиома NVI и различные формы паттерна проектирования "Стратегия".
Идиома NVI сама по себе - это пример реализации паттерна "Шаблонный Метод".

— Недостаток переноса функциональности из функций-членов вовне класса заключается в том, что функциям-нечленам недостает прав доступа к закрытым членам класса.

— Объекты tr1::function работают как обобщенные указатели на функции.
Такие объекты поддерживают все вызываемые сущности, совместимые с сигнатурой целевой функции.



36. Никогда не переопределяйте наследуемые невирrуальные функции.

CЛЕДУЕТ ПОМНИТЬ:
— Никогда не переопределяйте наследуемые невиртуальные функции.



37. Никогда не переопределяйте наследуемое значение аргумента функции по умолчанию.

CЛЕДУЕТ ПОМНИТЬ:
— Никогда не переопределяйте наследуемые значения аргументов по умолчанию, потому что аргументы по умолчанию связываются статически, тогда как виртуальные функции (а только их и можно переопределять) динамически.



38. Моделируйте отношение «содержит» или «реализуется посредством» с помощью композиции.

CЛЕДУЕТ ПОМНИТЬ:
— Семантика композиции кардинально отличается от семантики открытого наследования.

— В предметной области композиция означает "содержит".
В области реализации она означает "реализовано посредством".
39. Продумывайте подход к использованию закрытого наследования.
CЛЕДУЕТ ПОМНИТЬ:
— Закрытое наследование означает "реализован посредством".
Обычно этот вариант хуже композиции, но все же приобретает смысл, когда производный класс нуждается в доступе к защищенным членам базового класса или должен переопределять унаследованные виртуальные функции.

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



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

CЛЕДУЕТ ПОМНИТЬ:
— Множественное наследование сложнее одиночного.
Оно может привести к неоднозначности и необходимости применять виртуальное наследование.

— Цена виртуального наследования - дополнительные затраты памяти, снижение быстродействия и усложнение операций инициализации и присваивания.
На практике его разумно применять, когда виртуальные базовые классы не содержат данных.

— Множественное наследование вполне законно.
Один из сценариев включает комбинацию открытого наследования интерфейсного класса и закрытого наследования класса, помогающего в реализации.



41. Разберитесь в том, что такое неявные интерфейсы и полиморфизм на этапе компиляции.

CЛЕДУЕТ ПОМНИТЬ:
— И классы, и шаблоны поддерживают интерфейсы и полиморфизм.

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

— Для параметров шаблонов интерфейсы неявны и основаны на корректных выражениях.
Полиморфизм проявляется во время компиляции - через конкретизацию и разрешение перегрузки функций.



42. Усвойте оба значения ключевого слова typename.

CЛЕДУЕТ ПОМНИТЬ:
— В объявлениях параметров шаблона ключевые слова class и typename взаимозаменяемы.

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



43. Необходимо знать, как обращаться к именам в шаблонных базовых классах.

CЛЕДУЕТ ПОМНИТЬ:
— В шаблонах производных классов ссылки на имена из шаблонов базовых классов осуществляются с помощью префикса "this->". using-объявления либо посредством явного указания базового класса.



44. Размещайте независимый от параметров код вне шаблонов.

CЛЕДУЕТ ПОМНИТЬ:
— Шаблоны генерируют множество классов и функций, поэтому любой встречающийся в шаблоне код, который не зависит от параметров шаблона, приводит к разбуханию кода.

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

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



45. Разрабатывайте шаблоны функций-членов так, чтобы они принимали "все совместимые типы".

CЛЕДУЕТ ПОМНИТЬ:
— Используйте шаблонные функции-члены для генерации функций, принимающих все совместимые типы.

— Если вы объявляете шаблоны обобщенных конструкторов копирования или обобщенного оператора присваивания, то по-прежнему должны объявить обычный конструктор копирования и оператор присваивания.



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

CЛЕДУЕТ ПОМНИТЬ:
— Когда пишете шаблон класса, в котором есть функции, нуждающиеся в неявных преобразованиях типа для всех параметров, определяйте такие функции как друзей внутри шаблона класса.



47. Используйте классы-характеристики для предоставления информации о типах.

CЛЕДУЕТ ПОМНИТЬ:
— Классы-характеристики делают доступной информацию о типах во время компиляции.
Они реализованы с применением шаблонов и их специализаций.

— В сочетании с перегрузкой классы-характеристики позволяют проверять типы во время компиляции.
48. Изучите метапрограммирование шаблонов.
CЛЕДУЕТ ПОМНИТЬ:
— Метапрограммирование шаблонов позволяет перенести часть работы со стадии исполнения на стадию компиляции. 
За счет этого можно раньше обнаружить ошибки и повысить производительность программ.

— Технология ТМР может быть использована для генерации кода на основе комбинации политик, а также чтобы предотвратить генерацию кода, некорректного для определенных типов данных.



49. Разберитесь в поведении обработчика new.

CЛЕДУЕТ ПОМНИТЬ:
— set_new_handler позволяет указать функцию, которая должна быть вызвана, если запрос на выделение памяти не может быть удовлетворен.

— Полезность nothrow new ограничена, поскольку эта форма применима только для выделения памяти;
последующие вызовы конструктора могут по-прежнему возбуждать исключения.



50. Когда имеет смысл заменять new и delete.

CЛЕДУЕТ ПОМНИТЬ:
— Есть много причин для написания специальных версий new и delete, включая повышение производительности, отладку ошибок при работе с кучей, а также сбор информации об использовании памяти.



51. Придерживайтесь принятых соглашений при написании new и delete.

CЛЕДУЕТ ПОМНИТЬ:
— Оператор new должен содержать бесконечный цикл, который пытается выделить память, должен вызывать функцию-обработчик new, если не удается удовлетворить запрос на выделение памяти, и должен обрабатывать запрос на выделение нуля байтов.
Версии оператора new уровня класса должны обрабатывать запросы на выделение блоков большего размера, чем ожидается.

— Оператор delete не должен ничего делать при передаче ему нулевого указателя.
Версии оператора delete уровня класса должны обрабатывать запросы на освобождение блоков, которые больше, чем ожидается.



52. Если вы написали оператор new с размещением, напишите и соответствующий оператор delete.

CЛЕДУЕТ ПОМНИТЬ:
— Когда пишете размещающую версию оператора new, убедитесь, что не забыли о соответственном размещающем операторе delete.
Если его не будет, то в программе могут возникать тонкие, трудноуловимые утечки памяти.

— Объявляя размещающие версии new и delete, позаботьтесь о том, чтобы нечаянно не скрыть нормальных версий этих функций.



53. Обращайте внимание на предупреждения компилятора.

CЛЕДУЕТ ПОМНИТЬ:
— Принимайте всерьез предупреждения компилятора и старайтесь добиться того, чтобы ваш код вообще не вызывал предупреждений, даже при задании максимального уровня диагностики.

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



54. Ознакомьтесь со стандартной библиотекой, включая TR1.

CЛЕДУЕТ ПОМНИТЬ:
—  Основная функциональность стандартной библиотеки С++ состоит из STL, потоков iostream и локалей. Также включена стандартная библиотека С99.

— TR1 добавляет поддержку "интеллектуальных" указателей (например, tr1::shared_ptr), обобщенных указателей на функции (tr1::function), кэшированных контейнеров, регулярных выражений и еще 10 компонентов.

— Отчет TR1 сам по себе - всеrо лишь спецификация.
Чтобы воспользоваться преимуществами TR1, понадобится реализация. Одним из источников реализаций компонентов TR1 является проект Boost.



55. Познакомьтесь с Boost
.
CЛЕДУЕТ ПОМНИТЬ:
— Boost - сообщество и WеЬ-сайт для разработки бесплатных библиотек на С++ с открытыми исходными текстами, подверrающихся публичному обсуждению.
Boost оказывает немалое влияние на процедуру стандартизации С++.

— Boost предоставляет реализацию многих компонентов TR1, но - кроме того - и множество друrих библиотек.
#505_C_Cpp_GO_RUST_NTWK

Веб сервер, что это такое, для чего он нужен, какие задачи должен выполнять?
Примеры веб - серверов на С, С++, Rust, Golang.


Веб-сервер — что это?

Веб-серверпрограмма или система программного обеспечения, которая принимает запросы от клиентов (обычно браузеров), обрабатывает их и возвращает запрошенные ресурсы (например, HTML-документы, изображения, файлы JavaScript и CSS).

Основная задача веб-сервера заключается в обеспечении обмена информацией между клиентом и сервером посредством протокола HTTP/HTTPS.


Основные задачи, выполняемые веб-сервером:
Приём запросовполучение входящего запроса от клиента (браузера).
Обработка запросовпроверка формата запроса, авторизация пользователя, определение запрашиваемого ресурса.
Генерация ответовформирование содержимого страницы, обработка динамических страниц, отправка статического контента (HTML-файлы, изображения, скрипты).
Отправка ответов клиентупередача сформированного ответа обратно клиенту согласно протоколу HTTP/HTTPS.
Безопасностьобеспечение защиты сервера и передаваемых данных, аутентификация пользователей, шифрование передачи данных (SSL/TLS).
Логированиезапись действий и ошибок в журнал для последующего анализа и устранения проблем.
Управление нагрузкойраспределение нагрузки среди нескольких серверов (балансировка нагрузки), кэширование часто используемых ресурсов для повышения производительности.
Оптимизация скорости сжатие файлов перед передачей, использование асинхронности обработки запросов, кеширование результатов запросов.


Пример простого веб-сервера на C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080

void handle_client(int client_socket) {
char buffer[1024];
ssize_t bytes_read = recv(client_socket, buffer, sizeof(buffer)-1, 0);
if(bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Received request:\n%s\n", buffer);

// Send a simple response back to the client
const char* response =
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
"<html><body><h1>Hello from C Web Server!</h1></body></html>";
send(client_socket, response, strlen(response), 0);
}

close(client_socket); // Close connection after sending response
}

int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);

// Create socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}

// Forcefully attaching socket to port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}

address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// Bind the socket to the specified IP and port
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0 ) {
perror("bind failed");
exit(EXIT_FAILURE);
}

// Listen for incoming connections
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}

while(1) { // Accept multiple clients
if ((new_socket = accept(server_fd, (struct sockaddr *)NULL, NULL)) < 0) {
perror("accept");
continue;
}

handle_client(new_socket);
}

return 0;
}


Этот пример демонстрирует простой веб-сервер на языке C, который слушает порт 8080, принимает входящие соединения и возвращает простейшую страницу с текстом "Hello from C Web Server!".
Пример простого веб-сервера на C++:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string>
#include <arpa/inet.h>

using namespace std;

const string default_response =
"RFC 2616 compliant HTTP Response\r\n"
"HTTP/1.1 200 OK\r\n"
"Server: Simple C++ Web Server\r\n"
"Content-Length: 26\r\n"
"\r\n"
"<h1>Hello from C++!</h1>\r\n";

// Handle incoming requests
void handleRequest(int sockfd) {
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
ssize_t n = read(sockfd, buffer, sizeof(buffer)-1);
cout << "Client Request:" << endl << buffer << endl;

write(sockfd, default_response.c_str(), default_response.length());
close(sockfd);
}

int main() {
int listenfd, connfd;
struct sockaddr_in servaddr;

listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(8080);

bind(listenfd, reinterpret_cast<const sockaddr*>(&servaddr), sizeof(servaddr));
listen(listenfd, 10);

while(true) {
connfd = accept(listenfd, nullptr, nullptr);
handleRequest(connfd);
}

return 0;
}


Здесь представлен веб-сервер на C++, также возвращающий простую HTML-страницу с сообщением "Hello from C++!". Сервер работает на порте 8080.


Пример простого веб-сервера на Rust:
Rust является популярным языком для разработки высокопроизводительных приложений благодаря своей безопасности памяти и эффективности исполнения.
Для написания веб-сервера в Rust удобно воспользоваться библиотекой actix-web:
use actix_web::{web, App, HttpServer, Responder};

async fn index() -> impl Responder {
"Hello from Rust!"
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
println!("Starting web server on http://localhost:8080");
HttpServer::new(|| {
App::new().route("/", web::get().to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}


Этот код создает простое приложение с использованием фреймворка Actix Web, которое прослушивает адрес localhost:8080 и выводит сообщение "Hello from Rust!" при обращении по адресу /.


Пример простого веб-сервера на Go (Golang):
Go отлично подходит для быстрого запуска простых и эффективных веб-сервисов.

Простой пример на Go выглядит следующим образом:

package main
import (
"fmt"
"log"
"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from Go!")
}

func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}


Данный скрипт запускает простой веб-сервер, доступный по адресу localhost:8080. При заходе на главную страницу возвращается простая строка "Hello from Go!".


Веб-серверы являются основой современных интернет-приложений и обеспечивают взаимодействие клиентских устройств с ресурсами, размещёнными на серверах.
Выбор языка зависит от требований проекта: C/C++ подойдут для низкоуровневых решений, Rust обеспечит безопасность памяти и высокую производительность, Go удобен для быстрой разработки и масштабирования.
#506_C_PRGM

Программка для перевода строки представляющей шестнадцатиричное число в строковом представлении в строку двоичных кодов. Т.е. нам задается шестнадцатиричное число в виде 0x1234567890abcdf или 0X12fde... или fffde4532a1eb. Длина входной строки до 100 символов, а на выходе мы имеем двоичное представление данного числа.

Не захотел использовать массив с представлением каждой шестнадцатиричной цифры и выполнил преобразование с помощью битовых операций.

// hex_to_bin.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

void digit_to_bin(const char digit) {
int cp_digit;
if (digit > 47 && digit < 58) {
cp_digit = digit - 48;
} else if (digit > 96 && digit < 103) {
cp_digit = digit - 87;
} else {
perror("Incorrect input");
exit(0);
}

for (int i = 3; i >= 0; --i) {
printf("%d", (cp_digit >> i) & 1);
}
}

void hex_to_bin(const char* hex_str_num) {
for (int i = 0; i != strlen(hex_str_num); ++i) {
digit_to_bin(tolower(hex_str_num[i]));
}
putchar('\n');
}

void read_line(char* buffer, size_t buffer_size) {
const char* input = fgets(buffer, buffer_size, stdin);

if (NULL != input) {
char* end_ptr = strchr(buffer, '\n');
if (NULL != end_ptr) {
*end_ptr = '\0';
}
}
}

char* prefix_ignore(char* input) {
char* result = NULL;
if (input[0] == '0' && tolower(input[1]) == 'x') {
result = &input[2];
}
return result;
}

int main(void) {
enum {SIZE = 101};
char input[SIZE] = {0};

read_line(input, SIZE);
//puts(input);

if (strlen(input)) {
const char* hex_ptr = prefix_ignore(input);
if (NULL != hex_ptr) {
hex_to_bin(prefix_ignore(input));
} else {
hex_to_bin(input);
}
}

return 0;
}

Если у кого есть идеи по оптимизации с радостью выслушаю.
#507_PPPO_TP

Что такое динамические классы?

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

Это отличается от статического подхода, когда структура классов фиксируется заранее, на этапе компиляции, и неизменна в процессе выполнения программы.


Примеры реализации динамических классов.

Python поддерживает создание классов динамически благодаря встроенным инструментам метаклассов (type(), __new__):
# Динамическое создание класса Person
Person = type('Person', (), {'name': 'John'})
john = Person()
print(john.name)


JavaScript позволяет переопределять методы объекта даже после его создания:
let obj = {};
obj.sayHello = function () {
console.log("Привет!");
};
obj.sayHello();


Ruby широко известен своей поддержкой изменения поведения классов во время выполнения («метапрограммирование»):
class Animal
def speak
puts "Р-р!"
end
end

Animal.class_eval do
define_method(:roar) { |volume| puts "#{volume} Р-Р-Р!!!" }
end

lion = Animal.new
lion.roar("Громко")


Преимущества динамических классов:
Гибкостьвозможность адаптации приложения к новым требованиям прямо во время работы.
Расширяемостьпростое внедрение новых типов и методов без остановки системы.
Простота тестированиялегче проверять и экспериментировать с новыми классами.

Важно помнить, что чрезмерное использование динамического создания классов усложняет поддержку и понимание кода, особенно для новичков.
#508_Cpp_GO_RUST_PPPO_TP

Динамические классы или аналоги в С++, Rust, Golang.

В ЯП C++, Rust и Golang отсутствуют механизмы прямого создания динамических классов аналогично Python или Ruby, поскольку они являются языками с сильной типизацией и строгими правилами компиляции.
Однако существуют альтернативные подходы, позволяющие реализовать похожее поведение, хотя и с ограничениями.


C++: Хотя создание классов во время выполнения невозможно, можно воспользоваться несколькими подходами для достижения гибкости:

Методы расширения функциональности:
Шаблоныгенерация семейств классов на стадии компиляции.
Наследование и полиморфизмсоздание иерархий классов с виртуальными методами.
RTTI (Run-time Type Information)получение информации о классе во время выполнения (например, оператор dynamic_cast).
Фреймворки для Reflectionбиблиотеки вроде libclang позволяют анализировать структуру классов на уровне бинарников.

Пример использования шаблона для динамической обработки разных типов:
template <typename T>
void process(T value) {
std::cout << "Обработка типа: " << typeid(value).name() << "\n";
}

process<int>(42); // Обработаем целое число
process<double>(3.14); // Обработаем вещественное число


Rustязык с безопасной моделью памяти и отсутствием наследования в традиционном смысле.
Вместо него используются структуры и трейт-интерфейсы.

Основные подходы:
Traits (черты)определение интерфейса для структур, позволяющего применять различные реализации методов к разным типам.
Generics (обобщенные типы)шаблонная система позволяет создавать универсальные алгоритмы и контейнеры, работающие с разными типами данных.
Boxed traitsиспользование динамического диспетчеринга (аналог виртуальных функций), где поведение выбирается на этапе выполнения.

Пример использования trait и boxed trait:
trait Shape {
fn area(&self) -> f64;
}

struct Circle(f64);
impl Shape for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.0.powi(2)
}
}

fn print_area(shape: &dyn Shape) {
println!("Площадь фигуры: {}", shape.area());
}

// Используем circle
let c = Circle(3.0);
print_area(&c);


Golangимеет простую и минималистичную систему типов и наследования.
Поддерживает интерфейсное программирование, что делает возможным частичное достижение динамичности:
Интерфейсописывает набор методов, которыми должен обладать объект, поддерживающий этот интерфейс.
Отражение (Reflection)пакет reflect позволяет получать информацию о типах переменных во время выполнения и вызывать методы динамически.

Пример использования интерфейса:
package main

import "fmt"

type Shaper interface {
Area() float64
}

type Rectangle struct {
width, height float64
}

func (r Rectangle) Area() float64 {
return r.width * r.height
}

func main() {
r := Rectangle{width: 10, height: 5}
fmt.Println(r.Area()) // Площадь прямоугольника
}


Использование отражения (reflection):
package main

import (
"fmt"
"reflect"
)

type MyStruct struct{}

func (m MyStruct) SayHi() string {
return "Привет!"
}

func main() {
myInstance := MyStruct{}
methodValue := reflect.ValueOf(myInstance).MethodByName("SayHi").Call(nil)[0]
fmt.Println(methodValue.String())
}


Таким образом, несмотря на отсутствие полноценного механизма динамических классов, в каждом из указанных языков существует ряд подходов, позволяющих достичь требуемого уровня гибкости и расширяемости приложений.
#509_ADM_DvOp_NTWK

Если сервер не отвечает, как можно получить доступ к серверу, не находясь непосредственно в ЦОДе?


Cуществует два варианта:

KVM (Keyboard, Video & Mouse)аппаратное средство удаленного управления компьютером через подключение клавиатуры, монитора и мыши.
Позволяет получать доступ даже тогда, когда ОС сервера зависла или вообще не загружается.

IPMI (Intelligent Platform Management Interface) интерфейс удалённого администрирования, позволяющий управлять оборудованием сервера независимо от состояния операционной системы.
Через IPMI также можно перезагрузить сервер, изменить настройки BIOS и многое другое.
#510_ADM_DvOp_TOS

Что такое inode?

При помощи какой команды можно посмотреть номер inode файла?

Inode (от англ. index node)структура данных файловых систем Unix-подобных ОС, содержащая метаданные файла или каталога.
Проще говоря, каждый файл и каталог в UNIX имеет связанный с ним уникальный номер inode, содержащий важную информацию о файле, такую как права доступа, владелец, размер, время последнего изменения и расположения блоков данных на диске.


Основные компоненты inode включают:
— Номер inode (идентификатор).
— Тип объекта (обычный файл, директорию, символьную ссылку и др.).
— Права доступа и владельца.
— Временные отметки (дата создания, модификации, последнего доступа).
— Размер файла и количество ссылок (hard links).
— Информация о расположении данных (блоки хранения на устройстве).



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


Примеры команд для работы с inode:

— Показать список файлов вместе с номерами inode:
ls -i


— Найти файлы по номеру inode:
find /path/to/directory -inum <номер_inode> -print


Понимание концепции inode полезно при работе с системой и диагностике проблем, связанных с хранением и организацией файлов.


Команда stat отображает подробную информацию о файле, включая его номер inode.

Пример использования:
stat file_name


Результат покажет много деталей о файле, среди которых будет и поле "Inode" с соответствующим значением.
#511_ADM_DvOp_TOS

Как узнать номер у процесса SIGKILL?
Как узнать номер процесса systemd?


Номер сигнала SIGKILL в ОС Linux — 9.
Этот сигнал используется для немедленного завершения процесса без возможности обработки самим процессом.
Процесс не может перехватывать или игнорировать этот сигнал, поэтому он гарантированно завершит процесс.



Для проверки номера сигнала в ОС Linux можно воспользоваться командой:
kill -l | grep SIGKILL


или посмотреть полный список сигналов командой:
man 7 signal


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


Как узнать номер процесса systemd?

Способ 1: Через команду ps:
ps aux | grep systemd


Получаем список процессов, связанных с системой инициализации, среди которых первым должен идти процесс systemd. Его PID находится в первом столбце.
Пример вывода команды:
root       1  0.0  0.0   7880  1804 ?        Ss   Aug11   0:01 /usr/lib/systemd/systemd --switch-root=/run/initramfs/initrd.img
...


Здесь первый столбец — это именно PID.

Способ 2: Через утилиту pidof, которая сразу выдаст нужный вам PID:
pidof systemd


Эта команда выводит PID основного процесса systemd.

Таким образом, самый простой способ быстро посмотреть PID процесса systemd — использовать команду pidof systemd.
#512_ADM_DvOp_TOS

Что такое systemd в Linux?

Systemd система инициализации и управления сервисами, разработанная для ОС семейства Linux.
Она заменила собой традиционные системы запуска служб типа SysV init и Upstart, став основой большинства современных дистрибутивов Linux, включая Ubuntu, Debian, Fedora, CentOS и многие другие.


Основные возможности Systemd.
Инициализация системыуправление запуском сервисов и демонов при загрузке операционной системы.
Управление устройствамиотслеживание состояния устройств и запуск соответствующих драйверов и приложений при подключении новых аппаратных компонентов.
Журналированиеведение журналов событий и сообщений от различных служб и приложений, что упрощает диагностику проблем.
Контроль ресурсоввозможность ограничения потребления ресурсов (процессора, памяти, ввода-вывода) отдельными приложениями или группами процессов.
Мониторинг зависимостейавтоматическое управление последовательностью загрузки и запуска служб, обеспечивая правильное выполнение зависимых друг от друга операций.
Параллельный запуск сервисовблагодаря поддержке параллельного запуска нескольких сервисов одновременно, ускоряется общая скорость загрузки ОС.
Автоматический перезапускслужба автоматически пытается восстановить работу приложения, если оно завершилось аварийно.


Структура Systemd.

Система построена вокруг трех основных понятий:
Единицы (units)абстрактное представление любого ресурса, управляемого systemd (например, службы, устройства, монтирования файловых систем).
Цель (target)группа единиц, объединенных общей задачей (например, сеть, графическая среда).
Демон (daemon) — основной исполняемый процесс systemd, который управляет всеми единицами и целями.


Для взаимодействия с systemd используются различные команды:

Запустить службу:
sudo systemctl start <служба>.service


Остановить службу:
sudo systemctl stop <служба>.service


Перезагрузить систему:
sudo systemctl reboot


Проверить статус сервиса:
sudo systemctl status <служба>.service


Отключить автозапуск службы:
sudo systemctl disable <служба>.service



Несмотря на критику из-за сложности и объема функций, systemd стала стандартом для многих крупных дистрибутивов Linux благодаря своей универсальности и эффективности.
#513_ADM_DvOp_TOS

За что отвечает процесс с PID = 0?

Процесс с номером PID = 0называется процессом нулевого уровня и обозначается swapper или scheduler.
Этот процесс является виртуальным процессом ядра операционной системы и отвечает за:
— Планирование задач (scheduling) и переключение между потоками выполнения.
— Управление памятью и
свопингом страниц («swapping») между оперативной памятью и диском, когда память занята.
— Внутреннее обслуживание и координацию потоков внутри ядра.

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


Процесс с PID = 0 является неотъемлемой частью функционирования ядра Linux и обеспечивает базовую инфраструктуру планирования задач и распределения ресурсов.
🔥1
#514_ADM_DvOp_TOS

С помощью чего общаются процессы в Linux?

Процессы в ОС Linux могут взаимодействовать друг с другом различными способами.


Основные методы межпроцессного взаимодействия (IPC — Inter Process Communication):


1. Каналы (Pipes)простейший способ связи между процессами — каналы.
Каналы бывают двух видов:

— Анонимные каналыиспользуются для передачи данных между родителем и дочерними процессами.
Именованные каналы (FIFO)позволяют обмениваться информацией между произвольными процессами, даже если они запущены разными пользователями.

2. Сигналы (Signals)асинхронное уведомление процессов об определенных событиях.
Например, сигнал SIGTERM используется для завершения процесса, SIGKILL принудительно прекращает выполнение программы.

3. Разделяемая память (Shared Memory)процессы могут совместно использовать область памяти, что позволяет быстро передавать большие объемы данных.

4. Сообщения (Message Queues)механизм отправки сообщений от одного процесса другому через очередь сообщений ядра ОС.

5. Семафоры (Semaphores)используются для синхронизации действий нескольких процессов.
Они помогают предотвратить одновременный доступ разных процессов к общим ресурсам.

6. Socket-файлы (Unix Domain Sockets)позволяют осуществлять передачу данных между процессами внутри одной системы посредством файлов сокетов.

7. FIFOs (Named Pipes)аналогично именованным каналам, обеспечивают двунаправленную связь между двумя процессами.


Эти механизмы широко применяются в различных приложениях Unix-подобных ОС, включая Linux, обеспечивая гибкость и эффективность межпроцессного взаимодействия.
#515_ADM_DvOp_TOS

Сигналы SIGINT, SIGTERM, SIGHUP, SIGKILL в Linux?


1. SIGINT (Signal Interrupt)посылается процессу, когда пользователь нажимает комбинацию клавиш Ctrl+C.
Обычно обрабатывается процессом для плавного прекращения своей работы. По умолчанию вызывает завершение процесса.

Пример: программа читающая ввод с клавиатуры, прерываемая нажатием Ctrl+C, должна завершить свою работу аккуратно, сохранив состояние или завершив активные операции.


2. SIGTERM (Signal Terminate)cамый распространенный сигнал для завершения процесса. Часто отправляется командой kill с параметром по умолчанию (kill <PID>).
Обрабатывая этот сигнал, приложение имеет возможность очистить ресурсы перед завершением. Если игнорируется или блокируется, процесс продолжает выполняться.
Используется для мягкого завершения процессов, позволяя завершить активность плавно.


3. SIGHUP (Signal Hangup)изначально отправлялся терминалом при разрыве соединения (например, закрытии сессии SSH).
Сейчас часто используется для перезагрузки конфигураций приложений.
Многие демоны и серверы реагируют на этот сигнал путем перезагрузки конфигурации без остановки службы.
Иногда также применяется для полного завершения приложения, аналогично
SIGTERM.


4. SIGKILL (Signal Kill)не перехватывается и не игнорируется ни одним процессом.
Остановка осуществляется немедленно, независимо от состояния обработки сигналов.
Посылка
SIGKILL останавливает процесс сразу же, без возможности очистки ресурсов (закрытия файлов, освобождения памяти и т.п.).
Запускается командой kill -9 <PID>. Применяется, когда обычные сигналы не приводят к остановке процесса (например, зависания или блокировки).


Каждый из этих сигналов играет важную роль в управлении поведением процессов в Linux.
Правильное использование сигналов помогает контролировать поведение приложений, обеспечивать аккуратное завершение работы и восстановление ресурса.
#516_ADM_DvOp_TOS

Что такое Load average?

Load Averageметрика производительности Unix-подобных ОС (Linux, FreeBSD), отражающая среднюю нагрузку процессора за определенный промежуток времени.
Показывает количество процессов, ожидающих выполнения на CPU, включая процессы в состоянии ожидания ввода-вывода (I/O wait).


Как рассчитывается?
Обычно выводится три значения нагрузки средней за промежутки времени:
За последнюю минуту (loadavg_1min)
За последние пять минут (loadavg_5min)
За последние пятнадцать минут (loadavg_15min)

Эти показатели помогают оценить, насколько загружены ресурсы системы и стабильна ли нагрузка во времени.

Например, команда uptime выводит load average следующим образом:
$ uptime
17:30:01 up 8 days, 3 hours, 2 users, load average: 0.15, 0.21, 0.25


Здесь:
0.15 — средняя загрузка за последнюю минуту,
0.21 — за последние пять минут,
0.25 — за последние пятнадцать минут.


Интерпретация значений.
Значение меньше единицы (например, 0.5) — система практически простаивает и свободна для новых задач.
Значение около единицы (например, 1.0)процессор загружен примерно на полную мощность одним процессом.
Большее значение, особенно значительно превышающее число ядер процессорасигнализирует о перегрузке системы (задержки, очереди задач).
Например, если у вас двухъядерная машина, а load average больше 2.0, это повод задуматься о снижении нагрузки.


Load Averageпомогает оперативно оценивать состояние загрузки сервера и вовремя реагировать на возможные проблемы производительности.
#517_ADM_DvOp_TOS

Как можно уберечь процесс от OOM-killer?

OOM-Killer (Out Of Memory Killer) механизм ядра Linux, предназначенный для аварийного завершения процессов в ситуации критической нехватки оперативной памяти.
Чтобы защитить важные процессы от убийства OOM-Killer'ом, можно предпринять следующие шаги:

1. Настройка параметра oom_score_adjкаждому процессу присваивается значение oom_score, которое определяет вероятность выбора процесса для уничтожения.
Чем выше этот показатель, тем вероятнее, что именно этот процесс станет жертвой OOM-Killer'a.
Можно вручную снизить вероятность для важных процессов путем изменения значения /proc/<PID>/oom_score_adj.

Значение oom_score_adj варьируется от -1000 до 1000:
-1000 — защищает процесс от убийств.
1000 — делает процесс наиболее привлекательным кандидатом для убийства.

Пример защиты процесса с PID=1234:
echo "-1000" > /proc/1234/oom_score_adj


Также, начиная с версии ядра 2.6.11, существует специальный флаг OOM_DISABLE (-17) для полной защиты процесса от убийств.
Пример настройки флага:
echo "-17" > /proc/1234/oom_score_adj



2. Использование системных переменных cgroupsконтейнеризация процессов позволяет ограничить потребление памяти отдельным приложением, что снижает риск возникновения OOM ситуаций глобально для всей системы.
Создавая группу контроля (cgroups), можно установить лимиты на доступную память.

Примеры ограничений:
# Создание группы control group
mkdir /sys/fs/cgroup/memory/myapp
# Установка лимита памяти для группы myapp (например, 1GB)
echo "1073741824" > /sys/fs/cgroup/memory/myapp/memory.limit_in_bytes
# Перемещение процесса в новую группу
echo "<PID>" > /sys/fs/cgroup/memory/myapp/tasks


Это позволит предотвратить чрезмерное потребление памяти одним процессом и избежать активизации OOM-Killer'a.


3. Оптимизация приложениянаиболее эффективный способ избежать OOM-Killer заключается в предотвращении утечек памяти и улучшении архитектуры приложений:
Мониторинг потребления памяти вашим приложением.
Профилирование и устранение утечек памяти.
Правильное управление памятью и освобождение неиспользуемых объектов.



4. Увеличение объема доступной памятииногда проще увеличить физическую оперативную память серверов или расширить область свопа, чтобы избежать исчерпания доступной RAM.
Однако злоупотребление swapping ухудшает производительность, поэтому лучше искать баланс между объемом ОЗУ и оптимизацией приложений.


5. Для сервиса, который управляется через systemd, есть возможность указать специальную опцию в systemd-unit файлеиспользование специальных опций systemd:
Сервисы, управляемые через systemd, могут указывать специальные опции в unit-файлах, такие как OOMScoreAdjust.
Эта настройка позволяет регулировать приоритет процесса относительно OOM-killer.

Пример конфигурации:
[Service]
OOMScoreAdjust=-1000


6. Записать определенное значение по пути /proc/{PID}/oom_adjкаждый процесс имеет настраиваемый параметр, определяющий его приоритет для OOM-killer.
Путем записи отрицательного значения в файл /proc/{PID}/oom_adj можно снизить вероятность выбора процесса для убийства.
Однако, начиная с ядра 2.6.36, рекомендуется использовать новый интерфейс /proc/{PID}/oom_score_adj, поддерживающий диапазон от -1000 до 1000.


Комбинируя изменение настроек OOM-киллера, ограничение потребления памяти через cgroups и профилирование ПО, можно существенно уменьшить шансы потерять важный процесс из-за нехватки памяти.
#518_ADM_DvOp_TOS

Какие типы записей хранятся в /etc/fstab?

Файл /etc/fstab — предназначен для хранения настроек автоматического монтирования файловых систем Linux при загрузке системы либо вручную командой mount.


Типичные записи в /etc/fstab.

Каждая запись состоит из шести полей, разделённых пробелами или табуляциями. Формат строки выглядит следующим образом:
<устройство> <точка_монтирования> <тип ФС> <опции> <dump> <pass>


Поля:
Устройство путь к устройству (например, /dev/sda1) или UUID устройства (UUID=...), метка диска (LABEL=...) или сетевой ресурс:
/dev/sdb1
UUID=f8a7c2b9-b2d5-42cd-a4f7-e7227054a678
LABEL=data_disk
//server/share


Точка монтированиякаталог, куда монтируется файловая система:
/mnt/data
/home/user


Тип файловой системытип используемой файловой системы (ext4, xfs, btrfs, ntfs, vfat и др.):
ext4
xfs
btrfs
swap
cifs
nfs
tmpfs
proc
sysfs
devtmpfs


Опции монтированиядополнительные опции для настройки поведения файловой системы при монтировании (например, права доступа, режим чтения/записи).
Некоторые часто используемые опции:
defaultsстандартные значения (rw, suid, dev, exec, auto, nouser, async)
roдоступ только для чтения
noexecзапрет запуска исполняемых файлов
nosuid игнорирование setuid/setgid флагов
_netdevустройство является сетевым ресурсом

Dump поле используется программой резервного копирования dump. Обычно имеет значение 0.
Passпорядок проверки файловой системы утилитой fsck. Значение 0 означает отсутствие проверки, 1 для корневой файловой системы, 2 для остальных.


Общие типы записей:

Локальные блочные устройства (HDD/SDD):
/dev/sda1     /               ext4      defaults       0          1


RAM-диски и временные файловые системы:
tmpfs           /run            tmpfs     mode=0755,nosuid,nodev,size=10% 0       0


SWAP-разделы:
/dev/sdc1       none            swap      sw                0       0


Сетевые ресурсы (NFS/CIFS):
//nas.example.com/home /mnt/nas_home cifs credentials=/root/.smbcreds,_netdev,rw,user 0 0


В /etc/fstab хранятся различные типы записей, определяющие правила монтирования локальных устройств, виртуальных ресурсов и сетевых хранилищ.
#519_ADM_DvOp_TOS

Что такое runlevels в Linux?

Runlevels («уровни исполнения») в Linuxразные состояния ОС, определяющие набор активных служб и демонов.
Каждый уровень определяет специфическое состояние системы и соответствующий набор сервисов, необходимых для функционирования в данном режиме.

Традиционно в классических дистрибутивах Linux (таких как Debian, Ubuntu до версии 15.04, CentOS/RHEL версий ниже 7.x) существовало семь уровней исполнения (от 0 до 6).
Однако начиная с появления systemd в большинстве современных дистрибутивов концепция runlevels была заменена единицами (units), хотя традиционная нумерация уровней всё ещё поддерживается ради обратной совместимости.

Oсновные традиционные уровни исполнения в Linux:
Уровень  Описание
0 Выключение компьютера
1 Однопользовательский режим
2–5 Много-пользовательские режимы
6 Перезагрузка компьютера



Подробности каждого уровня:
Уровень 0: Полное выключение системы.
Уровень 1: Система запускается в однопользовательском режиме, предназначенном преимущественно для обслуживания и устранения неполадок. Доступ ограничен одним администратором, графический интерфейс отсутствует.
Уровни 2–5: Стандартные много-пользовательские режимы, доступные пользователям одновременно. Уровни отличаются уровнем предоставляемых услуг.
К примеру, в некоторых системах:
Уровень 2 базовая сеть и службы;
Уровень 3 — полный многопользовательский режим с сетью;
Уровень 4 — зарезервирован и редко используется;
Уровень 5 — полная многозадачная среда с возможностью запуска X Window System (графического интерфейса).
Уровень 6: Перезагрузка системы.

Современные дистрибутивы используют систему инициализации systemd, которая позволяет гибко управлять сервисами независимо от традиционного разделения на runlevels.
Вместо старых init-скриптов теперь используются units (systemd единицы), каждая из которых управляет отдельным сервисом или процессом.
Несмотря на это, большинство дистрибутивов сохраняют возможность перехода между классическими уровнями исполнения через команду telinit или специальные команды типа init N (хотя последнее устарело и рекомендуется избегать его использования).


Для переключения между уровнями чаще всего применяются следующие инструменты:
Традиционные команды вроде init и telinit (устаревшие, но поддерживаются для совместимости);
Инструменты нового поколения, такие как команда systemctl isolate в systemd.

Пример команды для смены уровня в системе с systemd:
sudo systemctl isolate multi-user.target # эквивалент старого уровня 3



Runlevels позволяют гибко настраивать работу системы в зависимости от потребностей пользователей и администратора.
#520_DvOp_NTWK_TP

Какие методы запросов поддерживает HTTP?

HTTP-протокол поддерживает ряд методов запросов, каждый из которых служит определенной цели взаимодействия клиента и сервера.


Основные методы HTTP:

GETиспользуется для извлечения данных с сервера.
Не должен изменять состояние ресурса на сервере.

Безопасность считается безопасным методом, поскольку не вызывает побочных эффектов.
Идемпотентностьповторные запросы возвращают одинаковый результат.
Данные передаются в виде параметров в URL (строке запроса).

POST — применяется для отправки данных на сервер с целью обработки (создания новых ресурсов, передачи форм и т.п.).
Может вызывать изменения на стороне сервера.
Не безопасен и не идемпотентен.
Передаваемые данные помещаются в тело запроса.


PUT — предназначен для обновления существующего ресурса или создания нового ресурса по указанному URI.
Если ресурс существует, обновляет его целиком; если нет — создает новый.

Идемпотентен, повторные PUT-запросы приводят к одному и тому же состоянию ресурса.

DELETE — удаляет указанный ресурс на сервере.
Считается идемпотентным, так как повторные DELETE-запросы дадут тот же эффект (или ничего не сделают, если ресурс уже удалён).

HEAD — аналогичен GET, но возвращает только заголовки ответа, без тела документа.
Удобен для проверки доступности ресурса или выяснения размера файла перед скачиванием.

OPTIONS — запрашивает список методов, поддерживаемых указанным ресурсом.
Часто применяется для кросс-доменных запросов (CORS), чтобы проверить допустимые способы взаимодействия с ресурсом.

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

CONNECT — устанавливает туннель (обычно для прокси-серверов), часто используется для реализации HTTPS-туннелирования через прокси.

TRACE — возвращает сообщение запроса обратно клиенту, позволяя диагностировать проблемы связи.
Редко используется в обычной практике разработки веб-сервисов.


Дополнительная информация:
Методы POST, PUT и PATCH предназначены для внесения изменений на сервере и называются небезопасными.
Методы GET, HEAD и OPTIONS считаются безопасными, так как не вызывают изменений на сервере.
Методы GET, HEAD, PUT и DELETE являются идеmpoтентыми методами, потому что повторные вызовы этих методов приведут к одному и тому же результату.


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