Skip to main content

Về việc tổ chức bảng MACVLAN với 10000 phần tử

Bài viết này là phần ứng dụng của bài viết trước về bảng băm (hash table). Nếu các bạn chưa nắm được khái niệm về tổ chức dữ liệu trong bảng băm. Các bạn vui lòng đọc lại bài viết trước tại đây

Các kiến thức cơ sở

Bảng MACVLAN là một cơ sở dữ liệu được sử dụng trong các thiết bị mạng như Router/Switch/ONT/OLT với mục đích tìm kiếm cổng ra của một bản tin dựa vào địa chỉ MAC đích của bản tin đó. Nếu các bạn chưa biết địa chỉ MAC của một cổng mạng là gì, các bạn có thể vào command line của hệ điều hành Linux và gõ ifconfig


Địa chỉ MAC (màu vàng) hay còn gọi là địa chỉ vật lý (có độ dài 48bit) là địa chỉ duy nhất được gắn cho mỗi cổng mạng của một máy tính, địa chỉ này được sử dụng để nhận biết chính xác từng máy trong môi trường mạng IP. Xem xét một sơ đồ mạng như sau


Giả sử máy tính A có địa chỉ MACA, máy tính B có địa chỉ MACB, máy tính C có địa chỉ MACC được cắm lần lượt vào cổng 1, 2, 3 của Switch. Trong quá trình hoạt động của mình, Switch sẽ tìm cách học các địa chỉ này và duy trì một bảng MAC tương ứng
Vlan
Mac address
Type
Port
1
MACA
Dynamic
1
1
MACB
Dynamic
2
1
MACC
Dynamic
3

Như vậy Switch sẽ biết máy tính A,B,C nằm lần lượt trên cổng 1,2,3 và khi có một bản tin hướng đến các máy tính này thì Switch sẽ chuyển tiếp ra các cổng tương ứng. Chúng ta có thể nhìn thấy bảng này trên các Router/Switch của Cisco bằng command-line show mac address-table
Bởi việc xử lý tìm kiếm được thực hiện trên từng bản tin, nên việc tổ chức cơ sở dữ liệu trên Switch đòi hỏi thời gian tìm kiếm phải nhanh nhất có thể. Đồng thời trong các mạng LAN lớn, số lượng máy tính có thể lên tới hàng vạn, nên số lượng địa chỉ MAC sẽ tăng lên tương ứng, việc tổ chức cơ sở dữ liệu đòi hỏi sự cân bằng giữa bộ nhớ được cấp phát tới bảng và thời gian tìm kiếm

Tổ chức cơ sở dữ liệu của bảng MAC với 10000 bản ghi

Ở bài viết trước, tôi đã trình bày một phương thức tổ chức cơ sở dữ liệu dạng bảng băm, trong bài viết này chúng ta sẽ sử dụng phương thức tổ chức cơ sở dữ liệu trên để tổ chức bảng MACVLAN với 10000 bản ghi

Các giao diện chính của bảng

Bảng có các giao diện được đưa tới người sử dụng như sau
  • Khởi tạo bảng, cấp phát bộ nhớ cho bảng
  • Chèn một bản ghi dựa vào địa chỉ MAC
  • Xóa một bản ghi dựa vào địa chỉ MAC
  • Tìm kiếm một bản ghi dựa vào địa chỉ MAC
  • In ra tất cả các bản ghi trong bảng
  • Xóa bảng, giải phóng bộ nhớ bảng và các cấu trúc dữ liệu quản lý bản ghi

Khởi tạo bảng và cấp phát bộ nhớ cho hoạt động của bảng

Khởi tạo bảng là phương thức được gọi đầu tiên trước tất cả các phương thức khác. Khởi tạo bảng sẽ cấp phát bộ nhớ cho bảng và toàn bộ hoạt động quản lý bản ghi của bảng. Hàm khởi tạo bảng sẽ gọi tới phương thức Table_new của bảng băm. Hàm này đòi hỏi phải truyền vào số lượng tối đa các bản ghi sẽ tổ chức trong bảng, một hàm băm (hash) để tìm kiếm vị trí của danh sách liên kết mà bản ghi được lưu giữ và hàm so sánh (compare) để tìm kiếm vị trí của bản ghi trong danh sách liên kết

