понедельник, 29 июня 2009 г.

Быстрое копирование файлов

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

Постойте, что тут у нас? Задача тридцать три: написать программу для быстрого копирования файлов, провести сравнение скорости с проводником Windows и Total Commander-ом в файловых системах FAT и NTFS. Оба-на! Ни единой подробности реализации в условии задания. В чём прикол? Мы же никогда не обгоним вышеперечисленных "соперников". Не говоря уже о десятке сторонних программ, встраиваемых в проводних Windows и ускоряющих(это ещё под сомнением) копирование.
Что же мы можем придумать такого необычного? Да и вообще, что обуславливает вариативность задания? Очевидные параметры:
1. Размер буфера
2. Вкл/Откл буферизации ОС
3. Прочие аттрибуты открытия файла

Для всех является очевидным, что чтение одного мегабайта данных за раз и чтение десять раз по сто килобайт несоизмеримы по времени. Второе несомненно выполняется намного дольше. А значит, чем больше буфер мы выберем, тем быстрее будет наше копирование. Это обычная логика здравомыслящего человека. Но мы ведь не для этого здесь собрались? Более бесполезную статью трудно придумать. Даже по меркам моего блога. И вообще уже, какая то вода получается, а не статья.

Нам известно, что в общем случае файл на жёстком диске хранится не одним куском, а разбит на фрагменты (в терминологии Microsoft - extents). Под экстентом мы будем понимать набор смежных секторов носителя информации. А что если мы будем считывать файл блоками размером с эти экстенты? Если размер экстента будет неприлично большой, будем считывать его за несколько раз. Тоесть при каждом считывании, потребуется считать с диска только подряд идущие сектора. Даст ли нам это преимущество в скорости? Для слабофрагментированных файлов наш алгоритм вообще сведётся к изначальному примитивному копированию с постоянным буфером. Немного погуглив, нашёл следующие ссылки:
1. FSCTL_GET_RETRIEVAL_POINTERS Control Code
2. http://www.gidforums.com/t-4551.html

Речь идёт о вызове функции драйвера для получения массива экстентов для конкретного файла с информацией о размере и привязке экстентов к реальным кластерам на диске.
VCN (Virtual Cluster Number) - номер виртуального кластера. Для каждого файла своя нумерация. Например, виртуальный кластер #3 в файле на диске (диск с размером кластера 4КБ) означает область файла с байта 12288 до байта 16391.
LCN (Logical Cluster Number) - номер реального кластера на носителе информации. Каждому виртуальному кластеру сопоставлен ровно один реальный и наоборот. Виртуальные нумеруются от начала файла, а реальные от начала диска.

Теперь разберёмся с кэшированием. Нужно ли нам кэширование ОС при чтении и при записи? Для сильно-фрагментированных файлов кэширование чтения не только не полезно, но и вредно. Мы ведь считываем с диска практически всегда поэкстентно. А значит послеидущие сектора нам точно не нужны. Значит кэширование чтения нам только навредит. Практика показала, что от кэширования записи тоже лучше отказаться. Ну и чего нам теперь не хватает? Берём и пишем код.

#define _WIN32_WINNT 0x0400

#include <windows.h>
#include <WinIoCtl.h>
#include <iostream>

using namespace std;

const wchar_t *FROM_FILE = L"c:\\Documents and Settings\\Артурчег\\Local Settings\\Application Data\\Microsoft\\CD Burning\\EPL.2009.Liverpool.vs.Arsenal.720p.HDTV.x264\\First time-001.mkv";
const wchar_t *TO_FILE = L"W:\\1.mkv";

