📜  好友内存分配计划|设置 2(解除分配)

📅  最后修改于: 2022-05-13 01:56:11.839000             🧑  作者: Mango

好友内存分配计划|设置 2(解除分配)

先决条件 – 好友分配 |设置 1
问题:编写一个程序来实现操作系统中内存分配和释放的伙伴系统。
解释 -
正如我们在 Set 1 中已经知道的那样,分配是通过使用空闲列表来完成的。现在,重新分配,我们将保持与段的段值的关键和大小的起始地址的额外的数据结构图(用C ++ unordered_set,HashMap的Java中),每当分配请求来更新它。现在,当释放请求到来时,我们将首先检查映射以查看它是否是有效请求。如果是这样,我们然后将块添加到空闲列表中,跟踪其大小的块。然后,我们将搜索空闲列表以查看它的伙伴是否空闲 - 如果是,我们将合并块并将它们放在它们上方的空闲列表中(跟踪两倍大小的块),否则我们不会合并并简单地在那之后返回。
如何知道哪个块是给定块的伙伴?
让我们定义两个术语- buddyNumberbuddyAddress 。一个区块的buddyNumber由以下公式计算:

(base_address-starting_address_of_main_memory)/block_size

我们注意到这始终是一个整数,因为分子和分母都是 2 的幂。现在,如果它们都是由同一个更大的块分裂形成的,那么一个块将成为另一个块的伙伴。例如,如果 4 个 16 字节的连续分配请求到来,我们将得到块 0-15、16-31、32-47、48-63,其中块 0-15 和 16-31 是伙伴(因为它们形成通过拆分块 0-32) 但 0-15 和 32-47 不是。一个区块的buddyAddress是其好友区块的起始索引,由公式给出:

block_starting_address+block_size (if buddyNumber is even)
block_starting_address-block_size (if buddyNumber is odd)

因此,我们所要做的就是在空闲列表中找到这个buddyAddress (通过与该特定列表中的所有起始地址进行比较),如果存在,就可以进行合并。
例子:
让我们通过跟踪大小为 128 KB 的内存块来看看算法是如何进行的。最初,空闲列表是:{}, {}, {}, {}, {}, {}, {}, { (0, 127) }

  • 分配请求: 16 字节
    没有找到这样的块,所以我们向上遍历,将0-127块拆分为0-63、64-127;我们添加 64-127 以列出跟踪 64 字节块并向下传递 0-63;再次分为0-31和32-63;我们添加 32-63 以列出跟踪 32 字节的块,向下传递 0-31;再进行一次拆分,将 0-15 返回给用户,同时将 16-31 添加到跟踪 16 字节块的空闲列表中。
    列表为:{}, {}, {}, {}, { (16, 31) }, { (32, 63) }, { (64, 127) }, {}
  • 分配请求: 16 字节
    直接分配内存段 16-31,因为它已经存在。
    列表为:{}, {}, {}, {}, {}, { (32, 63) }, { (64, 127) }, {}
  • 分配请求: 16 字节
    没有找到这样的块,所以我们将遍历到块 32-63 并将其拆分为块 32-47 和 48-63;我们将添加 48-63 以列出跟踪 16 字节块并将 32-47 返回给用户。
    列表为:{}, {}, {}, {}, { (48, 63) }, {}, { (64, 127) }, {}
  • 分配请求: 16 字节
    直接分配内存段 48-63,因为它已经存在。
    列表为:{}、{}、{}、{}、{}、{}、{(64, 127)}、{}
  • 解除分配请求: StartIndex = 0
    将完成释放,但无法合并,因为它的buddyNumber是 0, buddyAddress是 16(通过公式),它们都不在空闲列表中。
    列表为:{}, {}, {}, {}, { (0, 15) }, {}, { (64, 127) }, {}
  • 解除分配请求: StartIndex = 9
    结果:无效请求,因为从未分配过此段。
    列表为:{}, {}, {}, {}, { (0, 15) }, {}, { (64, 127) }, {}
  • 解除分配请求: StartIndex = 32
    将完成释放,但无法合并,因为块的buddyNumber为 0 和 2 块的buddyAddress分别为 16 和 48,它们都不在空闲列表中。
    列表为:{}, {}, {}, {}, { (0, 15), (32-47) }, {}, { (64, 127) }, {}
  • 解除分配请求: StartIndex = 16
    由于块 16-31 的buddyAddress为 0,它出现在跟踪 16 字节块的空闲列表中,因此将完成解除分配并合并块 0-15 和 16-31。
    列表为:{}, {}, {}, {}, { (32-47) }, { (0, 31) }, { (64, 127) }, {}