Trước hết chúng ta tìm hiểu về thiết kế một hàm băm (hash) cho bảng MACVLAN. Có rất nhiều nghiên cứu và các khuyến nghị khác nhau về phương pháp thiết kế hàm băm. Các yêu cầu cơ bản của hàm băm như sau
  • Hàm lấy vào một tập các dữ liệu lớn và đưa ra tập các dữ liệu nhỏ hơn (1)
  • Hàm phải có khả năng phân phối đều trên tập dữ liệu đầu ra (2)
  • Hàm phải hoạt động nhanh, do đó hàm càng đơn giản càng tốt (3)
Tôi xin giải thích một chút về các yêu cầu trên, với yêu cầu số (1), ví dụ như trong trường hợp của chúng ta tổ chức bảng MACVLAN sẽ lấy vào địa chỉ MAC (có độ dài 48bit) và trả ra một chỉ số (có độ dài 8 bit), chỉ số này sẽ sử dụng làm chỉ số cho mảng địa chỉ các danh sách liên kết chứa các bản ghi. Như thế mảng các địa chỉ này có độ dài 2^8-1 = 255 phần tử. Nếu hàm băm trả ra tập dữ liệu có độ dài lớn hơn 48-bit, như thế sẽ cần cấp phát một mảng có độ dài lớn hơn 2^48 thì sẽ quá lớn vượt quá khả năng bộ nhớ của đa số máy tính.
Với yêu cầu số (2), địa chỉ MAC là một số có giá trị từ 00:00:00:00:00:00 tới FF:FF:FF:FF:FF:FF. Ngoại trừ một số địa chỉ được cấp phát đặc biệt (địa chỉ multicast, địa chỉ broadcast, các địa chỉ cấp phát dành riêng bởi IANA, …) thì địa chỉ MAC hoàn toàn là một số ngẫu nhiên trong khoảng giá trị trên. Khi địa chỉ MAC chạy trong khoảng tối thiểu-tối đa thì hàm băm phải trả ra kết quả là một phân phối đều trong khoảng 0-255 (vì kết quả ra là 8bit). Vì kết quả đầu ra này sẽ dùng làm chỉ số hóa cho mảng các địa chỉ danh sách liên kết của bản ghi nên nếu kết quả không phải là một phân phối đều thì sẽ khiến cho số lượng bản ghi tại một vị trí sẽ nhiều hơn vượt trội so với các vị trí khác. Điều này sẽ khiến cho thời gian tìm kiếm một bản ghi khi biết địa chỉ của danh sách liên kết chứa bản ghi trong trường hợp tồi nhất sẽ tăng lên. Chúng ta có 10000 bản ghi và có 255 vị trí chứa danh sách liên kết, chúng ta sẽ muốn rằng mỗi vị trí sẽ có 10000/255 ~ 40 bản ghi chứ không phải nơi thì có hàng trăm bản ghi, nơi thì lại chẳng có bản ghi nào, phải không ạ. Hình dưới đây mô tả một hàm hash tạo ra phân phối đều, khi tập đầu vào đi hết khoảng giá trị của nó, số lượng mỗi phần tử ở tập đầu ra là bằng nhau

