Мой основной вопрос состоит в том что, возможно ли во время работы программы как-то(
с помощью любых средств ) узнавать где находится переменная,хотя бы засечь момент когда
она переносится в файл подкачки.
А также у меня вопрос. Правильно ли я понимаю, что файл подкачки используется только
когда не хватает места в оперативной памяти или если есть долго не использующиеся переменные?
Можно ли задать адрес переменной,а потом по ходу программы смотреть находится ли
эта переменная по заранее заданному адресу в оперативной памяти(на c# с помощью .Net).
И соответственно если ее не будет в оперативной памяти, то она находится в файле подкачки.
Так ли работает адрес у переменной, или он не меняется вне зависимости от того где
находится?
Ответы
Ответ 1
возможно ли во время работы программы как-то узнавать где находится
переменная,
С уровня софта - нет. С уровня драйвера, или уровня ядра - можно. (через каталог
страниц виртуальной памяти). Как конкретно - это очень специфическая область - нужно
копать DDK (Driver Development Kit).
UPD: Виртуальный адрес переменной (я бы назвал обычный), при смене физического адреса
- не меняется. Физический адрес нужен для DMA (используется в драйверах HDD, USB, сетевой
и звуковой карт). Если адрес существует в физической памяти - он всегда будет иметь
физический адрес (но при запросе физического адреса он вероятне всего будет назначен).
хотя бы засечь момент когда она переносится в файл подкачки.
С уровня драйвер - может быть. Возможно в ядре есть обработчик, скидывающий страницу
на диск - найти и перехватить его займет какое-то время (от месяца до года), и требует
хороших знаний в этой области.
А также у меня вопрос. Правильно ли я понимаю, что файл подкачки используется только
когда не хватает места в оперативной памяти или если есть долго не использующиеся переменные?
Алгоритм распределения памяти сложный и сложно-предсказуемый, но есть особенности,
я постараюсь все перечислить. Ответ зависит как минимум от двух факторов. Если суммарная
памяти всех программ меньше чем обьем оперативки - то да, память может находится в
физической памяти. Если нет (сегодня такая ситуация что чаще нет) - то есть большая
вероятность, что память может оказаться в файле подкачки.
Так же, в случае когда оперативной памяти на всех не хватает и статистика использовании
ноль - память уходит в файл подкачки.
Если область памяти переменной не используется или она забита просто нолями - то
такой кусок памяти может пока его не наполнят - вообще нигде не хранится, и не занимать
место ни в физической памяти ни в файле подкачки.
Так же вероятность нахождение в файле подкачке зависит от частоты обращений к странице
памяти по сравнению с другими программами. Т.е. если вы переменную постоянно пишете/читаете
- есть вероятность, что она может оказаться в физической памяти (если других претендетов
с более высокой статистикой обращений туда нету).
В некоторых ОС (98/ХР и многих других) можно было отключить файл подкачки - тогда
- переменная всегда в оперативке. Боюсь что win10 отключение не переживёт:)
У OS есть специальная метка в памяти - "невыгружаемая память" (например части ядра).
Если эту метку получить - память выгружаться не будет, и всегда будет в оперативке.
Как - уровень драйвера.
UPD: Важная деталь. Любая память, к которой вот сейчас произошло обращение, и страница
под неё отстутствует - то страница выделяется, физическая память добывается (если нет
свободной, отбирается у другого процесса, а его скидывается на диск), с файла подкачки
считывается, т.е. не видно что её не было. Если это не произошло - тогда это "синий
экран смерти", но думаю такого не бывает, там хорошо прописан алгоритм что б этого
не случилось. Страница стека, куда указывает esp (rsp) - может быть выгружена, если
идет работа с другим процессом, но "по первому требованию" она будет загружена назад.
Как итог, хочу сказать
1) Волноваться за память - не стоит.
2) стоит стараться оптимально использовать память (чем меньше используется тем лучше,
но не в ущерб алгоритму)
3) желательно знать сколько вашей программе нужно памяти, и взять сколько нужно,
чем брать кусками - тогда OS проще предсказать сколько памяти вам нужно - и ПО будет
работать быстрее.
4) Нужно знать что есть размер страницы памяти, он равен 8192 либо 4096(раньше было)
(можно узнать через АПИ). Желательно большие структуры выравнивать по границам этого
к-ва байт - тогда программа будет работать быстрее.
Ссылка на мой труд, где я обьяснял поведение с массивом Язык C, глобальные массивы
Ответ 2
Дело в том, что переменная в памяти всегда находится в файле подкачки и одновременно
всегда находится в памяти. Хотя конечно, в файл подкачки она может попасть не сразу,
а из памяти может быть временно вытеснена, программно вы это никак не определите. Если
переменной в памяти нет, обращение к ней вызовет системное исключение, и в результате
этот блок памяти будет подгружен. Причем возможно он будет загружен совсем в другую
область физической памяти. Но для программы эта переменная всегда в памяти, всегда
по тому же адресу, и всегда доступна.
Ответ 3
В Windows для этого существует функция QueryWorkingSetEx. "Working set" - это набор
страниц виртуальной памяти, загруженных в данный момент в физическую память. Если заданный
адрес в него не попадает, то в результате QueryWorkingSetEx для него значение флага
Valid будет равно нулю, если попадает - единице.
Пример на С++:
#include
#include
#include
bool IsInPhysicalMemory(PVOID address){
const UINT FLAG_VALID = 0x1;
PSAPI_WORKING_SET_EX_INFORMATION info = {0};
info.VirtualAddress = address;
BOOL bResult = QueryWorkingSetEx(GetCurrentProcess(),(PVOID)&info,sizeof(info));
if(bResult == FALSE) {printf("QueryWorkingSetEx failed: %d\n",(int)GetLastError());return
false;}
return ((info.VirtualAttributes.Flags & FLAG_VALID) == FLAG_VALID);
}
int x = 1;
int main(int argc, char* argv[])
{
//...
bool bResult = IsInPhysicalMemory((PVOID)&x);
if(bResult == false) printf("In pagefile\n");
else printf("In physical memory\n");
//...
}
Пример на C# (требует включения unsafe-кода):
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
unsafe class Program
{
[DllImport("kernel32.dll")]
static extern IntPtr GetCurrentProcess();
[DllImport("Psapi.dll")]
static extern int QueryWorkingSetEx(
IntPtr hProcess,
[In, Out] ref PSAPI_WORKING_SET_EX_INFORMATION pv,
uint cb);
public struct PSAPI_WORKING_SET_EX_INFORMATION
{
public IntPtr VirtualAddress;
public IntPtr VirtualAttributes;
};
public static bool IsInPhysicalMemory(IntPtr address)
{
PSAPI_WORKING_SET_EX_INFORMATION info = new PSAPI_WORKING_SET_EX_INFORMATION();
info.VirtualAddress = address;
int iResult = QueryWorkingSetEx(GetCurrentProcess(), ref info, (uint)Marshal.SizeOf(info));
if (iResult == 0) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
return (((Int64)info.VirtualAttributes & 0x1) == 0x1);
}
static int y = 1;
static void Main(string[] args)
{
//...
fixed (int* p = &y)
{
if (IsInPhysicalMemory((IntPtr)p)) Console.WriteLine("In physical
memory");
else Console.WriteLine("In pagefile");
}
//...
}
}
}
Можно ли задать адрес переменной,а потом по ходу программы смотреть находится ли
эта переменная по заранее заданному адресу в оперативной памяти(на c# с помощью .Net).
И соответственно если ее не будет в оперативной памяти, то она находится в файле подкачки.
Так ли работает адрес у переменной, или он не меняется вне зависимости от того где
находится?
Обычно да, адрес переменной в виртуальной памяти фиксирован и не меняется в ходе
работы программы. (В C# переменные, которые хранятся в управляемой куче, имеют переменный
адрес, но это связано с деятельностью сборщика мусора, а не с перемещением в файл подкачки.)
Адрес страницы в физической памяти, соответствующей данному адресу в виртуальной
памяти, конечно меняется в ходе того как ОС перемещает страницы. Соответствие физического
и виртуального адреса можно получить в windbg в режиме отладки ядра (см. vtop), но
программно в usermode, насколько я знаю, нельзя.
Ответ 4
Мой основной вопрос состоит в том что, возможно ли во время работы
программы как-то( с помощью любых средств ) узнавать где находится
переменная,хотя бы засечь момент когда она переносится в файл
подкачки.
Вопрос интересный. Надо смотреть WinAPI есть ли там какие-нибудь функции на эту тему.
Думаю что таких функций нет, поэтому скорее всего для винды невозможно узнать, выгружена
ли данная переменная на диск или болтается в памяти. Кстати, чтобы это узнать, надо
еще анализировать карту загрузки и листинг. То есть WinAPI функция (буде такая бы и
была) должна была бы в качестве параметров принимать не только имя переменной но и
карту загрузки и листинг программы.
Думаю, что для линукса такой функции тоже нет. По тем же причинам - никто не будет
парсить листинг и карту загрузки в поисках имени, да и формат листинга и карты загрузки
не стандартизован. Конечно, в мире линукса есть много затейников. Может быть кто-то
и написал нашлепку к ядру, которая отслеживает что-то подобное хотя бы в целях отладки.
А также у меня вопрос. Правильно ли я понимаю, что файл подкачки
используется только когда не хватает места в оперативной памяти или
если есть долго не использующиеся переменные?
Теоретически файл подкачки используется, когда не хватает места в оперативной памяти.
Практически алгоритм файла подкачки на винде закрыт, поэтому ничего об нем сказать
нельзя. Для Линукса алгоритм, конечно, известен матерым линуксоидам.
Кстати, коллега nick_n_a подкинул свежую идею. Если отключить файл подкачки, то все
переменные будут сидеть в памяти. :-)
UPD1:
Кстати, да. Есть же еще переменные на стеке. Интересно, а страница стека выгружается
в файл подкачки? В любом случае непонятно, если пользователь запросил имя стековой
переменной, а эта переменная в данный момент не существует, что должна возвращать WinAPI
функция? А может эта стековая переменная и вообще никогда не будет создана в текущем
запуске программы?
Вобщем, вопрос довольно странный.
UPD2:
А еще есть такой вариант, что переменная (например структура) имеет размер бОльший,
чем страница памяти. Тогда возможен случай, что часть переменной выгружена на диск,
а часть нет. Как в этом случае нужно рассматривать переменную - как выгруженную или
как не выгруженную? Или если переменная не большая, но лежит на границе страниц.
UPD3:
А еще есть такой вариант, что переменная расположена в куче и опять же непонятно,
была она создана или нет в данный момент работы программы и будет ли вообще когда-нибудь
создана в этом запуске программы. Вобщем, видимо Вам преподаватель дал такое задание
просто чтобы вы подумали над проблемой. Потому, похоже, что технически эта проблема
"в общем виде" не решается.
UPD4:
Я понял что нужно получить память занимаемую процессом программы в
которой находится переменная. Но дело в том что я не понимаю как на
c++ это можно реализовать.
Sergesama Channel, Вы не стирайте старый вопрос. Просто дописывайте уточнения. Потому
что "норот" отвечал на Ваш старый вопрос, Вы его изменили и все ответы выглядят немного
шизофренично. :-)
Что касается получения памяти процесса, то мне вспоминается, что на винде это можно
получить (выгрузить на диск в двоичном виде). Не знаю уж как это соотносится с безопасностью,
но вроде бы такая функция в WinAPI была (но точно не скажу, надо смотреть). Но даже
если Вы получили образ памяти процесса, то все равно непонятно, какие страницы в данный
момент выгружены на диск, а какие нет.
Ответ 5
Технически в современном мире, память делится на виртуальную и физическую. Грубо
говоря, физическая память - это набор ячеек и адресов к ним в оперативной памяти. Виртуальная
память - отображение адресов физической памяти на некоторые виртуальные адреса с точки
зрения процессора. Что это нам дает? Во-первых, отображая физические адреса на виртуальные,
мы может изолировать память каждого процесса. Во-вторых, любой процесс может находиться
по одинаковому адресу в виртуальной памяти, но всегда в разных местах в физической
памяти. В-третьих, мы можем один адрес физической память отображать сразу на несколько
адресов виртуальной памяти на каждый процесс, что нам даст возможность разделяемой
памяти без лишних дублирований памяти между этими процессами. В-четвертых, благодаря
виртуальной памяти, её можно выгружать и загружать обратно при необходимости. Дополнительно
виртуальную память можно защищать (например запретить записывать что-то в память с
кодом), что было бы невозможно при работе с физической памятью напрямую.
При доступе к отсутствующему / выгруженному адресу виртуальной памяти процессор генерирует
прерывание / исключение, которое операционная система перехватывает и загружает на
нужный виртуальный адрес ранее выгруженную физическую память и после этого настраивает
оборажение физического адреса на виртуальный.
Теперь, что касается вашего вопроса, с точки зрения программы все её переменные хранятся
всегда по одним и тем же виртуальным адресам и всегда доступны (если они недоступны,
произойдет исключение и ОС загрузит память будто она всегда была загружена, и сделает
это незаметно для программы), но адрес в физической памяти может быть разным и меняться
в процессе загрузки / выгрузки как угодно. Если вам это действительно нужно, то копать
следует в сторону определения физических адресов на которые отображены виртуальные.
Отображение физических адресов на виртуальные происходит настройкой некоторой структуры
данных и загрузкой этой структуры в некоторый регистр процессора, после чего процессор
делает преобразования между адресами самостоятельно по этой самой структуре. Более
подробно можно почитать об этом, найдя информацию по ключевым словам "paging and virtual
memory".
Ответ 6
По крайней мере в Линуксе, если верить man 5 proc
это возможно.
Cудя по описанию, для известного адреса переменной вам нужно прочесть соответствующие
8 байт (uint64_t) (для страниц размером 4k просто сдвиньте адрес на 9, обнулите 3 младших
бита и сделайте seek) из файла /proc/self/pagemap и посмотреть на бит 62. Если 0, то
страница с переменной в swap-е.
/proc/[pid]/pagemap (since Linux 2.6.25)
This file shows the mapping of each of the process's virtual
pages into physical page frames or swap area. It contains one
64-bit value for each virtual page, with the bits set as fol‐
lows:
63 If set, the page is present in RAM.
62 If set, the page is in swap space
61 (since Linux 3.5)
The page is a file-mapped page or a shared anony‐
mous page.
60–57 (since Linux 3.11)
Zero
56 (since Linux 4.2)
The page is exclusively mapped.
55 (since Linux 3.11)
PTE is soft-dirty (see the kernel source file Doc‐
umentation/vm/soft-dirty.txt).
54–0 If the page is present in RAM (bit 63), then these
bits provide the page frame number, which can be
used to index /proc/kpageflags and /proc/kpage‐
count. If the page is present in swap (bit 62),
then bits 4–0 give the swap type, and bits 54–5
encode the swap offset.
Before Linux 3.11, bits 60–55 were used to encode the base-2
log of the page size.
To employ /proc/[pid]/pagemap efficiently, use
/proc/[pid]/maps to determine which areas of memory are actu‐
ally mapped and seek to skip over unmapped regions.
The /proc/[pid]/pagemap file is present only if the CON‐
FIG_PROC_PAGE_MONITOR kernel configuration option is enabled.
Permission to access this file is governed by a ptrace access
mode PTRACE_MODE_READ_FSCREDS check; see ptrace(2).