int main(int argc, TCHAR *argv[])
{
DWORD time1 = GetTickCount();

// Подготовка файла
HANDLE hFile = CreateFile(
FROM_FILE, // 126 parts
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,
NULL);

if( hFile == INVALID_HANDLE_VALUE )
{
cout << "CreateFile(): error " << GetLastError() << endl;
return 1;
}

// Подготовка буферов
STARTING_VCN_INPUT_BUFFER inputBuffer;
inputBuffer.StartingVcn.QuadPart = 0;

DWORD sz = sizeof(RETRIEVAL_POINTERS_BUFFER)+sizeof(LARGE_INTEGER)*2*1000000;
RETRIEVAL_POINTERS_BUFFER *outputBuffer = (RETRIEVAL_POINTERS_BUFFER*) new char [sz];
outputBuffer->ExtentCount = 1000000;
outputBuffer->StartingVcn.QuadPart = 0;

// Запрос данных о файле
DWORD retSize;
BOOL fResult = DeviceIoControl(
(HANDLE) hFile, // handle to device
FSCTL_GET_RETRIEVAL_POINTERS, // dwIoControlCode
(LPVOID) &inputBuffer, // input buffer
(DWORD) sizeof(inputBuffer), // size of input buffer
(LPVOID) outputBuffer, // output buffer
(DWORD) sz, // size of output buffer
(LPDWORD) &retSize, // number of bytes returned
(LPOVERLAPPED) NULL ); // OVERLAPPED structure

if (!fResult)
{
cout << "DeviceIoControl() error: " << GetLastError() << endl;
return 2;
}
cout << "File consist from " << outputBuffer->ExtentCount << " extents." << endl;

DWORD SECTOR_SIZE = 4096;

// Создание цепочки размеров
DWORD arrsize = outputBuffer->ExtentCount;
DWORD *arr = new DWORD [arrsize];
arr[0] = SECTOR_SIZE*(outputBuffer->Extents[0].NextVcn.QuadPart - outputBuffer->StartingVcn.QuadPart);

DWORD fsz = arr[0];
for(int i=1; i<arrsize; i++)
{
arr[i] = outputBuffer->Extents[i].NextVcn.QuadPart - outputBuffer->Extents[i-1].NextVcn.QuadPart;
arr[i] *= SECTOR_SIZE;
fsz += arr[i];
}
cout << "Aligned file size is " << fsz << " bytes." << endl;

DWORD MAX_BUFFER_SIZE = 10*1024*1024;
char *buffer = new char [MAX_BUFFER_SIZE];

// Создание файла
HANDLE hFileTo = CreateFile(
TO_FILE,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, //!!! Remove if no aligned to 4096 !!!
NULL);

if( hFileTo == INVALID_HANDLE_VALUE )
{
cout << "CreateFile(): error " << GetLastError() << endl;
return 1;
}

// Цикл копирования
DWORD curSize = 0;
DWORD bytesReaden, bytesWritten;
for (int i=0; i<arrsize; i++)
{
if(arr[i] <= MAX_BUFFER_SIZE)
curSize = arr[i];
else {
curSize = MAX_BUFFER_SIZE;
arr[i] -= MAX_BUFFER_SIZE;
i--;
}
//curSize = MAX_BUFFER_SIZE;
fResult = ReadFile(hFile,buffer,curSize,&bytesReaden,NULL);

if (!fResult)
cout << "Reading Collision: " << GetLastError() << endl;

if (curSize != bytesReaden)
cout << "(curSize != bytesReaden) == (" << curSize << " != " << bytesReaden << ")\n";

fResult = WriteFile(hFileTo,buffer,bytesReaden,&bytesWritten,NULL);

if (!fResult)
{
cout << "Writing Collision (" << bytesWritten << " from " \
<< bytesReaden << " ) " << GetLastError() << endl;

// Дописывание хвоста файла (буферизация не отключена)
CloseHandle(hFileTo);
HANDLE hFileTo = CreateFile(
TO_FILE,
FILE_APPEND_DATA,
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
fResult = WriteFile(hFileTo,buffer,bytesReaden,&bytesWritten,NULL);
if (!fResult)
cout << "Writing ERROR: " << GetLastError() << endl;
break;
}
}

CloseHandle(hFile);
CloseHandle(hFileTo);

DWORD time2 = GetTickCount();
cout << "All right, my time is " << time2-time1 << " ms\n";

return 0;
}

Подсветка синтаксиса выполнена при помощи Notepad++



Реализованная система прошла тестирование на сильно-фрагментированных файлах. Быстро отыскать такие файлы в системе вам поможет отчет стандартной программы дефрагментации(рис. 1). Результаты тестирования представлены в таблице 1 и таблице 2. Тесты были проведены именно в том порядке в котором они представлены в таблице. Таблица говорит сама за себя.


Рис. 1. Отчёт дефрагментатора

Таблица 1
+------------+--------------+----------------------------------+
| Фрагментов | Размер файла | Имя файла |
+------------+--------------+----------------------------------+
| 1,858 | 1.01 ГБ | First time-001.mkv |
+------------+--------------+----------------------------------+
+-------------------------------+-----------+
| Программа | Время |
+-------------------------------+-----------+
| FragCopy (без буф-ии) | 62.128сек |
| FragCopy (без буф-ии) | 63.944сек |
| FragCopy (без буф-ии) | 62.078сек |
| FragCopy (без буф-ии чтения) | 62.484сек |
| FragCopy (с буф-ей) | 92.547сек |
+-------------------------------+-----------+
| Total Commander 6.55 (32Bit) | 79.200сек |
+-------------------------------+-----------+
| Проводник (Windows XP 32Bit) | 98.200сек |
+-------------------------------+-----------+

Таблица 2
+------------+--------------+----------------------------------+
| Фрагментов | Размер файла | Имя файла |
+------------+--------------+----------------------------------+
| 1,202 | 662 МБ | A0017447.exe |
+------------+--------------+----------------------------------+
+-------------------------------+-----------+
| Программа | Время |
+-------------------------------+-----------+
| FragCopy (без буф-ии) | 10.110сек |
| FragCopy (без буф-ии) | 10.062сек |
| FragCopy (без буф-ии) | 10.062сек |
| FragCopy (без буф-ии) | 10.000сек |
| FragCopy (без буф-ии чтения) | 38.703сек |
+-------------------------------+-----------+
| Total Commander 6.55 (32Bit) | 15.200сек |
+-------------------------------+-----------+
| Проводник (Windows XP 32Bit) | 13.900сек |
+-------------------------------+-----------+

Алгоритм хорошо себя показывает на файлах с большим числом фрагментов. Ну правильно, ведь при малом числе экстентов и их не малом размере мы копируем буфером постоянного размера. Никакого разнообразия. Есть идеи?


Читать дальше

воскресенье, 28 июня 2009 г.

Попытка создать ку-Триггер

Появилась идея сделать ещё один шаг эволюции в схеме триггера . . .
Не 2 входа, 2 коньюнктора и 2 выхода, а всех элементов по три.
Ниже приведены скриншоты схем и тестов.


Рис. 1. Схема обычного триггера


Рис. 2. Симуляция работы обычного триггера


Рис. 3. Схема нового триггера


Рис. 4. Симуляция работы нового триггера


Работа обычного триггера прерывается при попытке подать на оба входы "1".
Работа нового триггера прерывается при попытке подать на все три входа "0".
Не совсем то, чего я ожидал. Думал щас какая-нить прикольная фигня получится.

// Если потом найду время ещё на других тестах погоняю.
// Если у вас есть идеи по этому поводу, пишите в комментах свои тесты.

Читать дальше

понедельник, 22 июня 2009 г.

QLineEditGhost

Слепил себе простенький виджет на Qt 4.5.1. Строка ввода с "призрачной" надписью. Интересно почему изначально не присутствовала эта возможность у QLineEdit-а.


Рис. 1. Анимация (18Кб)

Исходник:

#ifndef QLINEEDITGHOST_H
#define QLINEEDITGHOST_H

#include <QLineEdit>

class QLineEditGhost : public QLineEdit
{
Q_OBJECT

bool isGhostNow;
QString ghostText_;
public:
QLineEditGhost ( const QString & str = "", QWidget * parent = NULL):
QLineEdit(parent), ghostText_(str), isGhostNow(true)
{
connect( this, SIGNAL(cursorPositionChanged(int,int)),
this, SLOT(cursorPositionChangedGhost(int,int)) );
connect( this, SIGNAL(textEdited(QString)),
this, SLOT(textEditedGhost(QString)) );
setText( ghostText() );
QPalette pal = palette();
pal.setColor( QPalette::Text, QColor(128,128,128) );
setPalette(pal);
}

QString text () const
{
if (isGhostNow)
return "";
return QLineEdit::text();
}

QString ghostText() const {
return ghostText_;
}
void setGhostText(const QString &ghostText_in) {
ghostText_ = ghostText_in;
}


public slots:
void cursorPositionChangedGhost(int from, int to)
{
if ((to != 0) && isGhostNow)
setCursorPosition(0);
}
void textEditedGhost(QString str)
{
QPalette pal = palette();
if (isGhostNow)
{
pal.setColor( QPalette::Text, QColor(0,0,0) );
setPalette(pal);
isGhostNow = false;
setText( text().left( text().length() - ghostText_.length() ) );
}
if (text().length() == 0)
{
pal.setColor( QPalette::Text, QColor(128,128,128) );
setPalette(pal);
isGhostNow = true;
setText( ghostText_ );
}
}

};

#endif // QLINEEDITGHOST_H

Подсветка синтаксиса выполнена при помощи Notepad++



Во время написания кода возникли проблемы в вызовом протектед метода
родительского класса. Пытался написать так: ((QLineEdit*)this)->text()
Думал-думал и придумал: QLineEdit::text()

Ну и в конце концов, меня уже взбесило, что исходник не влазит по ширине и по-уродски обрубаются строчки. Увеличил ширину блога на 200 пикселей. Теперь ширина 944px. Картинку-шапку тоже пришлось увеличить.

Читать дальше

среда, 17 июня 2009 г.

Вас притесняют вирусы?

Какие у вас права в системе? Администратора? А вы можете запустить диспетчер задач или редактор реестра? Если система говорит вам что администратор отключил/запретил то или иное действие, а администратор это вы . . . Тогда вас поимел вирус. Этот пост для вас.

1. Диспетчер задач и редактор реестра

Перечислю наиболее простые способы возвращения себе прав запуска диспетчера задач и редактора реестра. Все способы требуют наличия прав редактирования реестра.


Способ 1 ( Требует наличия "c:\windows\regedit.exe" )

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


Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System]
"DisableTaskMgr"=dword:00000000
"DisableRegistryTools"=dword:00000000