Cuối cùng với yêu cầu số (3), rõ ràng là hàm hoạt động càng nhanh càng tốt bởi hàm được gọi ở hầu hết các phương thức quan trọng của bảng. Nên hàm hoạt động càng nhanh thì các hoạt động tìm kiếm/xóa/sửa/thêm bản ghi sẽ hoạt động càng nhanh, sẽ càng tối ưu thời gian xử lý
Với các yêu cầu trên, chúng ta xem xét thêm cấu trúc địa chỉ của địa chỉ MAC :
Chúng ta thấy rằng trong 48bit địa chỉ MAC, có 24 bit đầu là địa chỉ của nhà sản xuất, 24bit sau sẽ gắn cho cạc mạng. Như thế thay vì đưa cả 48bit địa chỉ MAC vào hàm băm, nếu chúng ta chỉ đưa 24bit đầu thì chúng ta sẽ nhóm được tất cả địa chỉ MAC của một hãng vào một danh sách liên kết duy nhất, điều này sẽ tạo thuận lợi cho việc quản lý cũng như thống kê.
Hàm băm của bảng MACVLAN sẽ được thiết kế như sau:
#define GOLDEN_RATIO_PRIME_24 0x9e3701UL
unsigned int hash_24(unsigned int val, unsigned int bits)
{
    unsigned int hash = val * GOLDEN_RATIO_PRIME_24;

    return (hash >> (24 - bits)) & ((1 << bits) - 1);
}
unsigned mac_hash(const void *key)
{
    unsigned char *macaddr = (unsigned char *)key;
    unsigned int mackey = 0;
    unsigned machash;

    mackey = (macaddr[0] << 16) | (macaddr[1] << 8) | (macaddr[2]);
    machash =  (unsigned )hash_24(mackey, L1_HASHKEY_BIT);     
    LOG("hashpos %u\n", machash);
    return machash;
}
Trong hàm mac_hash, chúng ta thấy rằng 24 bit đầu của địa chỉ mac sẽ được dùng để tạo ra mackey, mackey này sẽ làm đầu vào của hàm hash_24, đầu ra của hàm hash_24 sẽ trả ra một giá trị 8bit (chạy từ 0-255).

Trong hàm hash_24 ta thấy đầu ra sẽ được tính toán bằng cách nhân với một giá trị định nghĩa trước GOLDEN_RATIO_PRIME_24. Làm thế nào để tính toán được giá trị này? Chúng ta sẽ tìm hiểu một chút. Nếu bạn chưa từng nghe tới TỶ LỆ VÀNG trong toán học. Xin mời đọc qua một chút tại đây 
Tỷ lệ vàng là giá trị 1.618. Ưu điểm của tỷ lệ vàng là nó sẽ tạo ra một phân phối đều các đầu ra trên một tập đầu vào ngẫu nhiên như trong yêu cầu số (2) của hàm băm. Đầu vào của hàm là một giá trị 24-bit, cho nên tỷ lệ vàng của 24-bit này là (2^24-1)/1.618 ~ 0x9e3701 (Lấy tròn về 2^23+2^21−2^17+2^14−2^11−2^8 +1 để tối ưu cho phép nhân). Chúng ta chỉ lấy 8-bit cao nhất trong kết quả của phép nhân với tỷ lệ vàng (bạn có thể lấy 8-bit thấp nhất, sẽ không ai cản bạn đâu ạ) bằng phép AND bit với 0xFF. Bởi vì tỷ lệ vàng cũng là tỷ lệ hội tụ của hai số liên tiếp trong dãy Fibonacci, cho nên phương pháp xây dựng hàm hash theo tỷ lệ vàng còn được gọi là Fibonacci hash.

Như vậy chúng ta đã đi qua phần khó nhất của việc tổ chức bảng MACVLAN, chúng ta đi tiếp tới hàm so sánh để tìm bản ghi khi biết địa chỉ của danh sách liên kết chứa bản ghi

int mac_cmp(const void *x, const void *y)
{
    return memcmp(x, y, MACADDR_LEN);
}
Hàm đưa vào toàn bộ địa chỉ MAC, so sánh để tìm kiếm bản ghi phù hợp và trả ra kết quả. Nếu tìm thấy, hàm trả lại 0, ngược lại hàm sẽ trả ra giá trị khác 0
Khi các đầu vào đã đầy đủ, hàm khởi tạo bảng và cấp phát bộ nhớ cho bảng 
Table_T mactable;
int macv_init(void)
{
    mactable = Table_new(10000, mac_cmp, mac_hash);
    if(NULL == mactable)
    {
        LOG("table new error\n");
        return ERR_TABLE_NEW;
    }
    return SUCCESS;   
}

Chèn/Xóa/Tìm một bản ghi dựa vào key.