图 – Buddy 算法-分配和解除分配

执行 -
下面是完整的程序。

C++
#include
using namespace std;
 
// Size of vector of pairs
int size;
 
// Global vector of pairs to track all
// the free nodes of various sizes
vector> arr[100000];
 
// Map used as hash map to store the
// starting address as key and size
// of allocated segment key as value
map mp;
 
void Buddy(int s)
{
     
    // Maximum number of powers of 2 possible
    int n = ceil(log(s) / log(2));
     
    size = n + 1;
    for(int i = 0; i <= n; i++)
        arr[i].clear();
 
    // Initially whole block of specified
    // size is available
    arr[n].push_back(make_pair(0, s - 1));
}
 
void allocate(int s)
{
     
    // Calculate index in free list
    // to search for block if available
    int x = ceil(log(s) / log(2));
     
    // Block available
    if (arr[x].size() > 0)
    {
        pair temp = arr[x][0];
 
        // Remove block from free list
        arr[x].erase(arr[x].begin());
         
        cout << "Memory from " << temp.first
             << " to " << temp.second
             << " allocated" << "\n";
 
        // Map starting address with
        // size to make deallocating easy
        mp[temp.first] = temp.second -
                         temp.first + 1;
    }
    else
    {
        int i;
         
        // If not, search for a larger block
        for(i = x + 1; i < size; i++)
        {
             
            // Find block size greater
            // than request
            if (arr[i].size() != 0)
                break;
        }
 
        // If no such block is found
        // i.e., no memory block available
        if (i == size)
        {
            cout << "Sorry, failed to allocate memory\n";
        }
         
        // If found
        else
        {
            pair temp;
            temp = arr[i][0];
 
            // Remove first block to split
            // it into halves
            arr[i].erase(arr[i].begin());
            i--;
             
            for(;i >= x; i--)
            {
                 
                // Divide block into two halves
                pair pair1, pair2;
                pair1 = make_pair(temp.first,
                                  temp.first +
                                 (temp.second -
                                  temp.first) / 2);
                pair2 = make_pair(temp.first +
                                 (temp.second -
                                  temp.first + 1) / 2,
                                  temp.second);
                                  
                arr[i].push_back(pair1);
 
                // Push them in free list
                arr[i].push_back(pair2);
                temp = arr[i][0];
 
                // Remove first free block to
                // further split
                arr[i].erase(arr[i].begin());
            }
             
            cout << "Memory from " << temp.first
                 << " to " << temp.second
                 << " allocate" << "\n";
                  
            mp[temp.first] = temp.second -
                             temp.first + 1;
        }
    }
}
 