Способ 2 ( Требует наличия "c:\windows\system32\reg.exe" )

Запустите командную строку: Пуск -> Выполнить... -> cmd
Выполните 2 команды:
1) reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System" /v DisableTaskMgr /t REG_DWORD /d 0 /f2) reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System" /v DisableRegistryTools /t REG_DWORD /d 0 /f

Способ 3 ( Требует наличия "c:\windows\system32\wscript.exe" )

Скопируйте текст ниже в текстовый файл и переименуйте его в *.JS, после чего кликните по нему 2 раза для запуска скрипта добавляющего информацию в реестр.


reg = new ActiveXObject("WScript.Shell");

reg.RegWrite("HKEY_CURRENT_USER\\Software\
\\Microsoft\\Windows\\CurrentVersion\
\\Policies\\System\\DisableTaskMgr",0,"REG_DWORD");

reg.RegWrite("HKEY_CURRENT_USER\\Software\
\\Microsoft\\Windows\\CurrentVersion\
\\Policies\\System\\DisableRegistryTools",0,"REG_DWORD");


2. Отображение скрытых и системных файлов в проводнике

Частенько вирусы не дают возможности просмотреть скрытые и системные файлы в проводнике. При попытке изменения этих атрибутов в свойствах папки - вирус исправляет настройки обратно. Если изменять значения ключей реестра - вирус это тоже исправляет обратно. Есть способ это уладить.