Bởi vì hàm sử dụng framework bảng băm, cho nên các giao diện này được xây dựng thật dễ dàng
mac_vlan_t *macv_insert(mac_vlan_t *mace)
{
    return Table_put(mactable, mace->macaddr, mace);
}
mac_vlan_t *macv_remove_by_macaddr(unsigned char *macaddr)
{
    return Table_remove(mactable, (const void *)macaddr);
}
mac_vlan_t *macv_find_by_macaddr(unsigned char *macaddr)
{
    return Table_get(mactable, (const void *)macaddr);
}
Lưu ý rằng đối với giao diện chèn một phần tử dựa vào key, hàm sẽ lấy vào một cấu trúc dữ liệu do người dùng chỉ định. Cấu trúc dữ liệu này chính là bản ghi cần được lưu giữ trong bảng băm
typedef struct mac_vlan_s
{
    unsigned int  vlanid;
    unsigned char macaddr[MACADDR_LEN];
    unsigned int  inc_port;
    unsigned int  status;     
} mac_vlan_t;
Nội dung bản ghi chứa các thông tin của bảng MACVLAN, bao gồm VLAN ID, địa chỉ MAC, giá trị này cũng chính là Primary Key của bảng, số hiệu cổng mạng của Switch mà từ đó địa chỉ MAC được học và phương pháp học, phương pháp này có hai giá trị DYNAMIC nghĩa là học động thông qua trao đổi các bản tin giữa máy tính và Switch hoặc STATIC - học tĩnh thông qua cấu hình từ Administrator. Bản ghi này cần được cấp phát bộ nhớ từ người sử dụng và truyền tới hàm INSERT, do đó người sử dụng phải tự giải phóng bộ nhớ của bản ghi khi bản ghi không còn được sử dụng, hàm REMOVE bản ghi dựa vào key chỉ xóa các cấu trúc dữ liệu quản lý bản ghi chứ không xóa bản ghi.

In ra tất cả các bản ghi trong bảng

Hàm in ra tất cả các bản ghi của bảng MACVLAN sử dụng phương thức Table_toArray của bảng để lưu lại tất cả địa chỉ của các bản ghi vào một mảng, địa chỉ MAC cũng sẽ được lưu tương ứng với từng bản ghi. Sử dụng phương thức này giúp chúng ta dễ dàng truy vấn thông tin của toàn bộ các bản ghi
int macv_total_record(void)
{
    return Table_length(mactable);
}
void show_macv(void)
{
    int i;
    void **array = Table_toArray(mactable, NULL);
    unsigned char *macaddr;
    mac_vlan_t *macvlan;

    LOG("--------------------------------------------\n\n");
    LOG("Vlan    Mac Address          Type         Port\n");
    LOG("----    ---------------      -------      -----\n");

    for(i=0; array[i]; i+=2)
    {
        macaddr = (unsigned char *)(array[i]);
        macvlan = (mac_vlan_t *)(array[i+1]);
        LOG("%4d    %02x:%02x:%02x:%02x:%02x:%02x    %7s    %4d\n", macvlan->vlanid, macvlan->macaddr[0], macvlan->macaddr[1], macvlan->macaddr[2], macvlan->macaddr[3], macvlan->macaddr[4], macvlan->macaddr[5], macvlan->status==1?"DYNAMIC":"STATIC", macvlan->inc_port);
    }
    LOG("Total of record %5d\n", macv_total_record());
    free(array);
}
Và một phần mềm tốt thì đồng nghĩa với việc có một giao diện đẹp, vì thế hàm in tất cả các bản ghi sẽ định dạng in như là vendor Cisco đã định dạng

Xóa bảng, giải phóng bộ nhớ bảng và các cấu trúc dữ liệu quản lý bản ghi