void deallocate(int id)
{
     
    // If no such starting address available
    if(mp.find(id) == mp.end())
    {
        cout << "Sorry, invalid free request\n";
        return;
    }
     
    // Size of block to be searched
    int n = ceil(log(mp[id]) / log(2));
     
    int i, buddyNumber, buddyAddress;
 
    // Add the block in free list
    arr[n].push_back(make_pair(id,
                               id + pow(2, n) - 1));
    cout << "Memory block from " << id
         << " to "<< id + pow(2, n) - 1
         << " freed\n";
 
    // Calculate buddy number
    buddyNumber = id / mp[id];
 
    if (buddyNumber % 2 != 0)
        buddyAddress = id - pow(2, n);
    else
        buddyAddress = id + pow(2, n);
         
    // Search in free list to find it's buddy
    for(i = 0; i < arr[n].size(); i++)
    {
         
        // If buddy found and is also free
        if (arr[n][i].first == buddyAddress)
        {
             
            // Now merge the buddies to make
            // them one large free memory block
            if (buddyNumber % 2 == 0)
            {
                arr[n + 1].push_back(make_pair(id,
                   id + 2 * (pow(2, n) - 1)));
                    
                cout << "Coalescing of blocks starting at "
                     << id << " and " << buddyAddress
                     << " was done" << "\n";
            }
            else
            {
                arr[n + 1].push_back(make_pair(
                    buddyAddress, buddyAddress +
                    2 * (pow(2, n))));
                     
                cout << "Coalescing of blocks starting at "
                     << buddyAddress << " and "
                     << id << " was done" << "\n";
            }
            arr[n].erase(arr[n].begin() + i);
            arr[n].erase(arr[n].begin() +
            arr[n].size() - 1);
            break;
        }
    }
 
    // Remove the key existence from map
    mp.erase(id);
}
 
// Driver code
int main()
{
     
    // Uncomment following code for interactive IO
    /*
    int total,c,req;
    cout<<"Enter Total Memory Size (in Bytes) => ";
    cin>>total;
    initialize(total);
    label:
    while(1)
    {
        cout<<"\n1. Add Process into Memory\n
        2. Remove Process \n3. Allocation Map\n4. Exit\n=> ";
        cin>>c;
        switch(c)
        {
            case 1:
            cout<<"Enter Process Size (in Bytes) => ";
            cin>>req;
            cout<<"\n===>";
            allocate(req);
            break;
 
            case 2:
            cout<<"Enter Starting Address => ";
            cin>>req;
            cout<<"\n===>";
            deallocate(req);
            break;
 
            case 3:
            print();
            break;
 
            case 4:
            exit(0);
            break;
 
            default:
            goto label;
        }
    }*/
     
    Buddy(128);
    allocate(16);
    allocate(16);
    allocate(16);
    allocate(16);
    deallocate(0);
    deallocate(9);
    deallocate(32);
    deallocate(16);
 
    return 0;
}
 
// This code is contributed by sarthak_eddy


Java
import java.io.*;
import java.util.*;
 
class Buddy {
 
    // Inner class to store lower
    // and upper bounds of the allocated memory
    class Pair {
         
        int lb, ub;
        Pair(int a, int b)
        {
            lb = a;
            ub = b;
        }
    }
 
    // Size of main memory
    int size;
 
    // Array to track all
    // the free nodes of various sizes
    ArrayList arr[];
     
    // Hashmap to store the starting
    // address and size of allocated segment
    // Key is starting address, size is value
    HashMap hm;
     
    // Else compiler will give warning
    // about generic array creation
    @SuppressWarnings("unchecked")
    Buddy(int s)
    {
         
        size = s;
        hm = new HashMap<>();
         
        // Gives us all possible powers of 2
        int x = (int)Math.ceil(Math.log(s) / Math.log(2));
 
        // One extra element is added
        // to simplify arithmetic calculations
        arr = new ArrayList[x + 1];
 
        for (int i = 0; i <= x; i++)
            arr[i] = new ArrayList<>();
 
        // Initially, only the largest block is free
        // and hence is on the free list
        arr[x].add(new Pair(0, size - 1));
    }
     