Необходимо задать следующие значения ключам реестра в ветке "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced":
1) Hidden = 1
2) SuperHidden = 1
3) ShowSuperHidden = 2

После установки значений ключей, система вообще не сможет понять что она показывает (рис. 1). Главное, что и скрытые и системные файлы отныне в проводнике показываются. Теперь уж вирус от нас не спрячется. Почему для настройки отображения скрытых и системных файлов в проводнике задействованы 3 ключа реестра? Hidden, SuperHidden, ShowSupeHidden . . . (может кто подскажет в чём прикол?)



Рис. 1. Свойства папки / Вид



Читать дальше

суббота, 30 мая 2009 г.

DropHandler (не доделал пока)

Началось всё с того, что я взял несколько сотен мегабайт музыки у моей девушки. Каково же было моё удивление, когда вместо фотографий альбомов, я обнаружил фотки подружек/одноклассниц/семьи. Задумался над тем, как же она выставляла все эти фотки. Сдаётся мне - каждую вручную через iTunes. Потом пошла мысль за мыслью об монотонных операциях, которые бы желательно сократить . . .

1. Рождение идеи

А ведь было бы гораздо проще и интуитивно понятнее, если бы можно было просто натащить фотку мышкой на мп3-шку. Действительно, просто хватаем, тащим мышой и отпускаем на нужной песне - бац фотка стала картинкой альбома.