Việc xóa bảng cũng sử dụng phương thức xóa bảng của bảng băm. Tuy nhiên chúng ta nhớ rằng khi INSERT các bản ghi vào bảng, người dùng phải cấp phát bộ nhớ cho các bản ghi, cho nên tại đây người dùng phải tự giải phóng hết các bản ghi đã cấp phát. Việc xóa bảng sẽ không giải phóng các bản ghi mà chỉ giải phóng các cấu trúc quản lý bản ghi và chính bản thân bảng MACVLAN
void freeall_record(const void *key, void **value, void *cl)
{
    if(*value)
    {
        free(*value);
    }
}
void macv_delete_table(void)
{
    Table_map(mactable, freeall_record, NULL);
    Table_free(&mactable);
}
Chúng ta sử dụng phương thức Table_map của bảng băm và truyền vào đó hàm freeall_record để giải phóng tất cả các bản ghi trước khi xóa bảng. Chúng ta nhớ rằng phương thức Table_map sẽ duyệt qua tất cả các bản ghi của bảng, và chỉ cần truyền vào đó hàm giải phóng bộ nhớ là tất cả các bản ghi sẽ được tự động giải phóng.
Như vậy chúng ta đã kết thúc tất cả các giao diện quan trọng của bảng MACVLAN. Phần còn lại chỉ là các hàm được tôi viết để test các giao diện này. Các bạn có thể xóa nó trước khi release.
#define INSERT_TEST 10000
#define MACSTR_LEN 150
#define LEARNTYPE_LEN 30
mac_vlan_t *create_random_mac_vlan(void)
{
    int i;
    mac_vlan_t *mac_vlan_e = malloc(sizeof(mac_vlan_t));
    if(NULL == mac_vlan_e)
    {
        return NULL; 
    }
    mac_vlan_e->vlanid   = (unsigned int )(random()%2000);
    mac_vlan_e->inc_port = (unsigned int )(random()%24);
    mac_vlan_e->status = (unsigned int )(random()%2);
    for(i=0;i<MACADDR_LEN;i++)
        mac_vlan_e->macaddr[i] = (unsigned char)(((unsigned int)random()) & 0x000000ff);

    DEBUG("create %02x:%02x:%02x:%02x:%02x:%02x vlanid %d key %p macvlan %p \n",mac_vlan_e->macaddr[0], mac_vlan_e->macaddr[1],mac_vlan_e->macaddr[2],
                    mac_vlan_e->macaddr[3], mac_vlan_e->macaddr[4], mac_vlan_e->macaddr[5], mac_vlan_e->vlanid, &(mac_vlan_e->macaddr[0]), mac_vlan_e);
    return mac_vlan_e; 
}
int macv_insert_test(unsigned int num_insert_test)
{
    int i;
    for(i=0;i<num_insert_test;i++)
    {
        mac_vlan_t *mace = create_random_mac_vlan();
        macv_insert(mace);
    }
    return i;
}
int main(void)
{
    mac_vlan_t *found_mace = NULL, *add_mace = NULL;
    char macstr[MACSTR_LEN] = {0};
    unsigned char macaddr[MACADDR_LEN] = {0};
    int vlanid;
    char learntype[LEARNTYPE_LEN] = {0};
    int port;
    char command;
    int numread;
    macv_init(INSERT_TEST);
    macv_insert_test(INSERT_TEST/2000);
    LOG("Test bin already add %d record to mac table.Let type your command to query\n ", INSERT_TEST/2000);

    while(1)
    {
        memset(macstr, 0x00, MACSTR_LEN);
        memset(learntype, 0x00, LEARNTYPE_LEN);
        port = vlanid = 0;    
        fgets(macstr, MACSTR_LEN, stdin);
//        printf("Got str %s\n", macstr);
        numread = sscanf(macstr, "%c %*s %02x:%02x:%02x:%02x:%02x:%02x %*s %d %*s %s %*s %d",&command ,&macaddr[0], &macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4], &macaddr[5], &vlanid, learntype, &port);

        if(command == 'r')
        {
            found_mace = macv_remove_by_macaddr(macaddr);
            if(NULL == found_mace)
            {
                LOG("Address %02x:%02x:%02x:%02x:%02x:%02x not found in table, current total record %d\n",macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5] ,Table_length(mactable)); 
            }
            else
            {
                LOG("Address %02x:%02x:%02x:%02x:%02x:%02x deleted from table, current total record %d\n", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5],Table_length(mactable));
                free(found_mace);
            }
            continue;
        }
        else if(command == 'a')
        {
            if(0 >= vlanid || 0 >= port || 2048 < vlanid || 10000 < port)
            {
               LOG("Don't have info to add mace\n");
               continue; 
            }
            add_mace = malloc(sizeof(mac_vlan_t));
            add_mace->vlanid = vlanid;
            add_mace->inc_port = port;
            memcpy(add_mace->macaddr, macaddr, MACADDR_LEN);
            if(0 == strcmp(learntype, "STATIC"))
                add_mace->status = 0;
            else
                add_mace->status = 1;

            found_mace = macv_insert(add_mace); 
            if(NULL != found_mace)
            {
                LOG("Record of mac %02x:%02x:%02x:%02x:%02x:%02x existing in table. Info updated and current total record %d\n", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5], Table_length(mactable));
            }
            else
            {
                LOG("New record of mac %02x:%02x:%02x:%02x:%02x:%02x added to table, current total record %d\n", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5], Table_length(mactable));
            } 
            continue;
        }
        else if(command == 's')
        {
            show_macv();
            continue;
        } 
        else if(command == 'e')
        {
            macv_delete_table();
            LOG("Good bye !!!");
            break;
        }
        else
        {
             LOG("Unknown command %c.Type a-add, r-remove, s-show, e-exit. example \n", command);
             LOG("a mac c9:9a:66:32:0d:b7 vlanid 100 learntype DYNAMIC port 102\n");
             LOG("r mac c9:9a:66:32:0d:b7\n");
             LOG("s\n");
             LOG("e\n"); 
             continue;
        }
    }
  
    return 0;    
}
Phần test sẽ tạo ra 5 bản ghi được INSERT tự động vào bảng (các bạn có thể dễ dàng sửa code để tạo ra 10000 bản ghi, tôi chỉ tạo 5 vì muốn test hàm show), bảng sẽ được thao tác thông qua lệnh từ command-line
Lệnh
Mô tả
Ví dụ
s
Hiển thị nội dung tất cả các bản ghi trong bảng
S
r
Xóa một bản ghi dựa vào địa chỉ MAC
r mac c9:9a:66:32:0d:b7
a
Thêm một bản ghi dựa vào địa chỉ MAC, nếu bản ghi đã tồn tại, update thông tin bản ghi
a mac c9:9a:66:32:0d:b7 vlanid 100 learntype DYNAMIC port 102
e
Thoát khỏi chương trình, xóa tất cả các bản ghi, xóa bảng
E