    void allocate(int s)
    {
 
        // Calculate which free list to search to get the
        // smallest block large enough to fit the request
        int x = (int)Math.ceil(Math.log(s) / Math.log(2));
 
        int i;
        Pair temp = null;
 
        // We already have such a block
        if (arr[x].size() > 0) {
 
            // Remove from free list
            // as it will be allocated now
            temp = (Pair)arr[x].remove(0);
            System.out.println("Memory from " + temp.lb
                            + " to " + temp.ub + " allocated");
                             
            // Store in HashMap
            hm.put(temp.lb, temp.ub - temp.lb + 1);
            return;
        }
 
        // If not, search for a larger block
        for (i = x + 1; i < arr.length; i++) {
 
            if (arr[i].size() == 0)
                continue;
 
            // Found a larger block, so break
            break;
        }
 
        // This would be true if no such block was found
        // and array was exhausted
        if (i == arr.length) {
             
            System.out.println("Sorry, failed to allocate memory");
            return;
        }
 
        // Remove the first block
        temp = (Pair)arr[i].remove(0);
 
        i--;
 
        // Traverse down the list
        for (; i >= x; i--) {
 
            // Divide the block in two halves
            // lower index to half-1
            Pair newPair = new Pair(temp.lb, temp.lb
                                    + (temp.ub - temp.lb) / 2);
 
            // half to upper index
            Pair newPair2 = new Pair(temp.lb
                                    + (temp.ub - temp.lb + 1) / 2,
                                    temp.ub);
 
            // Add them to next list
            // which is tracking blocks of smaller size
            arr[i].add(newPair);
            arr[i].add(newPair2);
 
            // Remove a block to continue the downward pass
            temp = (Pair)arr[i].remove(0);
        }
 
        // Finally inform the user
        // of the allocated location in memory
        System.out.println("Memory from " + temp.lb
                        + " to " + temp.ub + " allocated");
 
        // Store in HashMap
        hm.put(temp.lb, temp.ub - temp.lb + 1);
    }
    void deallocate(int s)
    {
        // Invalid reference, as this was never allocated
        if (!hm.containsKey(s)) {
            System.out.println("Sorry, invalid free request");
            return;
        }
 
        // Get the list which will track free blocks
        // of this size
        int x = (int)Math.ceil(Math.log(hm.get(s))
                                        / Math.log(2));
        int i, buddyNumber, buddyAddress;
 
        // Add it to the free list
        arr[x].add(new Pair(s, s + (int)Math.pow(2, x) - 1));
        System.out.println("Memory block from " + s + " to "
                           + (s + (int)Math.pow(2, x) - 1) + " freed");
 
 
        // Calculate it's buddy number and buddyAddress. The
        // base address is implicitly 0 in this program, so no
        // subtraction is necessary for calculating buddyNumber
        buddyNumber = s / hm.get(s);
     
        if (buddyNumber % 2 != 0) {
            buddyAddress = s - (int)Math.pow(2, x);
        }
         
        else {
            buddyAddress = s + (int)Math.pow(2, x);
        }
         
         
        // Search in the free list for buddy
        for (i = 0; i < arr[x].size(); i++) {
             
             
            // This indicates the buddy is also free
            if (arr[x].get(i).lb == buddyAddress) {
                 
                // Buddy is the block after block
                // with this base address
                if (buddyNumber % 2 == 0) {
                     
                    // Add to appropriate free list
                    arr[x + 1].add(new Pair(s, s
                                  + 2 * ((int)Math.pow(2, x)) - 1));
                    System.out.println("Coalescing of blocks starting at "
                                            + s + " and "
                                            + buddyAddress + " was done");
                }
                 
                // Buddy is the block before block
                // with this base address
                else {
                     
                    // Add to appropriate free list
                    arr[x + 1].add(new Pair(buddyAddress,
                                    buddyAddress + 2 * ((int)Math.pow(2, x))
                                                                         - 1));
                    System.out.println("Coalescing of blocks starting at "
                                                + buddyAddress + " and "
                                                + s + " was done");
                }
 
                // Remove the individual segements
                // as they have coalesced
                arr[x].remove(i);
                arr[x].remove(arr[x].size() - 1);
                break;
            }
        }
 
        // Remove entry from HashMap
        hm.remove(s);
    }
     
