Опубликован: 05.07.2006 | Доступ: свободный | Студентов: 4680 / 885 | Оценка: 4.12 / 3.74 | Длительность: 18:59:00
Лекция 9:

Интерфейс системы UNIX

8.7. Пример - распределитель памяти

В "лекции №5" мы написали бесхитростный вариант функции alloc. Вариант, который мы напишем теперь, не содержит ограничений: обращения к функциям alloc и free могут перемежаться в любом порядке; когда это необходимо, функция alloc обращается к операционной системе за дополнительной памятью. Кроме того, что эти процедуры полезны сами по себе, они также иллюстрируют некоторые соображения, связанные с написанием машинно-зависимых программ относительно машинно-независимым образом, и показывают практическое применение структур, объединений и конструкций typedef.

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

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

Освобождение памяти также влечет за собой просмотр свободного списка в поиске подходящего места для введения освобожденного блока. Если этот освободившийся блок с какой-либо стороны примыкает к блоку из списка свободных блоков, то они объединяются в один блок большего размера, так что память не становится слишком раздробленной. Обнаружить смежные блоки просто, потому что свободный список содержится в порядке возрастания адресов.

Одна из проблем, о которой мы упоминали в "лекции №5" , заключается в обеспечении того, чтобы возвращаемая функцией alloc память была выровнена подходящим образом для тех объектов, которые будут в ней храниться. Хотя машины и различаются, для каждой машины существует тип, требующий наибольших ограничений по размещению памяти, если данные самого ограничительного типа можно поместить в некоторый определенный адрес, то это же возможно и для всех остальных типов. Например, на IBM 360/370,HONEYWELL 6000 и многих других машинах любой объект может храниться в границах, соответствующим переменным типа double ; на PDP-11 будут достаточны переменные типа int.

Свободный блок содержит указатель следующего блока в цепочке, запись о размере блока и само свободное пространство; управляющая информация в начале называется заголовком. Для упрощения выравнивания все блоки кратны размеру заголовка, а сам заголовок выровнен надлежащим образом. Это достигается с помощью объединения, которое содержит желаемую структуру заголовка и образец наиболее ограничительного по выравниванию типа:

typedef int align; /*forces alignment on pdp-11*/
union header { /*free block header*/
   struct {
   union header *ptr; /*next free block*/
   unsigned size; /*size of this free block*/
   } s;
   align  x; /*force alignment of blocks*/
};
typedef union header header;

Функция alloc округляет требуемый размер в символах до нужного числа единиц размера заголовка; фактический блок, который будет выделен, содержит на одну единицу больше, предназначаемую для самого заголовка, и это и есть значение, которое записывается в поле size заголовка. указатель, возвращаемый функцией alloc, указывает на свободное пространство, а не на сам заголовок.

static header base; /*empty list to get started*/
static header *allocp=null; /*last allocated block*/
char *alloc(nbytes)/*general-purpose storage allocator*/
unsigned nbytes;
{
   header *morecore();
   register header *p, *g;
   register int nunits;
   nunits=1+(nbytes+sizeof(header)-1)/sizeof(header);
   if ((g=allocp)==null) \( /*no free list yet*/
base.s ptr=allocp=g=&base;
base.s.size=0;
   }


   for (p=g>s.ptr; ; g=p, p=p->s.ptr) {
if (p->s.size>=nunits) { /*big enough*/
    if (p->s.size==nunits) /*exactly*/
   g->s.ptr=p->s.ptr;
    else { /*allocate tail end*/
   p->s.size-=nunits;
   p+=p->s.size;
   p->s.size=nunits;
    }
    allocp=g;
    return((char *)(p+1));
 }
 if(p==allocp) /*wrapped around free list*/
     if((p=morecore(nunits))==null)
    return(null); /*none left*/
 }
    }