Форум программистов, компьютерный форум, киберфорум
locm
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  

Пишем драйвер на PureBasic

Запись от locm размещена 26.12.2014 в 21:46
Показов 12991 Комментарии 0

Обычно драйверы для Windows пишут на языке C++, возможно потому что DDK фирма Майкрософт предоставила именно для этого языка. Но энтузиасты портировали DKK для других языков. Сейчас мы поговорим о создании драйверов на языке бейсик, который на первый взгляд не подходит для этого (хотя бы потому что бейсик считается простым языком, не предназначенным для системного программирования), но это только на первый взгляд. Теперь давайте определится с диалектом бейсика и компилятором, который будем использовать при разработке драйверов. Наверное стоит выбрать бейсик от фирмы Майкрософт, ведь по логике, он должен лучше всего подходить для разработки под Windows. Претендентами были выбраны Small Basic, VB и VB.NET.
Small Basic и VB.NET были исключены почти сразу. Первый из-за ограниченных возможностей и наличия внешнего рантайма, а второй из-за компиляции только в управляемый код, что нам не подходит, ведь на уровне ядра нет .NET Framework.
VB для создания драйверов в принципе подходит и в соседнем блоге это доказали, но методы создания драйвера, а так же внешний рантайм, ограничивают возможности и снижают удобство разработки.
Поэтому был продолжен поиск требуемого компилятора бейсика, но уже от сторонних фирм, не имеющих прямого отношения к Майкрософт. Подходящим компилятором оказался PureBasic, фирмы Fantaisie Software. Правда он не бесплатный. На данный момент, стоимость индивидуальной лицензии на все версии и платформы составляет 79€.
PureBasic подошел в первую очередь потому, что в его дистрибутиве есть почти все что нужно для компиляции драйвера и необходимо лишь немного изменить ключи ликовки чтобы получить не приложение, а драйвер. Но обо всем по порядку. Прежде рассмотрим процесс компиляции в PureBasic. Он проходит в несколько этапов. Сначала бейсик-код транслируется в ассемблер, а затем при помощи компилятора ассемблера FASM создается объектный файл, который линкуется программой Polink, с функциями PureBasic, находящимися в статических библиотеках и в результате создается исполняемый файл. Чтобы получился драйвер, а не приложение, достаточно изменить ключ линковки с
Code Скопировано
1
/SUBSYSTEM:Windows
на
Code Скопировано
1
/SUBSYSTEM:native /driver
При этом будет создан драйвер, но он окажется не рабочим, т. к. не решена еще одна проблема - несмотря на то что получился драйвер, он использует WinAPI функции вместо функций ядра. Точнее, это вшитый в него рантайм вызывает некоторые WinAPI функции при инициализации и завершении работы. Есть как минимум два варианта решения этой проблемы.
  1. Исключить рантайм из кода.
  2. Заставить рантайм использовать функции ядра вместо WinAPI.
Первый вариант реализовать не сложно. В процессе компиляции получаем ассемблерный код и несложный препроцессор, находящийся между транслятором бейсик кода (pbcompiler.exe) и ассемблером FASM справится с этой задачей. Но у такого решения есть большой минус - многие возможности станут недоступными. Например, станут недоступными строковые переменные, динамические и ассоциативные массивы, связные списки, перестанут работать многие функции и т. д. Это не лучшее решение, ведь теряется вся простота бейсика.

