Cấu trúc Metadata cho Fragment trong Hệ thống Tệp tin Phân tán
Bước đầu tiên là định nghĩa cấu trúc dữ liệu để Kernel biết mỗi mảnh (fragment) của dữ liệu được lưu trữ ở đâu trên các node mạng.
Chúng ta cần một cấu trúc metadata chứa thông tin về tổng số mảnh (threshold), tổng số mảnh cần thiết để khôi phục (k), và danh sách địa chỉ IP/Port của từng node chứa fragment.
Sửa file /usr/src/linux-5.x/drivers/shamir/shamir_fs.h để thêm định nghĩa cấu trúc này.
Định nghĩa cấu trúc shamir_frag_meta để lưu trữ thông tin phân tán.
#ifndef _SHAMIR_FS_H
#define _SHAMIR_FS_H
#include
#include
#define SHAMIR_MAX_NODES 64
#define SHAMIR_MAX_PATH_LEN 256
#define SHAMIR_FRAGMENT_ID_LEN 16
struct shamir_frag_meta {
__u32 total_shares; // Tổng số mảnh (n)
__u32 threshold; // Số mảnh tối thiểu để khôi phục (k)
__u64 data_version; // Phiên bản dữ liệu để kiểm tra đồng bộ
__u8 fragment_id[SHAMIR_FRAGMENT_ID_LEN]; // ID duy nhất của file
struct {
__u32 ip_addr; // Địa chỉ IP (dạng __u32)
__u16 port; // Cổng mạng
__u64 offset; // Vị trí lưu fragment trên node đích
__u32 size; // Kích thước fragment
} nodes[SHAMIR_MAX_NODES];
};
// Cấu trúc super_block mở rộng cho filesystem phân tán
struct shamir_fs_sb {
struct dentry *root;
spinlock_t meta_lock;
struct shamir_frag_meta current_meta;
struct list_head fragment_cache;
};
#endif
Kết quả mong đợi: File header mới được tạo, sẵn sàng để các module khác include và sử dụng để truy xuất vị trí fragment.
Triển khai cơ chế phân phối Fragment qua mạng (Kernel Socket)
Bây giờ chúng ta cần viết logic để gửi các fragment đã tạo ra từ thuật toán Shamir đến các node đích qua mạng.
Chúng ta sẽ sử dụng socket TCP trong kernel (AF_INET) để đảm bảo độ tin cậy, vì mất một fragment là không thể khôi phục dữ liệu.
Sửa file /usr/src/linux-5.x/drivers/shamir/shamir_net.c để thêm hàm gửi dữ liệu.
Định nghĩa hàm shamir_send_fragment để gửi dữ liệu qua socket TCP đến node đích.
#include
#include
#include
#include
#include
#include
#include "shamir_fs.h"
static int send_data_via_socket(struct sock *sk, const void *data, size_t len)
{
int err = 0;
struct sk_buff *skb;
struct msghdr msg;
int size = len + sizeof(struct shamir_frag_meta); // Gửi kèm metadata nhỏ
// Khởi tạo socket buffer
skb = sock_alloc_send_skb(sk, size, GFP_ATOMIC, &err);
if (!skb)
return -ENOMEM;
// Copy dữ liệu fragment vào skb
skb_put(skb, len);
memcpy(skb->data, data, len);
// Setup message header để gửi
memset(&msg, 0, sizeof(msg));
msg.msg_flags = 0;
err = sock_sendmsg(sk, &msg);
if (err < 0) {
kfree_skb(skb);
pr_err("Shamir Net: Failed to send fragment to %pI4, err=%d\n",
&sk->sk_dst_cache->daddr, err);
return err;
}
return 0;
}
static int connect_to_node(struct shamir_frag_meta *meta, int node_idx)
{
struct sock *sk;
int err;
struct sockaddr_in addr;
sk = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, GFP_KERNEL);
if (IS_ERR(sk)) {
pr_err("Shamir Net: Socket creation failed\n");
return PTR_ERR(sk);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = meta->nodes[node_idx].ip_addr;
addr.sin_port = meta->nodes[node_idx].port;
err = sk->ops->connect(sk, (struct sockaddr *)&addr, sizeof(addr), 0);
if (err) {
sock_release(sk);
pr_err("Shamir Net: Connect to node %d failed, err=%d\n", node_idx, err);
return err;
}
return 0;
}
// Hàm wrapper để phân phối toàn bộ fragments
int shamir_distribute_shares(struct shamir_frag_meta *meta, void **fragments, size_t *sizes)
{
int i, ret;
struct sock *sk;
for (i = 0; i < meta->total_shares; i++) {
if (connect_to_node(meta, i) != 0) {
pr_warn("Shamir Net: Skipping node %d (connection failed)\n", i);
continue;
}
// Giả sử socket đã được lưu trong context hoặc tạo mới mỗi lần
// Trong thực tế cần quản lý connection pool
sk = /* logic lấy socket đã connect */ NULL; // Placeholder
ret = send_data_via_socket(sk, fragments[i], sizes[i]);
if (ret != 0) {
pr_err("Shamir Net: Distribution failed at node %d\n", i);
return ret;
}
}
return 0;
}
Kết quả mong đợi: Module có khả năng thiết lập kết nối TCP và đẩy dữ liệu fragment vào mạng, log lỗi nếu node không phản hồi.
Xử lý Đồng bộ hóa và Cân bằng tải (Load Balancing)
Khi ghi dữ liệu, chúng ta cần chọn các node có tải thấp nhất để lưu fragment. Khi đọc, cần đảm bảo tất cả các fragment được thu thập trước khi giải mã.
Chúng ta sẽ sử dụng một cấu trúc bảng trạng thái (status table) để theo dõi tải của các node và cơ chế đồng bộ hóa (mutex/semaphore) để tránh race condition khi ghi.
Sửa file /usr/src/linux-5.x/drivers/shamir/shamir_sync.c.
Triển khai hàm shamir_balance_load để chọn node ít tải nhất và shamir_sync_write để ghi đồng bộ.
#include
#include
#include
#include
#include "shamir_fs.h"
// Cấu trúc theo dõi tải của node
struct node_load_tracker {
__u32 current_load; // Số request đang xử lý
__u64 last_seen; // Thời điểm cuối cùng ping thành công
atomic_t ref_count; // Số lượng fragment đang lưu
spinlock_t lock;
};
static struct node_load_tracker global_node_status[SHAMIR_MAX_NODES];
static DEFINE_SPINLOCK(distribution_lock);
// Hàm chọn node có tải thấp nhất (Weighted Round Robin đơn giản)
static int select_best_node(struct shamir_frag_meta *meta, int total_shares)
{
int best_idx = -1;
__u32 min_load = UINT_MAX;
int i;
spin_lock(&distribution_lock);
for (i = 0; i < total_shares; i++) {
if (i >= SHAMIR_MAX_NODES) break;
// Kiểm tra node còn khả dụng (đơn giản hóa: chỉ check load)
if (atomic_read(&global_node_status[i].ref_count) < min_load) {
min_load = atomic_read(&global_node_status[i].ref_count);
best_idx = i;
}
}
if (best_idx != -1) {
atomic_inc(&global_node_status[best_idx].ref_count);
}
spin_unlock(&distribution_lock);
if (best_idx != -1) {
// Cập nhật meta để biết node nào được chọn
meta->nodes[best_idx].ip_addr = /* logic lấy IP */ 0;
return best_idx;
}
return -1;
}
// Hàm ghi đồng bộ: Đảm bảo tất cả fragments được gửi trước khi trả về
int shamir_sync_write(struct inode *inode, const void *data, size_t len)
{
struct shamir_frag_meta meta;
void **fragments;
size_t *sizes;
int i, ret;
struct completion write_done;
int success_count = 0;
init_completion(&write_done);
// 1. Chia nhỏ dữ liệu (Giả sử hàm shamir_split đã có sẵn từ Phần 4)
// shamir_split(data, len, &meta.total_shares, &meta.threshold, &fragments, &sizes);
// 2. Cân bằng tải và phân phối
for (i = 0; i < meta.total_shares; i++) {
int node_idx = select_best_node(&meta, meta.total_shares);
if (node_idx < 0) {
pr_err("Shamir Sync: No available node for share %d\n", i);
continue;
}
// 3. Gửi fragment (non-blocking logic cần completion)
// shamir_send_async(meta.nodes[node_idx].ip_addr, fragments[i], sizes[i], &write_done);
success_count++;
}
// 4. Chờ tất cả các node xác nhận (simplified: wait for all)
// wait_for_completion_timeout(&write_done, HZ * 5);
// Dọn dẹp bộ nhớ fragments
// kfree(fragments); kfree(sizes);
if (success_count >= meta.threshold) {
return len; // Thành công nếu đạt ngưỡng
} else {
return -EIO; // Thất bại nếu không đủ fragment
}
}
Kết quả mong đợi: Hệ thống tự động chọn node ít tải nhất để ghi và chỉ trả về thành công khi đạt đủ số lượng fragment (threshold) được lưu trữ.
Verify Kết quả tích hợp
Sau khi biên dịch và load module, thực hiện các bước sau để xác minh tính năng.
1. Load module Shamir FS vào kernel.
sudo insmod shamir_fs.ko
Kiểm tra log kernel để đảm bảo không có lỗi khởi tạo.
dmesg | grep "Shamir"
Kết quả mong đợi: Xuất hiện dòng "Shamir FS: Loaded successfully" và các thông báo về việc đăng ký filesystem.
2. Mount filesystem phân tán và tạo file thử nghiệm.
sudo mkdir -p /mnt/shamir_dist
sudo mount -t shamir_fs -o "nodes=192.168.1.10:9000,192.168.1.11:9000,192.168.1.12:9000" none /mnt/shamir_dist
echo "Test data for distributed storage" > /mnt/shamir_dist/testfile.bin
Kết quả mong đợi: File được tạo thành công, không báo lỗi I/O.
3. Kiểm tra phân phối fragment trên các node đích (giả sử các node đã chạy daemon nhận socket).
ssh user@192.168.1.10 "ls -lh /var/lib/shamir_store/fragments/"
ssh user@192.168.1.11 "ls -lh /var/lib/shamir_store/fragments/"
ssh user@192.168.1.12 "ls -lh /var/lib/shamir_store/fragments/"
Kết quả mong đợi: Các file fragment có ID tương ứng xuất hiện trên cả 3 node (hoặc số lượng node theo cấu hình threshold).
4. Đọc lại file để kiểm tra cơ chế khôi phục (Reconstruction).
cat /mnt/shamir_dist/testfile.bin
Kết quả mong đợi: Nội dung "Test data for distributed storage" được hiển thị chính xác, chứng tỏ quá trình tập hợp fragment và giải mã Shamir thành công.
5. Kiểm tra cân bằng tải bằng cách tạo nhiều file liên tục.
for i in {1..10}; do echo "Data set $i" > /mnt/shamir_dist/file_$i.bin; done
dmesg | grep -A 5 "Shamir Net: Distribution"
Kết quả mong đợi: Log hiển thị việc dữ liệu được phân tán đều trên các node, không tập trung vào một node duy nhất.
Điều hướng series:
Mục lục: Series: Triển khai Database phân tán với Shamir Secret Sharing và Linux Kernel Modules
« Phần 4: Triển khai thuật toán Shamir trong không gian Kernel (User Space vs Kernel Space)
Phần 6: Quản lý bảo mật: Mã hóa, xác thực và kiểm soát truy cập »