    public static void main(String args[]) throws IOException
    {
        int initialMemory = 0, type = -1, val = 0;
         
        // Uncomment below section for interactive I/O
        /*Scanner sc=new Scanner(System.in);
        initialMemory = sc.nextInt();
        Buddy obj=new Buddy(initialMemory);
        while(true)
        {
            type = sc.nextInt();
            if(type==-1)
            break;
            else if(type==1)
            {
                val=sc.nextInt();
                obj.allocate(val);
            }
            else
            {
                val=sc.nextInt();
                obj.deallocate(val);
            }
        }*/
         
        initialMemory = 128;
        Buddy obj = new Buddy(initialMemory);
        obj.allocate(16);
        obj.allocate(16);
        obj.allocate(16);
        obj.allocate(16);
        obj.deallocate(0);
        obj.deallocate(9);
        obj.deallocate(32);
        obj.deallocate(16);
    }
}


C#
using System;
using System.Collections.Generic;
 
public class Buddy
{
 
    // Inner class to store lower
    // and upper bounds of the
    // allocated memory
    class Pair
    {
        public int lb, ub;
        public Pair(int a, int b)
        {
            lb = a;
            ub = b;
        }
    }
 
    // Size of main memory
    int size;
 
    // Array to track all
    // the free nodes of various sizes
    List []arr;
     
    // Hashmap to store the starting
    // address and size of allocated segment
    // Key is starting address, size is value
    Dictionary hm;
     
    // Else compiler will give warning
    // about generic array creation
    Buddy(int s)
    {
         
        size = s;
        hm = new Dictionary();
         
        // Gives us all possible powers of 2
        int x = (int)Math.Ceiling(Math.Log(s) /
                                  Math.Log(2));
 
        // One extra element is added
        // to simplify arithmetic calculations
        arr = new List[x + 1];
 
        for (int i = 0; i <= x; i++)
            arr[i] = new List();
 
        // Initially, only the largest block is
        // free and hence is on the free list
        arr[x].Add(new Pair(0, size - 1));
    }
     
    void allocate(int s)
    {
 
        // Calculate which free list to
        // search to get the smallest block
        // large enough to fit the request
        int x = (int)Math.Ceiling(Math.Log(s) /
                                  Math.Log(2));
 
        int i;
        Pair temp = null;
 
        // We already have such a block
        if (arr[x].Count > 0)
        {
 
            // Remove from free list
            // as it will be allocated now
            temp = (Pair)arr[x][0];
            arr[x].RemoveAt(0);
            Console.WriteLine("Memory from " + temp.lb +
                       " to " + temp.ub + " allocated");
                             
            // Store in Dictionary
            hm.Add(temp.lb, temp.ub - temp.lb + 1);
            return;
        }
 
        // If not, search for a larger block
        for (i = x + 1; i < arr.Length; i++)
        {
            if (arr[i].Count == 0)
                continue;
 
            // Found a larger block, so break
            break;
        }
 
        // This would be true if no such block
        // was found and array was exhausted
        if (i == arr.Length)
        {
            Console.WriteLine("Sorry, failed to" +
                              " allocate memory");
            return;
        }
 
        // Remove the first block
        temp = (Pair)arr[i][0];
        arr[i].RemoveAt(0);
 
        i--;
 
        // Traverse down the list
        for (; i >= x; i--)
        {
 
            // Divide the block in two halves
            // lower index to half-1
            Pair newPair = new Pair(temp.lb, temp.lb +
                                   (temp.ub - temp.lb) / 2);
 
            // half to upper index
            Pair newPair2 = new Pair(temp.lb + (temp.ub -
                                     temp.lb + 1) / 2, temp.ub);
 
            // Add them to next list
            // which is tracking blocks of
            // smaller size
            arr[i].Add(newPair);
            arr[i].Add(newPair2);
 
            // Remove a block to continue
            // the downward pass
            temp = (Pair)arr[i][0];
            arr[i].RemoveAt(0);
        }
 
        // Finally inform the user
        // of the allocated location in memory
        Console.WriteLine("Memory from " + temp.lb +
                   " to " + temp.ub + " allocated");
 
        // Store in Dictionary
        hm.Add(temp.lb, temp.ub - temp.lb + 1);
    }
     