2. Разведка обстановки

Сказано - сделано. Пошли копать под MS Explorer. Хватаю мышой JPG и тащу на MP3. И что я вижу? мп3-шке абсолютно пофигу все мои старания. Она просто прикидывается фоном, даже слушать не хочет ни про какую жпг. Погодите, а на кого вообще можно другие файлы перетаскивать? Метод простой проверки показал: EXE, CMD, COM, BAT, JS, VBS и конечно же фолдеры. Интересно знать, почему они реагируют на drag&drop, а остальные нет.

За ответом на этот вопрос идём в реестр в ветвь HKCR. Находим папку "HKCR/.exe", её значение по умолчанию "exefile". Теперь находим папку "HKCR/exefile" и видим всю информацию о возможных действиях с ехе-файлами. Бросается в глаза папка "HKCR/exefile/shellex/DropHandler", её имя говорит само за себя. Обработчик бросания - дословный перевод. Значение по умолчанию у этой ветви реестра - странный набор символов через дефис, заключённый в фигурные скобки. Где-то я такие уже видел . . .

А! Точно! В ветке "HKCR/CLSID" полным полно таких. CLSID - как пить дать Class ID. Внутри найденной ветви реестра видим путь к shell32.dll и еще один непонятный ключик ThreadingModel со значением Apartment. Теперь нам понятно, при перетаскивании любого объекта файловой системы на ехе-файл, будет вызываться один из методов динамической библиотеки shell32.dll

3. Принятие первичных мер

Создаём ветвь реестра "HKCR/.mp3/shellex/DropHandler" со значением по умолчанию как и у ехе-шника. Смотрим, что изменилось в проводнике. Хватаем картинку и тащим на мп3. И вот оно! MP3 файл выделяется как и EXE при натаскивании на него файла. Ну чтож, отпускаем. Как и ожидалось открылся плеер по умолчанию и стал проигрывать нашу песню. Ну а чего мы ещё хотели установив мп3-шке DropHandler от ехе-шки? Следующим этапом по логике вещей должно стать создание своего собственного DropHandler-а и получение его собственного CLSID.

4. Ныряем с головой

Ни разу ничего похожего не делал. Погуглил-погуглил и определился. Следует в MSVC создать проект ATL, как DLL. CLSID свежесозданной длл-ки я обнаружил прямо в исходном коде. Он сгенерировался в процессе создания проекта. Теперь следует найти в исходниках(а их не мало уже) основной класс и пронаследовать его от IDropHandler и IPersistFile. Придётся поставить порядка 12-ти методов-заглушек и только необходимые нам наделить логикой. Каждый раз при перекомпиляции проекта студия создаёт необходимые ветви реестра в "HKCR/CLSID" и "HKCR/AppID". Только внутри они пустые, я каждый раз заполнял их по примеру стандартного DropHandler-а shell32.dll. (А надо ли это было?)

// DropHandler всё ещё на операционном столе.
// Откликаться на дрэг пока не желает.
// Работа кипит.

Читать дальше

воскресенье, 24 мая 2009 г.

Google Chrome Tweaks