Второй вариант выглядит трудноосуществимым, ведь функции PureBasic скомпилированы и хранятся в статических библиотеках. Их декомпиляция, модификация и сборка, займут много времени и велика вероятность допустить ошибку, что сделать довольно просто учитывая что придется модифицировать ассемблерный код многих функций.
Так как же быть, ведь без рантайма отсутствуют многие возможности языка, но и использовать его в драйвере не представляется возможным из-за использования WinAPI функции вместо функций ядра?
На самом деле есть довольно простой способ без модификации кода рантайма и функций PureBasic, "отучить" их от WinAPI, причем не просто "отучить", но заставить использовать функции ядра. Звучит невероятно, правда? Но это возможно. Напомню, что рантайм и все функции PureBasic скомпилированы и находятся в статических библиотеках. Причем импорт WinAPI функций осуществляется посредством импорта символов из других статических библиотек и явно не указано из каких. Ничего не мешает подменить статические библиотеку из которой импортируются символы WinAPI функций (это например user32.lib, kernel32.lib и др.), на нашу из которой экспортируются символы с именами как в WinAPI функций. Таким образом чтобы отвязать рантайм и функции от WinAPI, требуется написать n-ое количество функций-переходников, которые являются в той или иной мере, аналогами WinAPI, но из них будут вызываться функции ядра. Конечно не для всех WinAPI функций есть аналоги в ядре, но во многих случаях это решаемо.
Приведу пример аналога WinAPI функции Sleep(), которая вызывает функцию ядра KeDelayExecutionThread().
PureBasic Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
ProcedureDLL RndLib_Sleep(TimeMS)
  Protected Time.q
 
  Time=TimeMS*10000  ; Перевод времени из миллисекунд и сотни наносекунд.
  Time=Time-(Time*2) ; Для задержки относительно текущего времени, число должно быть отрицательным.
 
  ProcedureReturn KeDelayExecutionThread(#KernelMode, #False, @Time)
  !public _Procedure0 as '_Sleep@4'
  !public __imp__Sleep
  !__imp__Sleep:
  !dd _Procedure0
EndProcedure
Как наверное поняли, код написан на PureBasic, но есть проблема, PureBasic не поддерживает создание статических библиотек, что странно, учитывая что в его дистрибутиве есть все необходимое для этого. Поэтому сборка статической библиотеки производилась в два этапа. Сначала утилитой coffIT из этого кода создавался в объектный файл (утилита при этом использовала pbcompiler и fasm), а затем, собиралась статическая библиотека при помощи утилиты polib, находящейся в папке Compilers дистрибутива PureBasic.
В этом коде, все что находится после символа "!" не обрабатывается компилятором PureBasic, а передается fasm в неизменном виде. Оператор public создает общедоступные метки, которые не будут удалены при компиляции и останутся в статической библиотеке. Именно это нам и нужно. Линкер вместо WinAPI функции Sleep() использует нашу, а поскольку есть вызов функции KeDelayExecutionThread, то линкер попытается ее найти в статических библиотеках и обнаружив в "ntoskrnl.lib", добавит функцию в импорт драйвера. WinAPI функция Sleep() туда не попадет, поскольку она заменена на нашу. Аналогичным образом подменяются и другие функции. Это позволило не только успешно инициализировать рантайм, но и заставить работать многие функции PureBasic, среди которых оказались и те, которые в драйвере вряд ли понадобятся. Например функции получения хеша (CRC32, MD5, SHA1 и т. д.) данных в памяти, а так же функции работы с регулярными выражениями.

В результате всего этого, был создан пакет файлов, который находится во вложении.
Установка.
  1. Установить PureBasic 5.11 x86 в любую папку.
  2. Открыть папку с установленным PureBasic и в папке "Compilers" переименовать файлы Fasm.exe и Polink.exe в Fasm_.exe и Polink_.exe.
  3. Открыть папку "PureLibraries\Windows\Libraries\" и удалить все файлы.
  4. Извлечь архив в папку с установленным PureBasic 5.11 x86.
  5. Запустить программу "IDE PB5.11 x86 Patch.exe", которая изменит расширение сохраняемых исполняемых файлов с .exe на .sys.
Подчеркну что нужна версия PureBasic именно 5.11 x86. С другими версиями этот пакет может быть не совместим.

Отдельно нужно сказать про память ядра. Она существует двух типов - подкачиваемая и неподкачиваемая.
Цитата с wasm.ru
Системные кучи (к пользовательским кучам не имеют никакого отношения) представлены двумя так называемыми пулами памяти, которые, естественно, располагаются в системном адресном пространстве:
Пул неподкачиваемой памяти (Nonpaged Pool). Назван так потому, что его страницы никогда не сбрасываются в файл подкачки, а значит, никогда и не подкачиваются назад. Т. е. этот пул всегда присутствует в физической памяти и доступен при любом IRQL. Одна из причин его существования в том, что обращение к такой памяти не может вызвать ошибку страницы (Page Fault). Такие ошибки приводят к краху системы, если происходят при IRQL >= DISPATCH_LEVEL.
Пул подкачиваемой памяти (Paged Pool). Назван так потому, что его страницы могут быть сброшены в файл подкачки, а значит должны быть подкачаны назад при последующем к ним обращении. Эту память можно использовать только при IRQL строго меньше DISPATCH_LEVEL.
Оба пула находятся в системном адресном пространстве, а значит, доступны из контекста любого процесса. Для выделения памяти в системных пулах существует набор функций ExAllocatePoolXxx, а для возвращения выделенной памяти всего одна - ExFreePool.
По умолчанию используется только неподкачиваемая память что необходимо для инициализации рантайма, которая хоть и происходит на уровне PASSIVE_LEVEL, но ведь в дальнейшем может произойти обращение к рантайму при более высоком IRQL и если он окажется равен или выше DISPATCH_LEVEL и "очень повезет" что требуемая область памяти окажется в файле подкачки - получим BSoD "на ровном месте". Но использовать для всего только неподкачиваемую память, тоже нерационально. Для возможности выбора типа памяти (подкачиваемая или нет), был добавлен макрос.
PureBasic Скопировано
1
SetPoolMode(Mode)
Возможные значения.
PureBasic Скопировано
1
2
3
4
5
Enumeration
  #Pool_NonPaged ; Использовать только неподкачиваемую память. Это по умолчанию.
  #Pool_Paged    ; Использовать только подкачиваемую память.
  #Pool_Auto     ; Автоматический выбор типа памяти в зависимости от IRQL.
EndEnumeration
Узнать текущий тип памяти можно с помощью макроса.
PureBasic Скопировано
1
GetPoolMode()
Смена типа памяти действует только на последующие выделения памяти, а тип уже выделенной памяти не меняется. И кроме того, это действует только на функции PureBasic. На вызов функции ядра ExAllocatePool() и ей подобных, не распространяется. Так же нужно следить за освобождением памяти. Даже если драйвер был выгружен из памяти, это не освободит ресурсы и память будет занята до перезагрузки системы. Если память неподкачиваемая, это равносильно уменьшению размера физической памяти!
В конце процедуры DriverUnload() нужно добавить строку, которая освободит память используемую рантаймом PureBasic.
Assembler Скопировано
1
!CALL _PB_EOP
Теперь рассмотрим простой пример драйвера, который только информирует о своей загрузке и выгрузке.
PureBasic Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Declare DriverEntry(*DriverObject, *RegistryPath)
*Point=@DriverEntry()
!jmp [p_Point]
 
IncludePath #PB_Compiler_Home+"DDK\"
XIncludeFile "ntddk.pbi"
XIncludeFile "ntstatus.pbi"
XIncludeFile "ntfunct.pbi"
 
 
Procedure DriverUnload(*DriverObject.DRIVER_OBJECT)
  
  DbgPrint("Unload Driver")
  
  !CALL _PB_EOP ; Освобождение ресурсов.
EndProcedure
 
Procedure DriverEntry(*DriverObject.DRIVER_OBJECT, *RegistryPath.UNICODE_STRING)
  
  DbgPrint("Load Driver")
  
  *DriverObject\DriverUnload = @DriverUnload()
  ProcedureReturn #STATUS_SUCCESS
EndProcedure
Рассмотрим код подробнее. Оператор Declare объявляет процедуру с именем DriverEntry(). Это необходимо потому что компилятор однопроходный, а процедура расположена ниже по коду относительно обращения к ней. Затем в переменную *Point помещается указатель на процедуру DriverEntry() и происходит переход по адресу в переменной *Point, т. е. в процедуру DriverEntry(). Возможно возникнет вопрос, почему не указать в качестве точки входа процедуру DriverEntry() и избавится от подобного прыжка по адресу? Сделать такое конечно можно, но при этом рантайм останется не инициализированным.
Оператор IncludeFile определяет путь к подключаемым файлам, а оператор XIncludeFile подключает их, причем делает это только один раз, т. е. если в исходнике окажется несколько подключений одного и того же файла, то будет подключен только один экземпляр, а остальные проигнорированы.
При выполнении кода процедуры DriverEntry() выводится отладочное сообщение "Load Driver", записывается в структуру *DriverObject адрес процедуры выгрузки драйвера и возвращается значение успешного выполнения.
Когда драйвер выгружается, будет выполнена процедура адрес которой сохранен в поле DriverUnload структуры *DriverObject. В данном коде это процедура DriverUnload(). В ней отсылается отладочное сообщение "Unload Driver", а затем, вызывается подпрограмма "_PB_EOP". Это необходимо чтобы рантайм "убрал за собой", т. е. освободил все те ресурсы что были использованы им при инициализации.
В этой же процедуре нужно освободить все ресурсы, используемые драйвером (например, память, различные хендлы и т. д.). Потому что даже если драйвер был выгружен, это не освободит ресурсы и они будут заняты до перезагрузки системы. В случае памяти, если она неподкачиваемая, это равносильно уменьшению размера физической памяти!
Все глобальные объекты (строки, массивы, связанные списки) так же нужно уничтожать при выгрузке драйвера. Наиболее просто это сделать если их поместить в общую структуру. Тогда все сведется к одной строке - очистке структуры функцией ClearStructure().

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

Нажмите на изображение для увеличения
Название: Compile.PNG
Просмотров: 844
Размер:	23.0 Кб
ID:	2934

После компиляции в результате которой, получили sys-файл, его можно проверить в действии, но сначала взглянем на импорт. Как видим, от WinAPI не осталось и следа.

Нажмите на изображение для увеличения
Название: PE_Explorer_Import_1.PNG
Просмотров: 814
Размер:	3.8 Кб
ID:	2937 Нажмите на изображение для увеличения
Название: PE_Explorer_Import_2.PNG
Просмотров: 786
Размер:	4.5 Кб
ID:	2938

Драйвером импортируется функция KeGetCurrentIrql() из hal.dll, используемая для определения текущего IRQL при автоматическом выборе типа памяти (подкачиваемая или нет). Из ntoskrnl.exe импортируются функции DbgPrint(), memset(), ExAllocatePool() и ExFreePool(). Первую можно видеть в коде драйвера и она используется для вывода отладочных сообщений. Вторая функция предназначена для заполнения памяти заданными данными. В нашем случае, она используется для обнуления (очистки) памяти. Третья и четвертая функция, выделяют и освобождают память. Этих функций достаточно для инициализации рантайма, причем действительно нужны только две ExAllocatePool() и ExFreePool(), а остальные можно или исключить при определенных условиях (функция KeGetCurrentIrql()) или заменить на небольшой участок кода (функция memset()). Довольно неплохо если учитывать что рантайм PureBasic изначально рассчитан на WinAPI, а не на функции ядра.
Размер файла драйвера получился равным 2 КБ. Возможно покажется что это много для такого простого драйвера, но в этот объем входит статически прилинкованный рантайм и пара функций-переходников эмулирующих WinAPI и вызывающих функции ядра. Если отказаться это всего этого и вырезать рантайм, то размер драйвера получится около 570 байт. Но нужно ли это? Отсутствие рантайма не позволит использовать многие возможности языка.

Теперь давайте запустим драйвер и посмотрим как он работает. Лучше это делать на виртуальной машине. Для этого понадобятся утилиты KmdManager и Dbgview. Первая запускает драйвер, а вторая отображает то, что было отправлено функцией DbgPrint(). После установки на PureBasic предлагаемого архива, эти утилиты будут доступны через меню "Инструменты". Их следует запускать с правами администратора. Для Windows 7 нужно выполнить эту рекомендацию.
Цитата Сообщение от Убежденный Посмотреть сообщение
Далее по поводу DbgPrint(Ex). Как известно, на Windows Vista и выше весь
отладочный вывод фильтруется, поэтому чтобы увидеть свои отладочные сообщения,
нужно включить соответствующий флаг в реестре на гостевой машине.
Ключ HKLM\SYSTEM\CurrentControlSet\Control\Se ssion Manager\Debug Print Filter.
Я использую параметр DEFAULT (тип REG_DWORD) со значением 0xf (15), это включает полный отладочный вывод. Хотя возможны и другие варианты.
Также нужно убедится что в меню "Capture" программы Dbgview есть галочка в пункте "Capture Kernel". Если ее там нет, ставим и перезапускаем программу.
В программе KmdManager указываем путь к драйверу и последовательно нажимаем на кнопки "Register", "Run", "Stop" и "Unregister". Если все сделано правильно, увидим примерно такую картину.

Нажмите на изображение для увеличения
Название: TestDriver.PNG
Просмотров: 1023
Размер:	19.9 Кб
ID:	2939

Это довольно простой драйвер, но его код можно еще сильнее упростить до такого.
PureBasic Скопировано
1
2
3
4
5
ImportC "ntoskrnl.lib" ; Импорт cdecl-функций.
  DbgPrint(String.s)
EndImport
 
DbgPrint("Load Driver")
Код максимально прост. Импортируется функция DbgPrint() из "ntoskrnl.lib", а затем она вызывается отправляя отладочное сообщение "Load Driver".

Нажмите на изображение для увеличения
Название: TestDriver_2.PNG
Просмотров: 957
Размер:	19.4 Кб
ID:	2940

Возможно те "кто в теме" подумают что этот код нормально работать не будет и вызовет BSoD и они будут по своему правы. Дело вот в чем. При выходе из функции DriverEntry() производится освобождение 8 байт из стека (2 локальных переменных - параметра процедуры, по 4 байта каждый), а затем возврат по текущему адресу в стеке. В этом коде ничего подобного нет. Как же он тогда работает? На самом деле все это есть. Препроцессор, находящийся между pbcompiler и fasm добавляет такой ассемблерный код.
Assembler Скопировано
1
2
3
 CALL _PB_EOP
 MOV eax,-1073741438
 RET 8
Чтобы было понятней, привожу весь ассемблерный код драйвера, передаваемый FASMу.
Assembler Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
format MS COFF
extrn _DbgPrint
extrn _ExitProcess@4
extrn _GetModuleHandleA@4
extrn _HeapCreate@12
extrn _HeapDestroy@4
extrn _memset
extrn PB_StringBase
extrn _SYS_InitString@0
extrn _SYS_FreeStrings@0
extrn _PB_StringBasePosition
public _PB_Instance
public _PB_ExecutableType
public _PB_OpenGLSubsystem
public _PB_MemoryBase
public PB_Instance
public PB_MemoryBase
public _PB_EndFunctions
 
macro pb_public symbol
{
 public  _#symbol
 public symbol
_#symbol:
symbol:
}
 
macro    pb_align value { rb (value-1) - ($-_PB_DataSection + value-1) mod value }
macro pb_bssalign value { rb (value-1) - ($-_PB_BSSSection  + value-1) mod value }
 
public PureBasicStart
section '.code' code readable executable           
PureBasicStart:
 PUSH dword I_BSSEnd-I_BSSStart
 PUSH dword 0
 PUSH dword I_BSSStart
 CALL _memset
 ADD esp,12
 PUSH dword 0
 CALL _GetModuleHandleA@4
 MOV [_PB_Instance],eax
 PUSH dword 0
 PUSH dword 4096
 PUSH dword 0
 CALL _HeapCreate@12
 MOV [PB_MemoryBase],eax
 CALL _SYS_InitString@0
; 
 PUSH dword _S1
 CALL _DbgPrint
 ADD esp,4
 CALL _PB_EOP
 MOV eax,-1073741438
 RET 8
_PB_EOP_NoValue:
; PUSH dword 0
_PB_EOP:
 CALL _PB_EndFunctions
 CALL _SYS_FreeStrings@0
 PUSH dword [PB_MemoryBase]
 CALL _HeapDestroy@4
 RET
; CALL _ExitProcess@4
_PB_EndFunctions:
 RET
section '.data' data readable writeable
_PB_DataSection:
_PB_OpenGLSubsystem: db 0
pb_public PB_DEBUGGER_LineNumber
 dd -1
pb_public PB_DEBUGGER_IncludedFiles
 dd 0
pb_public PB_DEBUGGER_FileName
 db 0
pb_public PB_Compiler_Unicode
 dd 0
pb_public PB_Compiler_Thread
 dd 0
pb_public PB_Compiler_Purifier
 dd 0
_PB_ExecutableType: dd 0
public _SYS_StaticStringStart
_SYS_StaticStringStart:
_S1: db "Load Driver",0
pb_public PB_NullString
 db 0
public _SYS_StaticStringEnd
_SYS_StaticStringEnd:
align 4
align 4
s_s:
 dd 0
 dd -1
align 4
section '.bss' readable writeable
_PB_BSSSection:
align 4
I_BSSStart:
_PB_MemoryBase:
PB_MemoryBase: rd 1
_PB_Instance:
PB_Instance: rd 1
align 4
PB_DataPointer rd 1
align 4
align 4
align 4
align 4
I_BSSEnd:
section '.data' data readable writeable
SYS_EndDataSection:
Теперь рассмотрим драйвер по сложнее, в котором используются функции PureBasic, которые не работали бы без инициализации рантайма и подмены некоторых WinAPI функций на их аналоги в ядре, т. к. было бы невозможно использование двусвязного списка и строк.
PureBasic Скопировано
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Declare DriverEntry(*DriverObject, *RegistryPath)
*Point=@DriverEntry()
!jmp [p_Point]
 
IncludePath #PB_Compiler_Home+"DDK\"
XIncludeFile "ntddk.pbi"
XIncludeFile "ntstatus.pbi"
XIncludeFile "ntfunct.pbi"
 
Procedure DriverUnload(*DriverObject.DRIVER_OBJECT)
  !CALL _PB_EOP ; Освобождение ресурсов.
EndProcedure
 
Procedure DriverEntry(*DriverObject.DRIVER_OBJECT, *RegistryPath.UNICODE_STRING)
  
  SetPoolMode(#Pool_Auto)    ; Автоматический выбор типа памяти в зависимости от IRQL.
  
  NewList x()
  For i=1 To 10
    If AddElement(x())
      x()=i*10
    EndIf
  Next i
  
  DbgPrint("Size list "+ListSize(x())+" items")
  ForEach x()
    DbgPrint(Str(x()))
  Next
  
  *DriverObject\DriverUnload = @DriverUnload()
  ProcedureReturn #STATUS_SUCCESS
EndProcedure
В процедуре DriverEntry() в первую очередь включатся автоматический выбор типа памяти (по умолчанию используется только неподкачиваемая память). Затем создается связный список с именем x, после чего в цикле в него добавляется 10 элементов. Далее формируется строка сообщающая о количестве элементов в списке и в цикле ForEach выводится текущее содержимое элементов списка.

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

Если запустить драйвер, увидим такую картину.

Нажмите на изображение для увеличения
Название: LinkedList.PNG
Просмотров: 740
Размер:	20.7 Кб
ID:	2942

В папке Examples архива, есть много примеров различных драйверов, как простых, только показывающих работу функций PureBasic, переведенных с WinAPI на функции ядра, так и примеров по сложнее, среди которых можно найти коды драйверов, предоставляющих доступ к портам компьютера, защищающих процесс от завершения, скрывающих указанные процессы в диспетчере задач, а так же драйвер обрабатывающий прерывания от LPT порта.
Вложения
Тип файла: zip PB_5.11_x86_DriverPack_v2.2.zip (1.11 Мб, 461 просмотров)
Размещено в Драйверы
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
std::vector в C++: от основ к оптимизации производительности
NullReferenced 05.04.2025
Для многих программистов знакомство с std::vector происходит на ранних этапах изучения языка, но между базовым пониманием и подлинным мастерством лежит огромная дистанция. Контейнер std::vector. . .
Реляционная модель и правила Кодда: фундамент современных баз данных
Codd 05.04.2025
Конец 1960-х — начало 1970-х годов был периодом глубоких трансформаций в области хранения и обработки данных. На фоне растущих потребностей бизнеса и правительственных структур существовавшие на тот. . .
Асинхронные операции в Django с Celery
py-thonny 05.04.2025
Разработчики Django часто сталкиваются с проблемой, когда пользователь нажимает кнопку отправки формы и. . . ждёт. Секунды растягиваются в минуты, терпение иссякает, а интерфейс приложения замирает. . . .
Использование кэшей CPU: Максимальная производительность в Go
golander 05.04.2025
Разработчикам хорошо известно, что эффективность кода зависит не только от алгоритмов и структур данных, но и от того, насколько удачно программа взаимодействует с железом. Среди множества факторов,. . .
Создаем Telegram бот на TypeScript с grammY
run.dev 05.04.2025
Одна из его самых сильных сторон Telegram — это интеграция ботов прямо в экосистему приложения. В отличие от многих других платформ, он предоставляет разработчикам мощный API, позволяющий создавать. . .
Паттерны распределённых транзакций в Event-Driven микросервисах
ArchitectMsa 05.04.2025
Современные программные системы всё чаще проектируются как совокупность взаимодействующих микросервисов. И хотя такой подход даёт множество преимуществ — масштабируемость, гибкость, устойчивость к. . .
Работа с объемным DOM в javascript
Htext 04.04.2025
Сегодня прочитал статью тут о расходах памяти в JS, ее утечках и т. п. И вот что вспомнил из своей недавней практики. Может, кому пригодится. Хотя, в той статье об этом тоже есть. Дело в том, что я. . .
Оптимизация производительности Node.js с помощью кластеризации
run.dev 04.04.2025
Масштабирование приложений для обработки тысяч и миллионов запросов — обыденная задача для многих команд. Node. js, благодаря своей асинхронной событийно-ориентированной архитектуре, стал популярной. . .
Управление зависимостями в Python с Poetry
py-thonny 04.04.2025
Стандартный инструмент для установки пакетов в Python - pip - прекрасно справляется с базовыми сценариями: установил пакет командой pip install и используешь его. Но что произойдёт, когда разные. . .
Мониторинг с Prometheus в PHP
Jason-Webb 04.04.2025
Prometheus выделяется среди других систем мониторинга своим подходом к сбору и хранению метрик. В отличие от New Relic, который использует агентный подход и отправляет данные во внешнее хранилище,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru
Выделить код Копировать код Сохранить код Нормальный размер Увеличенный размер