Enjoy your result

Đánh giá hiệu năng phương pháp

Hiệu năng của phương pháp này phụ thuộc rất lớn vào giải thuật của hàm băm. Có rất nhiều nghiên cứu đã đưa ra các hàm băm rất phức tạp. Hàm băm tôi sử dụng trong bài viết này cũng được sử dụng trong mã nguồn của Linux kernel. Tuy nhiên hàm băm này thực sự chỉ tối ưu cho số lượng bản ghi là 2^24 = 16777216. Có lẽ chúng ta cần tìm một hàm băm khác tối ưu chỉ cho 10000 bản ghi. Phần này xin phép được dành cho độc giả.

Với 10000 bản ghi và 255 vị trí lưu các danh sách liên kết, trong trường hợp cả 10000 bản ghi được điền đầy, mỗi danh sách liên kết sẽ có khoảng 40 bản ghi, như vậy trường hợp xấu nhất, hàm compare sẽ được gọi 40 lần để tìm được một bản ghi, như thế vẫn khá tồi, nhưng chúng ta có thể dễ dàng tăng số lượng bit đầu ra, như thế số lượng bản ghi trong một danh sách liên kết sẽ giảm xuống.

Như vậy chúng ta đã tạo ra một bảng MACVLAN 10000 bản ghi dựa vào bảng băm. Trong bài viết kế tiếp tôi sẽ trình bày phương pháp sử dụng hash table để tổ chức cơ sở dữ liệu với 1 triệu bản ghi.

Toàn bộ mã nguồn của framework các bạn có thê download tại GitHub của Nhã Uyên Education
https://github.com/nhauyeneducation/linux_systemtraining

Tin học Nhã Uyên là một tổ chức thành lập với mục tiêu đào tạo kỹ sư lập trình hệ thống, kỹ sư lập trình nhúng và kỹ sư lập trình mạng trên nền tảng hệ điều hành Linux/Unix tại Việt Nam. 

Mời các bạn vào Facebook của Tin học Nhã Uyên tại


https://www.facebook.com/tinhocnhauyen/ để theo dõi các bài viết kỹ thuật chất lượng tiếp theo của Tin học Nhã Uyên. Xin trân trọng cảm ơn các bạn

Comments

Popular posts from this blog

Hiểu về tổ chức bộ nhớ của Linux thông qua ví dụ về Memory Mapping