    void deallocate(int s)
    {
         
        // Invalid reference,
        // as this was never allocated
        if (!hm.ContainsKey(s))
        {
            Console.WriteLine("Sorry, invalid free request");
            return;
        }
 
        // Get the list which will track free blocks
        // of this size
        int x = (int)Math.Ceiling(Math.Log(hm[s]) /
                                  Math.Log(2));
        int i, buddyNumber, buddyAddress;
 
        // Add it to the free list
        arr[x].Add(new Pair(s, s +
                  (int)Math.Pow(2, x) - 1));
        Console.WriteLine("Memory block from " + s +
                                       " to " + (s +
               (int)Math.Pow(2, x) - 1) + " freed");
 
 
        // Calculate it's buddy number and
        // buddyAddress. The base address is
        // implicitly 0 in this program,
        // so no subtraction is necessary for
        // calculating buddyNumber
        buddyNumber = s / hm[s];
     
        if (buddyNumber % 2 != 0)
        {
            buddyAddress = s - (int)Math.Pow(2, x);
        }
        else
        {
            buddyAddress = s + (int)Math.Pow(2, x);
        }
         
        // Search in the free list for buddy
        for (i = 0; i < arr[x].Count; i++)
        {
             
            // This indicates the buddy is also free
            if (arr[x][i].lb == buddyAddress)
            {
                 
                // Buddy is the block after block
                // with this base address
                if (buddyNumber % 2 == 0)
                {
                     
                    // Add to appropriate free list
                    arr[x + 1].Add(new Pair(s, s + 2 *
                                 ((int)Math.Pow(2, x)) - 1));
                    Console.WriteLine("Coalescing of blocks starting at " +
                                 s + " and " + buddyAddress + " was done");
                }
                 
                // Buddy is the block before block
                // with this base address
                else
                {
                     
                    // Add to appropriate free list
                    arr[x + 1].Add(new Pair(buddyAddress,
                                            buddyAddress +
                                  2 * ((int)Math.Pow(2, x)) - 1));
                    Console.WriteLine("Coalescing of blocks starting at " +
                                 buddyAddress + " and " + s + " was done");
                }
 
                // Remove the individual segements
                // as they have coalesced
                arr[x].RemoveAt(i);
                arr[x].RemoveAt(arr[x].Count - 1);
                break;
            }
        }
 
        // Remove entry from Dictionary
        hm.Remove(s);
    }
     
    // Driver Code
    public static void Main(String []args)
    {
        int initialMemory = 0;
        initialMemory = 128;
        Buddy obj = new Buddy(initialMemory);
        obj.allocate(16);
        obj.allocate(16);
        obj.allocate(16);
        obj.allocate(16);
        obj.deallocate(0);
        obj.deallocate(9);
        obj.deallocate(32);
        obj.deallocate(16);
    }
}
 
// This code is contributed by 29AjayKumar


输出:
Memory from 0 to 15 allocated
Memory from 16 to 31 allocated
Memory from 32 to 47 allocated
Memory from 48 to 63 allocated
Memory block from 0 to 15 freed
Sorry, invalid free request
Memory block from 32 to 47 freed
Memory block from 16 to 31 freed
Coalescing of blocks starting at 0 and 16 was done

时间复杂度——
正如在集合 1 中已经讨论过的,分配的时间复杂度是O(log(n)) 。对于解除分配,在最坏的情况下,所有分配的块的大小可以为 1 个单位,这将需要O(n)时间来扫描列表以进行合并。然而,在实践中,这种分配不太可能发生,因此它通常比线性时间快得多。