Win9X/NT. Windows, в отличие от MS DOS, представляет собой многозадачную операционную систему, созданную для одновременной работы ряда приложений и/или меньших процессов с максимальными возможностями использования аппаратного обеспечения. Это означает, что Windows является разделяемой средой: ни одно приложение не может получить в свое распоряжение всю систему целиком. Хотя Windows 95, 98, ME, ХР и 2000/NT похожи, они имеют ряд технических отличий. Однако в этой книге рассматриваются общие черты, а не отличия, так что делать большую драму из различия операционных систем не стоит.
Windows – это многозадачная операционная система, то есть она может одновременно выполнять две и более программ. Конечно, программы используют единственный процессор и, строго говоря, выполняются не одновременно. Однако высокое быстродействие компьютера создает такую иллюзию. Windows поддерживает два типа многозадачности: процессную и потоковую.
1. Процесс – это программа (или приложение – в терминологии Windows), находящиеся в фазе выполнения. Процессная многозадачность заключается в том, что Windows может выполнять одновременно более одной программы. Таким образом, Windows поддерживает «традиционную» процессную многозадачность, с которой Вы, вероятно, знакомы.
2. Поток – это отдельно выполняемая и управляемая часть программы. Название происходит от термина «поток выполнения». Любой процесс имеет как минимум один поток. В Windows процесс может иметь несколько (много) потоков.
Тот факт, что Windows способна управлять потоками, и каждый процесс может иметь несколько потоков, означает, что любой процесс может иметь две или более частей, выполняющихся одновременно. Следовательно, работая в Windows, можно одновременно выполнять как несколько программ, так и частей отдельной программы. Вы увидите ниже, что это свойство делает возможным написание очень эффективных программ.
Для доступа к системе интерфейс в Windows использует множество функций, определенных в ней. Это множество функций называется Программным Интерфейсом Приложений (Application Program Interface, API). API содержит несколько сотен функций, которые программа пользователя может вызывать для доступа к Windows. Функции включают все необходимые системно-зависимые действия, такие как выделение памяти, вывод на экран, создание окон и т.п. Аналогичные WinAPI средства имеются и в современных версиях операционной системы Unix.
Windows позволяет выполняться нескольким приложениям одновременно, при этом каждое приложение по очереди получает малый отрезок времени для выполнения, после чего наступает черед другого приложения. Как показано на рис. 1.1, процессор совместно используется несколькими выполняющимися процессами. Точное определение, какой именно процесс будет выполняться следующим и какое процессорное время выделяется каждому из приложений, – задача планировщика.
Планировщик может быть очень простым, обеспечивающим выполнение каждого из процессов одинаковое количество миллисекунд, а может быть и очень сложным, работающим с учетом различных уровней приоритета приложений и вытесняющим низкоприоритетные приложения. В Windows 9X/NT используется вытесняющий планировщик, работающий с учетом приоритетов. Это означает, что одни приложения могут получить больше процессорного времени, чем другие.
Однако беспокоиться о работе планировщика не стоит, если только Вы не разработчик операционной системы или программы, работающей в реальном времени. В большинстве случаев Windows сама запустит и спланирует приложение, и с вашей стороны для этого не требуется никаких специальных действий.
Познакомившись с Windows поближе, мы увидим, что это не только многозадачная, но и многопоточная операционная система. Это означает, что в действительности программы состоят из ряда более простых потоков выполнения. Выполнение этих потоков планируется так же, как и выполнение более мощных процессов, таких, как программы. Вероятно, в настоящий момент на вашем компьютере работает от 30 до 50 потоков, выполняющих разные задачи. Итак, в Windows единая программа может состоять из одного или нескольких потоков выполнения.
На рис. 1.2 схематически показана многопоточность в Windows. Как видите, каждая программа в действительности состоит, в дополнение к основному потоку, из нескольких рабочих потоков.
Для развлечения посмотрим, сколько потоков выполняется на вашей машине в настоящий момент. Нажмите на компьютере под управлением Windows для вызова Active Program Task Manager и посмотрите, чему равно количество выполняющихся в системе потоков (или процессов). Это не совсем та величина, которая нас интересует, но весьма близкая к ней. Нас интересует приложение, способное сообщить реальное количество выполняющихся процессов. Для этого подходит множество условно бесплатных и коммерческих программ, но они нас не интересуют, поскольку в Windows есть встроенное средство для получения этой информации.
В каталоге Windows (в большинстве случаев это папка \WINDOWS) можно обнаружить программу SYSMON.EXE (она не включена в установку Windows по умолчанию, так что при ее отсутствии просто добавьте ее в систему посредством Control Panel ? Add/Remove Programs ? System Tools) или, в Windows NT, PERFMON.EXE. Данная программа предоставляет и другую важную информацию, такую, как использование памяти и загрузка процессора. Часто эта программа используется, чтобы отслеживать, что происходит при работе создаваемых программ.
А теперь о приятном: Вы можете сами управлять созданием потоков в своих программах. Это одна из наиболее увлекательных возможностей при программировании игр – мы можем создать столько потоков, сколько нам потребуется для выполнения различных задач в дополнение к основному процессу игры.
Замечание: в Windows 98/NT введен новый тип объекта выполнения – нить (fiber), который еще проще, чем поток.
Вот основное отличие игр для Windows от игр для DOS. Поскольку DOS – однозадачная операционная система, в ней после запуска программы на выполнение больше ничего другого выполняться не может (не считая время от времени вызываемых обработчиков прерываний). Следовательно, если вы хотите добиться многозадачности или многопоточности в DOS, вам придется эмулировать ее самостоятельно (см., например, книгу Teach Yourself Game Programming in 21 Days, где описано многозадачное ядро на основе DOS). И это именно то, чем многие годы занимались программисты игр. Конечно, эмуляция многозадачности и многопоточности никогда не будет такой же надежной, как реальная многозадачность и многопоточность в поддерживающей их операционной системе, но для отдельной игры такой эмуляции вполне достаточно.
Перед тем как перейти к программированию в Windows, нужно упомянуть еще одну деталь. Вы можете подумать, что Windows – «волшебная» операционная система, поскольку позволяет одновременно решать несколько задач и выполнять несколько программ. Но это не так. Если в системе только один процессор, то одновременно может выполняться только один поток, программа или другая единица выполнения. Windows просто переключается между ними так быстро, что создается иллюзия одновременной работы нескольких программ. Если же в системе несколько процессоров, то несколько задач могут выполняться действительно одновременно. Например, есть компьютер с двумя процессорами Pentium II 400MHz, работающий под управлением Windows 2000. В этой системе действительно возможно одновременное выполнение двух потоков инструкций. В ближайшем будущем следует ожидать новую архитектуру микропроцессоров для персональных компьютеров, которая обеспечит одновременное выполнение нескольких потоков как часть конструкции процессора. Например, процессор Pentium имеет два модуля выполнения – U- и V-каналы. Следовательно, он может одновременно выполнять две инструкции, однако эти инструкции всегда из одного и того же потока. Аналогично, процессоры Pentium II, III, IV также могут выполнять несколько инструкций одновременно, но только из одного и того же потока.
Модель событий. Windows является многозадачной и многопоточной операционной системой, но при этом она остается операционной системой, управляемой событиями (event-driven). В отличие от программ DOS, большинство программ Windows попросту ждут, пока пользователь не сделает что-то, что запустит событие, в ответ на которое Windows предпримет некоторые действия. На рис. 1.3 Вы можете рассмотреть работу этой системы. Здесь изображены несколько окон приложений, каждое из которых посылает свои события или сообщения Windows для последующей обработки. Windows выполняет обработку определенных сообщений, но большинство из них передаются для обработки вашему приложению.
Хорошая новость состоит в том, что Вам нет необходимости беспокоиться о других работающих приложениях – Windows сама разберется с ними. Все, что вы должны сделать, – это позаботиться о вашем собственном приложении и обработке сообщений для вашего окна (окон). Ранее, в Windows 3.0/3.1, это было не так. Эти версии Windows не были истинно многозадачными операционными системами, и каждое приложение должно было передать управление следующему. Это означало, что если находилось приложение, которое ухитрялось надолго захватить систему, другие приложения ничего не могли с этим поделать.
Теперь о концепциях операционной системы Вам известно почти все. К счастью, Windows настолько хорошо подходит для написания программ, что Вам не нужно заботиться о планировании, – от Вас требуется лишь программный код приложения.
Далее Вы встретитесь с реальным программированием и увидите, насколько простое это занятие. Но (всегда это «но»!) пока что Вам следует познакомиться с некоторыми соглашениями, используемыми программистами Microsoft. Применяя их, Вы никогда не запутаетесь среди имен функций и переменных.
Динамические библиотеки
Поскольку API содержит несколько сотен функций, можно предположить, что каждая программа для Windows должна связываться с большим количеством библиотек, и это может привести к дублированию большого объема кода. Однако это не так. Вместо обычных библиотек функции Windows API объединены в динамические библиотеки (Dynamic Link Library, DLL), доступ к которым может получить любая программа во время выполнения. В настоящем разделе Вы познакомитесь с тем, как работает динамическое связывание. Функции API Windows хранятся в перемещаемом формате в DLL. В процессе компиляции, когда программа вызывает функцию API, компоновщик не добавляет код этой функции к исполняемому модулю. Вместо него он добавляет только инструкции для загрузки функции, содержащие имя DLL, в которой находится функция, и ее имя. При выполнении программы все необходимые функции API также загружаются в память. Таким образом, при построении программы код функций API фактически не используется – он добавляется только тогда, когда программа загружается в память для выполнения.
Динамическое связывание имеет ряд важных преимуществ. Во-первых, поскольку практически все программы используют функции API, DLL сохраняет место на диске, не дублируя объектный код в выполняемых файлах. Во-вторых, дополнения и расширения Windows могут ограничиваться изменением программ в отдельных динамических библиотеках, и существующие приложения не будут нуждаться в перекомпиляции.
Приложения Windows могут использовать до 16 Гбайт виртуальной памяти! Более того, эти 16 Гбайт адресуются прямо, без переключения сегментов. В отличие от других операционных систем, которые используют сегментированную память, Windows рассматривает адресное пространство задачи как линейное. И поскольку она виртуализирует память, то каждое приложение может занять столько памяти, сколько (в разумной мере) пожелает. Так как прямая адресация более понятна программисту, она позволяет избежать опасности, связанной с использованием прежнего сегментного подхода.
Windows использует схему переключения задач с автовыгрузкой (preemptive multitasking), базируясь на временных квантах. Отработав некоторое время, задача в Windows автоматически выгружается системой и управление передается следующей задаче (если таковая имеется). Такая схема переключения является более предпочтительной, поскольку позволяет операционной системе полностью контролировать все задачи и предохраняет ее от блокирования одной задачей. Большинство программистов рассматривают схему переключения задач с автовыгрузкой как более прогрессивную.