Не все настройки браузера Google Chome видны из интерфейса, некоторые вообще можно указать только при запуске. Решил собрать в одном месте все интересные особенности браузера Google Chrome. Когда составлю свою оптимальную конфигурацию, щадящую озу, но всё же функциональную - обязательно припишу снизу статьи.

1. Скрытие/отображение вкладок:
Ctrl + B


2. История в текущей вкладке:
зажать кнопку мыши на кнопке "назад"


3. Процессы и треды (ключи запуска)
–process-per-tab
–process-per-site
–single-process


4. Информация о браузере:
about:version и about: - версия браузера
about:plugins - плагины
about:histograms - гистограммы
about:dns - DNS адреса
about:cache - кэш
view-cache:stats - статистика по кэшу
about:memory - Статистика по памяти
about:crash - Опаньки . . .


5. Все ключи запуска:
-disable-hang-monitor
-disable-metrics
-disable-metrics-reporting
-assert-test
-renderer-assert-test
-crash-test
-renderer-crash-test
-renderer-startup-dialog
-plugin-startup-dialog
-testshell-startup-dialog
-plugin-launcher
-channeltesting-channel
-homepagestart-renderers-manuallyrenderer
-renderer-pathplugin
-single-process
-process-per-tab
-process-per-site
-in-process-plugins
-no-sandbox
-safe-plugins
-trusted-plugins
-test-sandbox
-user-data-dir
-appupload-file
-dom-automation
-plugin-pathjs-flags
-geoidlangdebug-children
-wait-for-debugger-children
-log-filter-prefix
-enable-logging
-dump-histograms-on-exit
-disable-logging
-remote-shell-port
-uninstallomnibox-popup-count
-automation-channel
-restore-last-session
-record-mode
-playback-mode
-no-events
-hide-icons
-show-icons
-make-default-browser
-proxy-server
-dns-log-details
-dns-prefetch-disable
-debug-print
-allow-all-activex
-disable-dev-tools
-always-enable-dev-tools
-memory-model
-tab-count-to-load-on-session-restore
-memory-profile
-enable-file-cookies
-start-maximized
-enable-watchdogfirst-run
-message-loop-strategy
-message-loop-histogrammer
-importsilent-dump-on-dcheck
-disable-prompt-on-repost
-disable-popup-blocking
-disable-javascript
-disable-java
-disable-plugins
-disable-images
-use-lf-heap
-new-http


6. Горячие клавиши

[Ctrl + T] - Открыть новую вкладку
[CTRL + W] - Закрыть текущую вкладку
[Ctrl + Shift + T] - Открыть последнюю закрытую вкладку
[Ctrl + N] - Новое окно(не вкладка)
[Ctrl + Shift + N] - Открыть окно с режимом инкогнито
[Ctrl + L] - Выделить URL страницы(перейти в строку адреса)
[Ctrl + R] - Перезагрузить страницу
[Ctrl + U] - Смотреть исходный код страницы
[Ctrl + H] - Читать историю
[Ctrl + J] - Страница ваших загрузок
[Shift + Esc] - Дипетчер задач

[Ctrl + F] - Поиск текста на странице
[Ctrl + G] - Искать дальше
[Ctrl + P] - Печать страницы
[Ctrl + D] - Добавить страницу в закладки
[Ctrl + E / Ctrl + K] - Поиск с помощью Гугла
[Ctrl + "+" / Ctrl + MouseUp] - Приблизить
[CTRL + "-" / CTRL + MouseDown] - Отдалить



Читать дальше

вторник, 19 мая 2009 г.

Обрывки статей

Наконец-то сделал в блоге "обрывки"/начала статей на главной странице. Я использовал способ, описанный здесь.
Есть желание сделать текст в статьях, видимый только на главной.
Он будет кратко обещать читателю содержимое остальной части.
И работать будет диаметрально-противоположно остальному тексту.

Структура:
1. Введение в тему статьи
2. Описание тела статьи
3. Тело статьи

На главной будут видны пункты 1 и 2.
На странице со статьёй будут только пункты 1 и 3.
Читать дальше