Cơ sở lý thuyết về bộ nhớ ảo, bộ nhớ logic và bộ nhớ vật lý Hoạt động ánh xạ địa chỉ ảo tới địa chỉ vật lý Linux cung cấp cho các tiến trình hệ thống quản lý bộ nhớ ảo, nơi mỗi địa chỉ nhớ ảo có khả năng được ánh xạ tới một địa chỉ vật lý. Với độ dài 32 bit, toàn bộ không gian địa chỉ mỗi tiến trình có khả năng truy nhập là 2^32 ~ 4 Gigabit. Linux chia không gian địa chỉ này thành các trang nhớ (page) có độ dài bằng nhau (4096 bytes), mỗi khi tiến trình yêu cầu một vùng nhớ, cả trang nhớ tương ứng (chứa vùng nhớ) sẽ được cấp cho tiến trình. Bộ nhớ vật lý hệ thống chính là lượng RAM có trong hệ thống, Linux cũng chia bộ nhớ vật lý này thành các trang bằng nhau, gọi là các page frame, mỗi page frame được đánh số thứ tự gọi là các page frame number. Các địa chỉ ảo có thể sẽ được ánh xạ thành địa chỉ vật lý dựa vào các phần cứng gọi là các MMU (Memory Management Unit) theo một phương pháp gọi là lazy allocation . Theo phương pháp này, mỗi khi một vùng nhớ ảo được cấp phát cho tiến ...

Về một phương pháp quản lý bộ nhớ động trong Linux

Các kiến thức cơ bản về hệ thống Thư viện glibc trong Linux cung cấp cho chúng ta bốn hàm cơ bản để quản lý bộ nhớ động, ba trong số đó là các hàm cấp phát bộ nhớ, hàm còn lại là giải phóng bộ nhớ Hàm void *malloc(size_t size) nhận đầu vào số byte cần cấp phát và trả lại vùng nhớ được cấp phát. Nếu không tìm thấy vùng nhớ thỏa mãn độ dài cần cấp phát malloc sẽ trả về NULL Hàm void *calloc(size_t nmemb, size_t size ) sẽ cấp phát bộ nhớ cho một mảng nmemb*size và trả về con trỏ tới vùng nhớ được cấp phát, nhớ rằng mặc dù cấp phát bộ nhớ cho một mảng các phần tử nhưng nó vẫn là một vùng nhớ liên tục trong heap, vùng nhớ này được ghi giá trị 0 trên toàn vùng trước khi trả về Hàm void *realloc(void *ptr, size_t size) sẽ thay đổi số byte được cấp phát ở ptr và trả lại một vùng nhớ được cấp phát có độ dài size và có nội dung như là vùng nhớ ở ptr. Vùng nhớ được trả lại bởi realloc có thể chính là ptr trong trường hợp các vùng xung quanh ptr có thể đủ khả năng cung cấp một vùng dài h...

Về phương pháp notificall chain và các ứng dụng

Lời mở đầu Nhân hệ điều hành Linux được cấu thành từ rất nhiều các sub-system, mỗi sub-system này thực hiện các chức năng riêng biệt nhau, tuy nhiên một số nhóm sub-system có thể mong muốn nhận tập các sự kiện giống nhau. Nhân Linux thiết kế một phương pháp truyền thông để có thể chia sẻ một sự kiện tới nhiều các sub-system vừa tạo ra sự linh hoạt trong code vừa có khả năng mở rộng dễ dàng trong tương lai. Phương pháp đó gọi là notifier-call chain. Việc hiểu rõ hoạt động của phương pháp này là một nhu cầu giúp cho các kỹ sư thiết kế kiến trúc phần mềm có thể có một chất liệu phục vụ cho việc xây dựng hệ thống phần mềm của chính mình. Tổng quan về notifier-call chain Notifier-call chain là một danh sách liên kết đơn của các hàm sẽ được thực hiện tuần tự khi một sự kiện nhất định xảy ra. Mỗi hàm có chức năng giúp cho một sub-system nhất định biết về sự kiện và có hành động tương ứng đối với sự kiện đó. Như vậy notifi-call chain bao gồm hai thành phần, bên chủ động thông báo sự ...