Задумывались ли вы о том, насколько архаичен дизайн операционных систем, которыми мы пользуемся в настоящее время?
Например, почему в Windows (включая 11-тую версию) основной диск с операционной системой называется
C
?Или почему Microsoft открыто не публикует исчерпывающий список отличий в ядре Windows от версии к версии с точки зрения разработки приложений, т.е. различий в WinAPI. Может быть потому, что тогда все увидят, что начиная с Windows XP вообще ничего значимого в WinAPI не появилось? Навскидку, более 99% используемых в приложениях системных вызовов, вроде CreateFile имеют приписочку ‘Minimum supported Windows XP’ или ‘Minimum supported Windows 2000’, а в оставшемся <1% указано ‘Minimum supported Windows Vista’. Попробуйте найти хотя бы штук 5 новых полезных возможностей ядра Windows 11 относительно Windows 7, например. Речь не про новый отвратительный (по сравнению с Windows 7/8) калькулятор и не про обновлённый Блокнот или MS Paint, а об изменениях, которые видны программисту, пишущему полноценные (т.е. Desktop) приложения, т.е. изменений в WinAPI, ну или хотя бы о тех, которые не затрагивают WinAPI, но относятся именно к ядру ОС. Все те новшества, о которых Microsoft говорит на презентациях, легко реализуются на пользовательском уровне, либо в драйверах, вообще никак не требуя доработок ядра ОС. Если бы такой список изменений ядра ОС Windows был опубликован, то я уверен, что многих бы поразило, насколько незначительно ядро Windows 11 отличается от Windows 7 или даже Windows XP, которая вышла 23 года назад.
С другой стороны, у нас есть Unix, которая вообще разрабатывалась для мейнфреймов и "мини-компьютеров" размером с современный серверный шкаф для ЦОД, к которым подключалось множество терминалов и которые обслуживали большое количество инженеров.
О тех временах красноречиво говорит такое высказывание, приписываемое министру радиопромышленности СССР Николаю Горшкову:
Персонального компьютера не может быть. Могут быть персональный автомобиль, персональная пенсия, персональная дача. Вы вообще знаете, что такое ЭВМ? ЭВМ — это 100 квадратных метров площади, 25 человек обслуживающего персонала и 30 литров спирта ежемесячно!
Но времена поменялись. И теперь уже не один компьютер на 10-100 человек обслуживающего персонала, а один человек может обслуживать 10-100 компьютеров. А вот принципиальный дизайн ОС семейства Unix остался по сути прежним. Вот только современные дистрибутивы Unix/Linux всеми силами пытаются его скрыть от пользователя под красивыми обёртками (включая новые более удобные версии утилит командной строки и графический интерфейс, если говорить про desktop). Но что скрывается под этими обёртками?
Попробуйте выполнить такую простую задачу, как узнать количество свободного места на диске вашего компьютера используя командную строку. Речь про Linux. (В Windows это сделать на удивление просто командой
dir
без параметров [смущает только вывод числа в байтах, из-за чего получается огромное количество цифр, особенно если свободное место измеряется в терабайтах].)Я вот честно не помню даже такую простую команду, т.к. взаимодействую с Linux крайне редко. Но что нам выдаёт Google? Первый результат поиска по строке ‘linux command line free disk space’ ведёт на статью ‘5 Linux commands to check free disk space’. Что? 5 способов узнать свободное место на диске? Зачем мне 5 способов, когда у меня всего лишь один диск. :)(:
Ну ладно.
df -h
выглядит как то, что нужно.
Запускаем, и...
Filesystem Size Used Avail Use% Mounted on
udev 952M 0 952M 0% /dev
tmpfs 197M 1.8M 196M 1% /run
/dev/sda1 15G 11G 3.5G 76% /
tmpfs 984M 0 984M 0% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
tmpfs 984M 0 984M 0% /sys/fs/cgroup
/dev/loop0 15M 15M 0 100% /snap/gnome-characters/375
/dev/loop1 3.8M 3.8M 0 100% /snap/gnome-system-monitor/123
/dev/loop2 2.2M 2.2M 0 100% /snap/gnome-calculator/945
/dev/loop3 157M 157M 0 100% /snap/gnome-3-28-1804/110
/dev/loop4 1.0M 1.0M 0 100% /snap/gnome-logs/81
/dev/loop5 486M 486M 0 100% /snap/gnome-42-2204/120
/dev/loop6 45M 45M 0 100% /snap/gtk-common-themes/1440
/dev/loop7 55M 55M 0 100% /snap/core18/1650
/dev/loop8 165M 165M 0 100% /snap/gnome-3-28-1804/198
/dev/loop9 128K 128K 0 100% /snap/bare/5
/dev/loop10 56M 56M 0 100% /snap/core18/2785
/dev/loop11 1.5M 1.5M 0 100% /snap/gnome-system-monitor/184
/dev/loop12 141M 141M 0 100% /snap/gnome-3-26-1604/98
/dev/loop13 896K 896K 0 100% /snap/gnome-logs/119
/dev/loop14 74M 74M 0 100% /snap/core22/817
/dev/loop15 4.3M 4.3M 0 100% /snap/gnome-calculator/544
/dev/loop16 92M 92M 0 100% /snap/gtk-common-themes/1535
/dev/loop17 119M 119M 0 100% /snap/core/15511
/dev/loop18 512K 512K 0 100% /snap/gnome-characters/789
/dev/loop19 141M 141M 0 100% /snap/gnome-3-26-1604/111
tmpfs 197M 16K 197M 1% /run/user/121
vmhgfs-fuse 231G 216G 16G 94% /mnt/hgfs
tmpfs 197M 52K 197M 1% /run/user/1000
О-г-о. Ну надо же, сколько полезной информации выдала эта команда!
Напомню: у меня в системе всего один диск. А вот это вот всё к чему всё это?? Или предполагается, что продвинутые пользователи должны знать, что обозначает каждый "девайс"/‘точка монтирования’ в этом списке?
Или взять тот же Android. Зачем там внутренний storage разбит на такую кучу разделов? Если это упрощает жизнь разработчикам, то с дизайном операционной системы явно что-то не так.
В этом отношении дизайн Windows гораздо лучше ориентирован на пользователя: вместо непонятных точек монтирования используются логические диски. И в новой ОС я бы взял за основу эту же идею, только вместо буквенного названия дисков я предлагаю числовое, при этом диск 0 — это системный диск (аналог диска C в Windows).
Название для новой ОС я также предлагаю взять цифровое: 9os или просто 9.
[Почему именно 9? Ну, когда говорят 10-ка или 8-ка, то обычно подразумевают Windows. Но если Windows 8 и Windows 10 есть, то Windows 9 нету, так почему бы не использовать именно эту цифру.]
Безопасность
До сих пор существует нездоровая ситуация, когда операционную систему очень просто убить одной неверной командой, выполненной от имени администратора/суперпользователя. (Даже без дополнительного подтверждения и без ввода пароля.) Да, простого
rm -rf /
уже не достаточно, но с такой задачей справляется команда чуть подлиннее.Более того, практически любая современная ОС без дополнительной настройки защищает только свои системные файлы, а не файлы пользователя, которые можно модифицировать или вообще удалить любой программой без прав администратора.
Как-то лет 10 назад я получив [бесплатно и навсегда] 100 Гб в Облаке Mail.ru решил обезопасить все свои файлы в облаке. (Одной из причин, вероятно, была возникшая как раз в то время ситуация, когда автоматическое обновление Яндекс.Диска с версии 1.1.5 на 1.1.6 или 1.1.7 частично убивало раздел с операционной системой.)
Т.к. я не использовал веб-интерфейс для заливки новых файлов в облако и использовал клиентское приложение только на одном компьютере, я решил вообще запретить клиентскому приложению mail.ru-облака изменять/удалять какие-либо файлы на моём компьютере [точнее, ноутбуке], а разрешить только читать [чтобы получить одностороннюю синхронизацию]. Я попробовал два способа [я опущу детали этой настройки, например, необходимо было оставить доступ к служебным файлам .cloud.db, .cloud_ss, .cloud_temp_* в каталоге с облаком], используя встроенные в Windows 7 возможности настройки прав доступа для каталогов и файлов (вкладка ‘Безопасность’ в свойствах каталога):
- Сперва я установил разрешение изменять/удалять любые файлы в каталоге C:\Cloud@Mail.Ru только от имени администратора. Тогда не только Облако Mail.ru, но и любая программа без привилегий не могла ничего испортить в этом каталоге. Но такое решение оказалось не удобным: загрузить например новые фотографии в папку было можно, а вот изменить (например, применить ‘поворот JPEG без потерь’) уже нет. С документами работать также было очень затруднительно.
- Тогда я решил добавить в систему специального пользователя, у которого нет прав, кроме чтения каталога C:\Cloud@Mail.Ru, и запускать Облако Mail.ru только от имени этого пользователя (используя команду
runas
). Но в итоге, я так и не смог заставить надёжно работать это решение так, как задумывалось: клиент mail.ru-облака не мог изменять/удалять мои файлы, но мог добавить новую папку в C:\Cloud@Mail.Ru, к которой он впоследствии имел уже полный доступ. Постоянно возникали какие-то мелкие проблемы при работе с этой папкой, о которых я сейчас уже не могу вспомнить, увы, но сейчас на другом ноутбуке после установки Облака Mail.ru я решил больше не заморачиваться с такой настройкой и оставить всё как есть, в надежде, что история, аналогичная произошедшей с ЯДом, никогда не повторится.
Вообще, я считаю, что если ОС спроектирована и настроена правильно, то какими бы не были криворукими [не имею в виду никого конкретно, это гипотетически] разработчики приложения синхронизатора, оно [это приложение] в принципе не должно иметь возможность причинить вред ни операционной системе, ни пользовательским файлам (ну, за исключением тех файлов, с которыми это приложение непосредственно работает). Ещё лучше, если безопасная работа ОС не потребует настройки, а будет по умолчанию.
Как это может выглядеть?
Что если все приложения будут запускаться по умолчанию в sandbox-контейнере, при этом контейнером будет являться… просто директория на диске. Т.е. при запуске любого приложения оно может делать что угодно только в пределах его директории (в том числе обновлять себя и синхронизировать файлы в данной директории, для чего приложению открыт [возможно, ограниченный] доступ к локальной сети и Интернет).
Если приложение — это онлайн-игра, то ей в общем-то ничего больше и не нужно — достаточно возможности обновлять свои файлы с данными/ресурсами и хранить локальные настройки игрового клиента (настройки игровые разумнее хранить на сервере).
Если приложение — это текстовый или графический и какой угодно редактор, то для возможности редактирования какого-либо файла вне своей директории приложение обязано вызвать системную функцию (аналог OpenFileDialog() или SHBrowseForFolder() в Windows), которая посредством системной службы отобразит окно выбора файла, где пользователь выберет тот файл, который он хочет отредактировать и после выбора файла приложение будет иметь возможность читать и писать только этот выбранный пользователем файл. Если редактор был открыт путём клика мышью на иконке файла соответствующего типа, то редактор также может читать и писать только этот файл. Операционная система будет хранить список последних открытых файлов и давать возможность читать/писать их редактору без подтверждения со стороны пользователя.
С утилитами командной строки можно поступить аналогично: предоставлять доступ на чтение и запись всем файлам, присутствующим в списке аргументов командной строки.
Или можно поступить немного хитрее: ввести разделение для каждой программы на быстрые директории и медленные директории. В медленных директориях все изменения сохраняются, при этом есть ограничение на характер и количество изменений в единицу времени (для того, чтобы пользователь мог проверить их и откатить при желании). В быстрых такого ограничения нет и изменения в них не фиксируются. В самом простом случае к быстрым директориям можно отнести директорию самой программы и системную директорию для временных файлов/кэша. А к медленным — все остальные директории внешние по отношению к директории выполняемой программы.
Есть ещё и такая идея: расширение файла должно определять не просто «программу, которая открывает файлы данного типа», а «набор/множество программ, которые в принципе имеют право изменять файлы с таким расширением». К примеру, изменять файлы с расширением
.docx
или .xlsx
должно быть разрешено только ограниченному набору программ (офисных пакетов). Структура .docx
или .xlsx
файла достаточно сложная, это не plain text, и будет очень странно, если такой файл вдруг захочет модифицировать, скажем, программа-видеопроигрыватель [а такое может произойти, если пользователь ненароком установил расширение/плагин, содержащий, например, вирус-шифровальщик (ну или злоумышленник подменил плагин, встроив в него вредоносный код)]. Причём, если какая-то программа захочет модифицировать файл не своего типа, то нужно не выдавать окошко с ОК/Отмена, т.к. пользователи не всегда внимательно читают текст в таких окошках, а просто показывать информационное окно о том, что программа пыталась выполнить несанкционированную операцию и операция была отклонена, но если такую операцию действительно необходимо разрешить, тогда, уважаемый пользователь, соблаговолите залезть в настройки системы и руками добавить необходимое разрешающее правило.Также должна быть возможность установить разрешение на модификацию некоторых файлов только для некоторой программы или программ. К примеру, я веду свои записи в небольшом количестве текстовых файлов, с которыми я работаю исключительно в Sublime Text. И я бы хотел иметь возможность добавить правило, работающее на уровне операционной системы, которое бы запрещало открывать соответствующий файл на запись (т.е. вызывать функцию
CreateFile()
с dwDesiredAccess
установленным в GENERIC_WRITE
) для всех процессов, кроме sublime_text.exe
, который имеет определённую хэш-сумму и который лежит в определённой директории. Последнее нужно для того, чтобы злоумышленник не мог выполнить вредоносный плагин к Sublime Text, т.к. Sublime Text при запуске загружает плагины из своей директории и можно скопировать доверенный sublime_text.exe
в отдельную директорию с вредоносным плагином. Но все нужные мне плагины к Sublime Text я пишу сам используя тот же Sublime Text, поэтому все файлы в директории с Sublime Text разумно разрешить [опять же на уровне операционной системы] редактировать только через Sublime Text.Существует мнение, что на системном разделе не нужно хранить важные данные, чтобы в случае чего всегда можно было его отформатировать и легко переустановить систему. Я с таким мнением категорически не согласен. Во-первых, разделение физического диска на логические приводит к тому, что часть места остаётся недоступной для пользовательских данных с одной стороны [если на одном разделе свободно 20 Гб и на другом 20 Гб, то игрушку на 30 Гб поставить уже не получится], и с другой стороны есть риск нехватки места под систему в системном разделе, что приводит к серьёзной проблеме, т.к. свободно менять размеры непустых логических разделов [забирая место у одних и отдавая его другим] как правило нельзя. Во-вторых, я считаю, что операционной системе достаточно одной директории на диске. Зачем ей отдельный раздел? И в-третьих, переустановка системы потеряет все системные настройки и систему придётся настраивать заново, а то и переустанавливать программы. Все пользовательские настройки системы, как я считаю, должны лежать в понятном месте (в одной поддиректории внутри системной директории, к примеру), легко сохраняться/восстанавливаться (например переноситься со старого ноутбука на новый) и иметь поддержку версионирования "из коробки" — для этого настройки должны храниться в текстовом формате, дружественном для систем управления версиями (типа Git), чтобы можно было легко посмотреть какие конкретно изменения в настройках системы были сделаны за последнюю неделю например (и какая именно программа сделала эти изменения — это должно быть указано в commit message).
Неломающиеся символические ссылки
Жёсткие ссылки используют вместо "мягких"\soft из-за того, что последние ломаются. А что если сделать их неломающимися?
Концептуально символические ссылки в новой ОС я предлагаю реализовать в виде простого текстового файлика с расширением
.~
[я думал использовать юникодный символ 🔗 (U+1F517 Link Symbol), но решил использовать тильду, т.к. внешне она на него похожа и её можно набрать с клавиатуры]. Файлик этот при работе с файловой системой средствами ОС при обходе директории будет подменяться на объект (файл или директория), на который он ссылается. Также при обращении к объекту файловой системы в случае его отсутствия будет искаться файл с этим же именем плюс .~
в соответствующей директории, и если он найден, то объект по ссылке будет открываться "вместо" отсутствующего.Считаю разумным сделать возможность открытия файла
.~
для чтения: во-первых, это может служить простой проверкой типа объекта файловой системы (если существует файл <путь>.~
, значит <путь>
является символической ссылкой). И во-вторых, чтобы узнать, на какой объект указывает ссылка не нужны никакие специальные команды ОС или системные вызовы, а достаточно просто прочитать содержимое файла .~
.Кроме того, при создании первой символической ссылки на какой-либо файл/директорию рядом с целевым объектом создаётся дополнительная "фейковая" ссылка, которая содержит список всех ссылок, которые указывают на этот объект. Таким образом, при переименовании или перемещении любого файла
<имя_файла>
, ОС смотрит наличие <имя_файла>.~
и если такой файл есть, то ОС проходится по данному файлу и корректирует все символические ссылки, чтобы они указывали на новое местоположение файла.В случае удаления файла, на который есть одна или более символические ссылки можно поступать так: либо предлагать пользователю удалить все ссылки на этот файл, либо использовать поведение жёстких ссылок — при удалении файла, на который есть ссылки, он не удаляется, а просто перемещается в директорию к одной из ссылок, которые на него указывают. При этом корректируются все остальные символические ссылки, чтобы они указывали на новое местоположение файла.
Такие ссылки не требуют поддержки на уровне файловой системы и их можно помещать даже в zip-архивы.
Для обеспечения неломаемости ссылок внутри директории при её перемещении/переименовании, необходимо как-то обозначать, что внутри директории есть ссылки. Для этого я предлагаю использовать файл с именем директории с расширением
.~
. При переименовании директории, которая содержит ссылки, необходимо обойти все поддиректории, найти все ссылки, исправить их, и только затем переименовать директорию.Символические ссылки на директории, возможно, следует запретить, т.к. они являются источником проблем.
Хранимое время файлов. Правило трёх “c”
Однажды я задумался о том, как при копировании файлов [в том числе резервном копировании] поступать правильно: сохранять дату и время создания копируемого файла или устанавливать их в текущий момент, т.е. в момент копирования?
Вскоре у меня родился такой неожиданный ответ: правильно сохранять обе даты.
В итоге, я вижу смысл хранить следующие три времени:
- creation time (время создания)
- change time (время последнего изменения/модификации)
- copy time (время копирования)
При копировании файла время создания следует сохранять, т.к. пользователь работает не с какими-то кучками байтов, а с документами [представленными в цифровой форме], и разумно в свойствах файла документа видеть дату/время именно создания этого документа (время начала работы над этим документом), а не абстрактную цифру создания записи в таблице каталога. В таком случае можно оценить продолжительность работы над данным документом сравнив время модификации со временем создания (если эти времена совпадают, то это означает, что документ вообще не редактировался с момента создания).
А в чём польза времени копирования? Например, при копировании файлов с локального диска компьютера в папку на общем диске можно сортировать файлы в этой папке по времени копирования и тогда [при заходе на общую папку с другого компьютера, чтобы скачать/открыть только что скопированные файлы] сразу будет видно, какие файлы были скопированы в эту папку последними (при обычной сортировке по времени изменения скопированные файлы могут оказаться в середине списка). Аналогично при распаковке файлов из архива имеет смысл сохранять в copy time время распаковки файлов. Так, при распаковке архивов в директории Downloads в эту же директорию распакованные файлы/каталоги окажутся наверху списка [при сортировке по убыванию времени копирования] вне зависимости от того, когда они были созданы или изменены.
Также время копирования можно использовать как признак того, что данный файл был скопирован (у оригинального файла время копирования и создания совпадают, а у скопированного — отличаются).
Как хранить время копирования в существующих файловых системах? Я предлагаю сохранять его в access time (время доступа/обращения), т.к. как показала практика, смысла в нём нет никакого, потому что куча-куча программ читают файлы (это может быть служба индексирования, антивирусы, поиск и всё что угодно). Я достаточно часто выполняю [через Ctrl + Shift + F] поиск по свыше 6 тысячам текстовых файлов (все мои статьи, дневниковые записи, исходный код, сообщения/комментарии и пр.) в Sublime Text и это занимает всего несколько секунд. А если бы при этом у всех читаемых файлов обновлялась дата обращения, то этот поиск занимал бы времени наверное на порядок больше.
И по факту, современные операционные системы время доступа в большинстве случаев при обращении к файлам не трогают (в Windows обновление access time вообще отключено в реестре по умолчанию).
Но, разумеется, если дойдёт дело до новой файловой системы, то в записях каталогов разумно хранить только три выше обозначенные времени. А если вдруг понадобится хранить время доступа, то делать это можно не на уровне файловой системы, а на уровне операционной системы (вести журнал обращений к файлам).
А как же change time в Unix/Linux? [Которое отмечает изменение метаданных, а не содержимого файла.] Оно в новой ОС не нужно, т.к. не нужны… сами метаданные.
Я не очень опытный пользователь Linux и вероятно чего-то не понимаю, но на практике все эти права доступа (access permissions) только создавали проблемы и никак не помогали в обеспечении безопасности. В конце концов, в Windows их нет и надобности в них обычный пользователь точно никогда не испытает.
Признаком выполнимости файла, так же как и в Windows, в 9os является его расширение: например
.9e
[e — executable] или просто .9
.А вот по части скрытых каталогов: тут уже я на стороне Unix/Linux, в котором (в отличие от Windows) нет признака скрытости на уровне метаданных, а скрытые каталоги обозначаются соглашением в наименовании: каталог является скрытым, если его имя начинается на точку.
Насчёт скрытых файлов (нужны они или нет) я пока не определился. Со скрытыми каталогами проблем нет, т.к. у каталогов нет расширения, а у файлов — есть. И дело в том, что:
- достаточно часто я создаю файлы, у которых нет имени [т.е. имя пустое], а есть только расширение (и при этом такие файлы не являются скрытыми): например файл
.txt
содержит текстовое описание директории, в которой он находится; или.py
— Python-скрипт, который как-то обрабатывает файлы в его директории; или.md
— Markdown-файл, на основе которого генерируется index.html в этой же директории (файлindex.md
сгенерирует index/index.html, т.к.docs.md
генерирует docs/index.html); - многие файлы, начинающиеся с точки, по факту скрытыми не являются (например, файл
.gitignore
содержит вполне понятное текстовое содержимое, да ещё и версионится); - в принципе вместо скрытых файлов можно обойтись скрытыми каталогами — любой файл внутри скрытого каталога является скрытым.
Наконец, признак защищённости/системности на уровне метаданных файлов не нужен — системные файлы должны защищаться средствами ОС.
Пояснение по поводу ненужности всех метаданных файлов
Т.к. это довольно радикальное архитектурное решение, то я решил дать более подробное объяснение предлагаемой концепции.
Для начала обозначу такой момент, что в современном мире достаточно сложно гарантировать сохранность метаданных файлов — при синхронизации через облачные хранилища, при использовании систем управления версиями, при отправке по электронной почте — практически никакие метаданные, как правило, не будут сохранены (в первом случае можно рассчитывать на сохранение времени модификации файла, а во втором — на сохранение executable bit).
Но допустим, вам прям очень нужен атрибут файлов «только для чтения» (в терминах Windows или “immutable” в Linux). [Хоть лично я его ни разу не использовал, да и большинство пользователей, скорее всего, тоже.]
Можно вынести этот атрибут с уровня файловой системы на уровень операционной системы. Конкретно — ОС будет хранить в памяти HashMap с именами всех файлов только для чтения. Время/сложность поиска в HashMap — O(1). При грамотной реализации, к никакому даже малозаметному замедлению это не приведёт, т.к. проверка эта выполняется только при попытке записи файлов на диск, а операция записи достаточно редкая (по сравнению с чтением).
Далее. Если вы ставите атрибут «только для чтения» не интуитивно/наобум, а по какому-то чёткому признаку/условию, то можно это условие прописать прямо в виде алгоритма, выполняющегося на уровне операционной системы. Тогда вам достаточно будет один раз прописать этот алгоритм (например, запрет на модификацию файлов в определённой директории спустя сутки после копирования файлов в эту директорию) и система будет постоянно защищать ваши файлы вообще без лишних телодвижений с вашей стороны [т.е. без ручной установки атрибута «только для чтения»] путём выполнения кода типа
Для начала обозначу такой момент, что в современном мире достаточно сложно гарантировать сохранность метаданных файлов — при синхронизации через облачные хранилища, при использовании систем управления версиями, при отправке по электронной почте — практически никакие метаданные, как правило, не будут сохранены (в первом случае можно рассчитывать на сохранение времени модификации файла, а во втором — на сохранение executable bit).
Но допустим, вам прям очень нужен атрибут файлов «только для чтения» (в терминах Windows или “immutable” в Linux). [Хоть лично я его ни разу не использовал, да и большинство пользователей, скорее всего, тоже.]
Можно вынести этот атрибут с уровня файловой системы на уровень операционной системы. Конкретно — ОС будет хранить в памяти HashMap с именами всех файлов только для чтения. Время/сложность поиска в HashMap — O(1). При грамотной реализации, к никакому даже малозаметному замедлению это не приведёт, т.к. проверка эта выполняется только при попытке записи файлов на диск, а операция записи достаточно редкая (по сравнению с чтением).
Далее. Если вы ставите атрибут «только для чтения» не интуитивно/наобум, а по какому-то чёткому признаку/условию, то можно это условие прописать прямо в виде алгоритма, выполняющегося на уровне операционной системы. Тогда вам достаточно будет один раз прописать этот алгоритм (например, запрет на модификацию файлов в определённой директории спустя сутки после копирования файлов в эту директорию) и система будет постоянно защищать ваши файлы вообще без лишних телодвижений с вашей стороны [т.е. без ручной установки атрибута «только для чтения»] путём выполнения кода типа
if file.path.startswith(".../protected/dir/name/") and time.now() - file.copy_time > 24*3600: raise WriteForbidden()
.Зависимости
Тут я на стороне Windows.
Хочу привести вот этот комментарий, с которым я практически полностью согласен:
habr.com/ru/companies/timeweb/articles...:А также рекомендую к прочтению великолепную статью Джоэла Спольски ‘Пожалуйста, сэр, могу ли я получить компоновщик?’, она совсем небольшая.
В GNU/Linux обычно используется динамическая линковка библиотек. В Windows приложения все зависимости тащат с собой.И вот в итоге время и практика нам наглядно показывают, что выбранный в винде путь — правильнее. Потому что достаточно часто надо иметь и новую и старую версию софта одновременно. Например, новая уже что-то не умеет делать из того, что умела старая.
Вот пара цитат из неё с моими комментариями.
Я просто хочу все скомпоновать в один статический EXE файл, который для установки не предъявляет никаких особых требований. И мне не важно, что он несколько больше. Все что мне было бы необходимо — это функции, которые я действительно используюЯ также всегда старался придерживаться этого подхода. При сборке C++ проектов в MSVC я предпочитаю выбирать опцию
/MT
[в C/C++ -> Code Generation -> Runtime Library] для статической «линковки»/компоновки MSVC runtime libraries. В этом случае скомпилированный exe-шник не будет требовать установки свежего Visual C++ Redistributable (vcredist_x64.exe) для запуска. А ещё, например при разработке компьютерных игр вместо XAudio2 я использовал старый добрый DirectSound, т.к. он гарантированно поддерживается даже на голой Windows XP, а при использовании XAudio2 необходимо на машине пользователя устанавливать свежий Microsoft DirectX Runtime (ну т.е. версии не ниже той, что установлена на машине разработчика). Вспомогательную библиотеку D3DX от Microsoft я не использовал по этой же причине — поставляется она также в DirectX Runtime, а класть только необходимые dll-ки рядом с исполняемым файлом Microsoft запрещает. Свежий Microsoft DirectX Runtime во времена DirectX 9 (уж не знаю как обстоят дела сейчас с DX12) вообще никак не влиял на работоспособность 3D-игр — для них нужен был только графический драйвер от NVIDIA или от ATI/AMD. И было очень приятно иметь возможность просто скопировать игру на флешку и запустить на другом компьютере ничего не устанавливая.Среды исполнения — это проблема, очень похожая на проблему с DLL, потому что если приложение 1 было разработано для среды исполнения 1, и выходит среда выполнения версии 2, то внезапно, по какой-то непредсказуемой причине приложение 1 перестает правильно работать.В мире Linux, похоже, наконец-то это поняли и изобрели… контейнеры. Которые содержат в себе все необходимые приложению динамические библиотеки. А может стоило сразу просто использовать статическую компоновку и держать файлы конфигурации рядом с приложением, а не раскиданными по всей файловой системе?
Интерфейс командной строки
Для записи всех часто используемых команд я решил обойтись без английского языка. И без русского. И вообще без какого-либо другого естественного языка, а используя только символьные обозначения.
Вот как это выглядит на конкретных примерах:
Windows | Unix | 9os |
---|---|---|
move fname dir |
mv fname dir |
fname -> dir/ |
rename fname_old fname_new |
mv fname_old fname_new |
fname_old -> fname_new |
copy fname destdir |
cp fname destdir |
fname => destdir/ |
copy fname copy_fname |
cp fname copy_fname |
fname => copy_fname |
xcopy docs docs_copy |
cp -r docs docs_copy |
docs/** => docs_copy/ |
dir /W |
ls |
? . |
dir |
ls -l |
? * |
dir /W subdir |
ls subdir |
? subdir |
dir subdir |
ls -l subdir |
? subdir/* |
fsutil volume diskfree C: |
df / -h |
? 0: |
del filename |
rm filename |
- filename |
md newdir |
mkdir newdir |
+ newdir/ |
cd subdir |
. = subdir |
|
cd.. |
cd .. |
. = .. или .=.. |
Пример продвинутых команд с использованием символьной записи
Для вывода файлов в текущей директории, как видно из предыдущей таблицы, используется команда
В случае
В составной команде
Но часто требуется вывести файлы не просто с информацией об их размере или времени модификации, но и с сортировкой. Для этого необходимо к команде
Для ограничения количества выводимых файлов нужно просто добавить цифру/число к суффиксу сортировки:
Без указания суффикса сортировки [
? *
. При этом [в отличие от команды ? .
] напротив имени каждого файла выводится его размер. Но что, если хочется увидеть не только размер, но и время создания и время модификации файла? Для такой операции можно использовать команду ?+* *
либо ?*+ *
.В случае
?+* *
формат каждой строки вывода будет такой:<время_создания> <время_модификации> <размер> <имя_файла>
А в случае команды ?*+ *
такой:<время_модификации> <время_создания> <размер> <имя_файла>
В составной команде
?+*
:+
обозначает время создания.*
обозначает время модификации/‘последнего изменения’.
=
и, соответственно, для вывода всех трёх времён можно использовать команду ?+*=
.Но часто требуется вывести файлы не просто с информацией об их размере или времени модификации, но и с сортировкой. Для этого необходимо к команде
?
добавить суффикс /\
для сортировки по возрастанию или суффикс \/
для сортировки по убыванию. Например:?+*/\+ *
— сортирует по времени создания в порядке возрастания.?+*\/* *
— сортирует по времени модификации в порядке убывания.?+*\/ *
— сортирует по размеру в порядке убывания. (Т.е. размер файла никак не обозначается, а подразумевается неявно.)Для ограничения количества выводимых файлов нужно просто добавить цифру/число к суффиксу сортировки:
?+*/\+5 *.txt
— выводит 5 первых созданных txt-файлов в текущей директории.?+*\/1 *.png
— выводит самый большой png-файл в текущей директории.Без указания суффикса сортировки [
/\
или \/
] файлы сортируются по алфавиту без учёта регистра. При этом:- возможность сортировки по алфавиту в обратном порядке не предусмотрена [т.к. это в общем-то не имеет смысла];
- возможность ограничения количества выводимых файлов не предусмотрена (точнее, в этом случае действует ограничение по умолчанию: 1000).
?
поддерживает опциональный модификатор /
, который активирует расчёт размера директорий. Это может быть полезно для вывода объёма занимаемого места на диске конкретной директорией: ?/ subdir/
, а также для того, чтобы при сортировке по размеру учитывался объём директорий. Так, команда ?/\/10 downloads/*/
выведет 10 самых объёмных поддиректорий в директории downloads
.Насколько такие команды удобны в работе ещё предстоит выяснить. Впрочем, попробовать можно уже сейчас. Вот экспериментальная версия командной оболочки 9os. Особенностью данной ознакомительной версии является то, что никакие команды, введённые в ней, не способны причинить необратимый вред системе. Абсолютно любую введённую команду можно отменить (undo) специальной командой
!
. Чтобы вернуть (redo) отменённую команду [«отменить отмену»] введите !!
. Для отмены трёх последних команд используйте !3
. Для отмены всех команд: !*
. Для возвращения, соответственно: !!*
.Команда
!?
[или ?!
] показывает, какое действие будет отменено. А !!?
— какое действие будет возвращено. Все «вопросительные» команды (?
, !?
, !!?
) ничего не изменяют в файловой системе, поэтому отменить их нельзя.Сообщения об ошибках также обходятся без слов на естественном языке. Вот, к примеру, варианты ошибок при выполнении команды
filename -> dir/
:filename -> dir/ |
filename не найден |
filename -> dir/ |
dir не является директорией |
filename -> dir/ |
не удалось переместить файл (при перемещении файла возникла какая-то другая ошибка) |
filename -> dir/
обязателен (без него dir
будет рассматриваться не как директория, а как новое имя файла). И если попытаться выполнить команду filename -> dir
при условии что dir
является директорией, то возникнет такая ошибка:filename -> dir/
Зелёный слэш в конце строки является напоминанием того, что для перемещения в директорию нужно обязательно указывать слэш.
Почему?
Вообще говоря, улучшение читаемости команды перемещения за счёт этого слэша получается незначительное. Т.к. на практике имя файла довольно легко отличить от имени директории хотя бы тем, что у файла как правило есть расширение, а у директории расширения нет. Но строгость (различие команды перемещения в директорию от переименования) здесь полезна больше тем, что уменьшается вероятность ошибиться и выполнить неправильную команду. Так, если пользователь захотел переместить файл
filename
в директорию dir
, но немного ошибся в имени директории, то команда mv filename dirr
выполнится успешно, только вместо перемещения файла в директорию, она переименует этот файл в бессмысленное имя dirr
. В случае же команды filename -> dirr/
возникнет ошибка, т.к. директории dirr
не существует (к слову, команда mv filename dirr/
сработает аналогично, при этом команда mv filename dir/
сработает корректно, т.е. моя претензия лишь к недостаточной строгости mv
).Если в команде переименования файла целевое имя содержит недопустимые символы для файловой системы [
\/:*?"<>|
], то красным выделятся именно эти недопустимые символы:filename -> file:n<am>e
Также я считаю, обязательно нужно как-то давать понять пользователю успешно завершилось выполнение команды/процесса или нет. Идеология Unix, когда программа в случае успеха ничего не печатает в консоли — это хорошо, но как отличить ошибочный вывод от успешного? ‘Exit code’\‘код завершения’ процесса как-то должен быть обозначен визуально, причём ненавязчиво [выводить число не нужно, достаточно знать нулевой код или нет — точное значение при желании пользователь может узнать отдельной командой].
Стандартные поддиректории системной директории и системного диска
- ~ — директория со ссылками (поэтому и называется так) на утилиты, доступные из командной строки (аналог /bin и /usr/bin в Linux, но в отличие от Linux допускается создавать поддиректории: например в поддиректории
~/python/
хранятся символические ссылки на различные установленные версии Python, а ссылка~/python.~
указывает на конкретную версию, которая используется при выполнении в командной строкеpython <имя_скрипта.py>
); - 0 — директория автозагрузки (аналог /etc/init.d в Linux и shell:startup в Windows);
- 0:/@ — кэш\cache (т.к. символ @ похож на C и a); кэш приложений имеет смысл хранить централизованно для возможности быстрой очистки кэша сразу всех приложений (очищать кэш можно в любое время без вреда для приложений/системы и пользовательских данных), но хорошие приложения должны создавать в своей папочке символическую ссылку @.~, которая ссылается на соответствующую поддиректорию в системном кэше [это позволит посмотреть объём кэша конкретного приложения];
- 0:/.- — директория истории операций с файловой системой (нужна для поддержки undo/redo, в ней хранятся помимо истории [в файле с именем
.!
] сами удалённые файлы); обоснование выбора такого странного на первый взгляд [но очень логичного, на самом деле] названия директории можно посмотреть здесь.
Также предлагаю сделать специальный диск
@:
— диск в памяти (RAM drive) для файлов кэша, которым не нужна энергонезависимая память. Т.е. для файлов, время жизни которых ограничено временем работы системы.Встроенный язык программирования в 9os
Стандартные языки командных оболочек ОС Linux [Bash] и Windows [batch file] хорошо подходят лишь для достаточно простых задач. Но бывает так, что задуманный для решения простой задачи скрипт потихоньку обрастает функционалом и его в итоге приходится переписывать на полноценном языке программирования, либо продолжать мучительно поддерживать при отсутствии не только удобного синтаксиса и поддержки сложных типов-контейнеров, но и нормальной отладки.
Поэтому считаю разумным сделать язык файлов сценария 9os просто подмножеством полноценного компилируемого (но очень близкого к Python) языка — 11l. Доступ к командам операционной системы в 11l упрощён настолько, насколько это вообще возможно для языка высокого уровня без использования backticks (как в Perl, PHP, Ruby). Например, чтобы в Python выполнить команду
gcc test.c
необходимо написатьimport os
os.system('gcc test.c')
В 11l же, во-первых, имя функции модуля может быть пустым, а т.к. функция system()
является функцией по умолчанию в модуле os
, то вместоos:system(‘gcc test.c’)
можно писать простоos:(‘gcc test.c’)
Во-вторых, когда аргумент функции по умолчанию модуля всего один, можно опустить скобки и писатьos:‘gcc test.c’
И в-третьих, синтаксис обращения к функциям модуля в 11l позволяет не только использовать двухбуквенные и даже однобуквенные имена модулей [без конфликтов с именами переменных/функций и другими идентификаторами], но имена модулей могут начинаться с цифры или вообще состоять лишь из одной цифры!В данном случае вместо
os
можно использовать 9
:9:‘gcc test.c’
Хотя для команд с несколькими аргументами лучше использовать массив:9:[‘gcc’, ‘test.c’]
// Имя файла всё равно скорее всего будет браться из переменной:
9:[‘gcc’, cfile_name]
И в 9os будет возможно исполнять файлы с расширением [не считая двоичных файлов
.9
]:.11l
— это классический 11l;.11
— 11ll (11lInternationallocalized) — это локализованное подмножество 11l (для исходных файлов, полностью написанных на национальном языке, т.к. было бы странно, когда весь код внутри файла написан, к примеру, на русском [примеры кода на русском 11l можно посмотреть в этой статье], имя файла — тоже на русском, а в расширении присутствует латинская буква «l»);.911
— по сути тот же 11ll, отличие только в том, что модулем по умолчанию является9
, т.о. вместо9:‘gcc test.c’
можно писать просто:‘gcc test.c’
(хоть экономия в один символ не такая большая, но когда файл состоит в основном из команд операционной системы эти девятки будут мозолить глаза).
.911
решает проблему переписывания файлов сценария ОС, т.к. когда возможностей .911
станет не хватать, перейти на полноценный 11l можно посредством простой автоконвертации. Более того, этот автоматический конвертер собственно и будет каждый раз вызываться и на исполнение будет отдаваться именно 11l-код. Это даст возможность полноценной отладки, а также сверхбыстродействие (т.к. 11l работает в 20-500 раз быстрее Python [см. раздел ‘Производительность’ на сайте 11l]).Впрочем, такая «автоконвертация», вообще говоря, тривиальна, т.к. 11l итак позволяет назначать модуль по умолчанию используя механизм псевдонимов модулей: так, запись
np: = numpy:
в 11l является аналогом import numpy as np
в Python. А строка : = 9:
в 11l коде назначит 9
модулем по умолчанию. Тогда чтобы получить из файла .911
файл .11
достаточно добавить в начало эту строку кода [: = 9:
]. И главное назначение расширения .911
не в том, чтобы установить модуль по умолчанию (т.к. это сделать тривиально [и сбросить модуль по умолчанию тоже тривиально: строкой кода -:
]), а в том, чтобы показать, что данный файл требует для запуска именно 9os, т.к. просто .11
файлы являются кроссплатформенными.Выполнение пользовательских приложений в режиме ядра
В целом, это не новая идея. Существует множество веб-серверов, работающих в режиме ядра [которые, однако, не пользуются популярностью]. И существуют операционные системы [которые также не пользуются популярностью], реализующие «защиту на уровне языка» (language-based protection) — это когда вместо разделения на режим ядра (0 кольцо защиты) и режим пользователя (3 кольцо) весь код исполняется в режиме ядра (0 кольце), а безопасность/защита [невозможность залезть в память других процессов и ядра операционной системы] обеспечивается средствами языка программирования, что, потенциально, может дать существенный прирост производительности, т.к. нет разделения адресного пространства для различных процессов (а переключать адресное пространство достаточно дорого) и нет необходимости в переключениях из пользовательского режима в режим ядра при выполнении любого системного вызова (так, простой вызов
getpid()
обходится в 1000–1500 тактов).Вопрос «почему эта идея так и не взлетела» я предлагаю отложить в сторону, а более полезно будет подумать над возможностью опционального выполнения кода в режиме ядра. В таком случае, основным способом выполнения процессов/приложений останется традиционный (с разделением на пользовательский режим и режим ядра), но будет возможность выполнять те же самые приложения в режиме ядра, а также запускать всю систему в особом режиме, когда разделение режимов отсутствует и весь код исполняется в режиме ядра. Такая возможность позволит сравнивать производительность и стабильность выполнения одних и тех же приложений в различных режимах. (Здесь под «тех же самых» и «одних и тех же» подразумевается, что исходный код приложений будет такой же, но компилировать его, очевидно, нужно по-разному — в режиме ядра недопустимо использовать инструкции прерываний, поэтому исключения придётся реализовывать по-другому, например, используя скрытое возвращаемое значение у функций, как это сделано в языке Swift.)
Упрощённое взаимодействие с устройствами
Драйверы в новой ОС разумеется должно быть возможно писать на языке высокого уровня с поддержкой исключений. Но можно пойти ещё дальше. Дело в том, что для типичных внешних/периферийных устройств на типичном пользовательском компьютере уже не требуется максимальное быстродействие — задержки в несколько микросекунд или даже миллисекунд при общении с устройством вполне достаточно для хорошего пользовательского опыта. Поэтому работу с внешними устройствами вполне возможно перенести из режима ядра в режим пользователя. Что конкретно я предлагаю?
В Unix есть такое понятие как «файл устройства». Я же предлагаю «веб-сервер устройства». Современные домашние ПК и ноутбуки легко справляются с гигабитным сетевым соединением. Такой производительности вполне достаточно для любого периферийного/внешнего устройства.
Так вот, я предлагаю, чтобы при подключении устройства к компьютеру операционная система создавала на IP-адресе 127.0.0.1 [и
::1
в IPv6] виртуальный «слушающий» сокет с номером порта, соответствующим подключенному устройству. Нумерация может выглядеть как-то так:Порт | Описание/название устройства |
---|---|
900 | список всех доступных устройств |
910 | клавиатура |
920 | мышь |
950 | принтер (почему именно 5 — слова «принтер» и «пять» начинаются на букву «п») |
960 | сканер\scanner (почему 6 — слова “scanner” и “six” начинаются на букву “s”) |
930 | "тачскрин"\touchscreen (три\three) |
В результате, работа с устройством будет не сложнее, чем написание клиентского приложения на вашем любимом языке программирования.
Например, недавно у моей мышки сломалось колёсико. Я заменил её на Logitech G102 LightSync [которую мне подарили 2 или более года назад]. И у этой мышки очень яркая подсветка, которая никакими кнопками на самой мышке не отключается. Пришлось искать программный способ для этого. Единственный вариант, который я смог найти — это официальное приложение Logitech G HUB. Установка этого приложения выжрала 1.5Гб места на диске. Отключить с его помощью подсветку получилось, но, увы, мышка не запоминает настройки подсветки и после закрытия приложения снова светит на максимальной яркости, поэтому пришлось оставить эту кучу проприетарного
import requests
requests.get('http://127.0.0.1:921?lights=off')
Аналогично, чтобы отправить pdf-файл на печать достаточно такой строки кода:requests.post('http://127.0.0.1:950', files={'file': open('report.pdf', 'rb')})
Кроме этого, должен быть механизм для добавления «пользовательских перехватывающих веб-серверов устройств». Например, пользователь хочет использовать какую-нибудь хитрую раскладку клавиатуры, которую никакой AutoHotkey реализовать не может (к примеру, КЭРа). Для этого запускается специальный сервер-посредник, который перехватывает все события (коды нажатых клавиш) физической клавиатуры и преобразует их в новые события, которые и видят все остальные приложения.
Практическая реализация
Перед полноценной реализацией (впрочем, для ограниченного набора аппаратного обеспечения) есть смысл сконцентрироваться на более практичном решении — реализовать 9os в режиме подсистемы для Windows [и возможно Linux], т.е. WS9 — Windows Subsystem for 9 [os], по аналогии с WSL — Windows Subsystem for Linux.
В этом режиме сохранится возможность запускать нативные Windows-приложения и не будет проблем с работой устройств. При этом появятся дополнительные возможности 9os:
- Безопасная работа приложений — при исполнении любого процесса из под WS9 системные функции (CreateFile, CreateProcess, FindFirstFile и прочие) подменяются [с помощью какого-нибудь Microsoft Detours или ApiHijack] на безопасную реализацию из WS9.
- Возможность работать с zip- и 7z-архивами как с обычными директориями. Особенно полезно для архивов с очень большим количеством файлов, т.к. файлы распаковываются не все сразу, а только по требованию (перехваченным вызовом CreateFile) и не во временную директорию на диске, а в оперативную память. При изменении или добавлении файлов в такую "директорию" можно не модифицировать zip-файл, а создавать рядом с zip-файлом директорию
имя_архива.zip+
, в которой будут храниться изменённые или добавленные файлы (при этом с точки зрения приложений эта директория не видна, а видно только одну zip-директорию). - В отличие от стандартного поведения Windows при запуске приложений когда им не хватает какой-то динамической библиотеки, WS9 не будет выдавать бессмысленные [для типичного пользователя] сообщения об ошибках типа ‘Не найдена библиотека mscoree.dll’, а просто автоматически скачает и установит все необходимые для запуска приложения компоненты и библиотеки (Visual C++ Redistributable, .NET Framework нужной версии и прочее).
- Доступ к периферийным устройствам [хотя бы некоторым] через веб-сервер устройства, как было предложено в предыдущем разделе. Для этого WS9 будет содержать специальный «мастер-драйвер».
- Виртуализация устройств. Поддержка устройств, для которых отсутствует драйвер в данной версии Windows, посредством скрытой виртуальной машины с ОС, которая это устройство поддерживает. Такое можно проделывать вручную, но я предлагаю подумать над автоматизированным решением проблемы подключения старых устройств к новым ОС или даже наоборот.
В качестве платформы для разработки 9os я выбрал GitLab вместо GitHub по нескольким причинам:
- Имя 9os в GitHub уже давно было занято (хотя на всякий случай я зарегистрировал 9-os на GitHub).
- GitHub не поддерживает группы репозиториев с произвольной вложенностью, в результате все репозитории будут свалены в одну кучу, либо придётся изворачиваться с дополнительными организациями с префиксом
9os-
в названии. - GitHub не позволяет использовать в бесплатном аккаунте [а платить из РФ не очень удобно] в приватных репозиториях GitHub Pages.
- Над GitHub «работает слишком много людей». В результате, его интерфейс становится всё более перегружен и неповоротлив. Это заметно хотя бы по тому факту, что просматривать файлы исходного кода в несколько тысяч строк кода на GitHub уже сейчас довольно некомфортно на бюджетном ноутбуке. Скорее всего, тормозит механизм поиска определений функций и других символов, который отключить невозможно (скрытие панели символов не помогает). GitLab же, пока ещё, не успел обрасти подобным функционалом и просмотр кода в нём работает достаточно шустро.