强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Vala 语言入门教程 / 09 - POSIX 绑定

第 9 章:POSIX 绑定

Vala 编译为 C,天然具备调用 POSIX API 的能力。本章将介绍如何通过 Vala 进行系统级编程。


9.1 POSIX 与 GLib 的关系

Vala 通过两条路径访问系统 API:

┌──────────────────────────────────────────┐
│              Vala 源代码                   │
│         ┌──────────┴──────────┐          │
│    GLib/GIO 绑定         POSIX 绑定       │
│   (跨平台, 推荐)      (直接系统调用)       │
│         │                   │             │
│    GLib C API          POSIX C API       │
│         │                   │             │
│         └──────┬────────────┘             │
│           Linux/POSIX 内核                 │
└──────────────────────────────────────────┘
路径优点缺点
GLib 绑定跨平台、自动内存管理功能有限
POSIX 绑定功能完整、性能好平台相关

9.2 文件 I/O

9.2.1 GLib 文件操作(推荐)

void main () {
    // 写入文件
    try {
        GLib.FileUtils.set_contents (
            "/tmp/test.txt",
            "Hello, Vala!\n这是测试内容。\n"
        );
        print ("文件写入成功\n");
    } catch (GLib.FileError e) {
        printerr ("写入失败: %s\n", e.message);
    }

    // 读取文件
    try {
        string content;
        size_t length;
        GLib.FileUtils.get_contents ("/tmp/test.txt", out content, out length);
        print ("文件内容 (%zu 字节):\n%s\n", length, content);
    } catch (GLib.FileError e) {
        printerr ("读取失败: %s\n", e.message);
    }

    // 追加写入
    try {
        var stream = GLib.FileStream.open ("/tmp/test.txt", "a");
        if (stream != null) {
            stream.puts ("追加的内容\n");
            stream.flush ();
        }
    } catch (GLib.Error e) {
        printerr ("追加失败: %s\n", e.message);
    }

    // 检查文件是否存在
    bool exists = GLib.FileUtils.test (
        "/tmp/test.txt", GLib.FileTest.EXISTS
    );
    print ("文件存在: %s\n", exists.to_string ());

    // 获取文件信息
    try {
        int64 size;
        GLib.FileUtils.get_contents ("/tmp/test.txt", null, out size);
        print ("文件大小: %lld 字节\n", size);
    } catch (GLib.FileError e) {
        printerr ("错误: %s\n", e.message);
    }
}

9.2.2 GLib FileStream

void main () {
    // 使用 FileStream(类似 C 的 FILE*)
    var stream = GLib.FileStream.open ("/tmp/data.csv", "w");
    if (stream == null) {
        printerr ("无法打开文件\n");
        return;
    }

    // 写入 CSV 数据
    stream.printf ("姓名,年龄,城市\n");
    stream.printf ("张三,30,北京\n");
    stream.printf ("李四,25,上海\n");
    stream.printf ("王五,28,广州\n");
    stream.flush ();

    // 读取文件
    var reader = GLib.FileStream.open ("/tmp/data.csv", "r");
    if (reader != null) {
        string line;
        while ((line = reader.read_line ()) != null) {
            print ("  %s\n", line);
        }
    }
}

9.2.3 异步文件 I/O

public async void read_file_async (string path) throws GLib.Error {
    var file = GLib.File.new_for_path (path);

    // 异步读取
    var stream = yield file.read_async (GLib.Priority.DEFAULT, null);
    var data_stream = new GLib.DataInputStream (stream);

    string line;
    size_t length;
    while ((line = yield data_stream.read_line_async (
        GLib.Priority.DEFAULT, null, out length)) != null) {
        print ("%s\n", line);
    }

    yield stream.close_async (GLib.Priority.DEFAULT, null);
}

public async void write_file_async (string path, string content)
    throws GLib.Error
{
    var file = GLib.File.new_for_path (path);
    var stream = yield file.replace_async (
        null, false, GLib.FileCreateFlags.NONE,
        GLib.Priority.DEFAULT, null
    );

    yield stream.write_async (
        content.data, GLib.Priority.DEFAULT, null
    );
    yield stream.close_async (GLib.Priority.DEFAULT, null);
}

void main () {
    var loop = new GLib.MainLoop ();

    write_file_async.begin ("/tmp/async.txt", "异步写入的内容\n", (obj, res) => {
        try {
            write_file_async.end (res);
            print ("异步写入完成\n");

            read_file_async.begin ("/tmp/async.txt", (obj2, res2) => {
                try {
                    read_file_async.end (res2);
                } catch (GLib.Error e) {
                    printerr ("读取错误: %s\n", e.message);
                }
                loop.quit ();
            });
        } catch (GLib.Error e) {
            printerr ("写入错误: %s\n", e.message);
            loop.quit ();
        }
    });

    loop.run ();
}

9.2.4 目录操作

void main () {
    // 创建目录
    try {
        GLib.DirUtils.create ("/tmp/testdir", 0755);
        print ("目录创建成功\n");
    } catch (GLib.Error e) {
        printerr ("创建目录失败: %s\n", e.message);
    }

    // 创建嵌套目录
    try {
        GLib.DirUtils.create_with_parents ("/tmp/a/b/c", 0755);
        print ("嵌套目录创建成功\n");
    } catch (GLib.Error e) {
        printerr ("错误: %s\n", e.message);
    }

    // 列出目录内容
    try {
        var dir = GLib.Dir.open ("/tmp");
        string? name;
        while ((name = dir.read_name ()) != null) {
            string full_path = GLib.Path.build_filename ("/tmp", name);
            print ("  %s\n", full_path);
        }
    } catch (GLib.Error e) {
        printerr ("错误: %s\n", e.message);
    }

    // 递归遍历目录
    print ("\n递归遍历 /tmp/testdir:\n");
    traverse_dir ("/tmp/testdir");

    // 删除目录
    try {
        GLib.DirUtils.remove ("/tmp/testdir");
        print ("目录删除成功\n");
    } catch (GLib.Error e) {
        printerr ("删除失败: %s\n", e.message);
    }
}

void traverse_dir (string path) {
    try {
        var dir = GLib.Dir.open (path);
        string? name;
        while ((name = dir.read_name ()) != null) {
            string full_path = GLib.Path.build_filename (path, name);
            print ("  %s\n", full_path);

            if (GLib.FileUtils.test (full_path, GLib.FileTest.IS_DIR)) {
                traverse_dir (full_path);
            }
        }
    } catch (GLib.Error e) {
        printerr ("遍历错误: %s\n", e.message);
    }
}

9.3 进程管理

9.3.1 启动外部进程

void main () {
    // 简单的命令执行
    try {
        int exit_status;
        string std_out;
        string std_err;

        GLib.Process.spawn_command_line_sync (
            "ls -la /tmp",
            out std_out,
            out std_err,
            out exit_status
        );

        print ("输出:\n%s\n", std_out);
        print ("退出码: %d\n", exit_status);

        if (std_err.length > 0) {
            printerr ("错误:\n%s\n", std_err);
        }
    } catch (GLib.Error e) {
        printerr ("执行失败: %s\n", e.message);
    }
}

9.3.2 异步进程执行

public async int run_command (string command) throws GLib.Error {
    string[] argv;
    try {
        GLib.Shell.parse_argv (command, out argv);
    } catch (GLib.Error e) {
        throw e;
    }

    int exit_status;
    string std_out;
    string std_err;

    // 使用 spawn 同步版本(简化示例)
    GLib.Process.spawn_sync (
        null,          // 工作目录
        argv,          // 命令和参数
        null,          // 环境变量
        GLib.SpawnFlags.SEARCH_PATH,
        null,          // 子进程设置
        out std_out,
        out std_err,
        out exit_status
    );

    print ("输出:\n%s\n", std_out);
    if (std_err.length > 0) {
        printerr ("错误:\n%s\n", std_err);
    }

    return exit_status;
}

void main () {
    var loop = new GLib.MainLoop ();

    run_command.begin ("uname -a", (obj, res) => {
        try {
            int status = run_command.end (res);
            print ("退出码: %d\n", status);
        } catch (GLib.Error e) {
            printerr ("错误: %s\n", e.message);
        }
        loop.quit ();
    });

    loop.run ();
}

9.3.3 使用 Subprocess(GIO 推荐)

void main () {
    try {
        // 创建子进程
        var subprocess = new GLib.Subprocess (
            GLib.SubprocessFlags.STDOUT_PIPE | GLib.SubprocessFlags.STDERR_PIPE,
            "echo", "Hello from subprocess"
        );

        // 读取输出
        var stdout_stream = subprocess.get_stdout_pipe ();
        var data_stream = new GLib.DataInputStream (
            new GLib.UnixInputStream (stdout_stream, true)
        );

        string line;
        size_t length;
        while ((line = data_stream.read_line (null, out length)) != null) {
            print ("子进程输出: %s\n", line);
        }

        // 等待子进程完成
        subprocess.wait (null);
        int status = subprocess.get_exit_status ();
        print ("退出码: %d\n", status);

    } catch (GLib.Error e) {
        printerr ("错误: %s\n", e.message);
    }
}

9.3.4 进程管道

void main () {
    try {
        // 管道连接多个命令
        var proc1 = new GLib.Subprocess (
            GLib.SubprocessFlags.STDOUT_PIPE,
            "ls", "-la"
        );

        var proc2 = new GLib.Subprocess (
            GLib.SubprocessFlags.STDIN_PIPE | GLib.SubprocessFlags.STDOUT_PIPE,
            "grep", ".vala"
        );

        // 获取 proc1 的输出
        var stdout1 = proc1.get_stdout_pipe ();

        // 传递给 proc2 的输入
        var stdin2 = proc2.get_stdin_pipe ();

        // 复制数据
        var data_stream = new GLib.DataInputStream (
            new GLib.UnixInputStream (stdout1, true)
        );

        string line;
        while ((line = data_stream.read_line (null, null)) != null) {
            stdin2.write ((line + "\n").data);
        }
        stdin2.close ();

        // 读取最终输出
        var stdout2 = proc2.get_stdout_pipe ();
        var final_stream = new GLib.DataInputStream (
            new GLib.UnixInputStream (stdout2, true)
        );

        while ((line = final_stream.read_line (null, null)) != null) {
            print ("%s\n", line);
        }

    } catch (GLib.Error e) {
        printerr ("错误: %s\n", e.message);
    }
}

9.4 线程编程

9.4.1 基本线程

void main () {
    print ("主线程开始\n");

    // 创建线程
    var thread1 = new GLib.Thread<string> ("worker1", () => {
        for (int i = 0; i < 5; i++) {
            print ("  [线程1] 工作中... %d\n", i);
            GLib.Thread.usleep (500000);  // 0.5 秒
        }
        return "线程1完成";
    });

    var thread2 = new GLib.Thread<string> ("worker2", () => {
        for (int i = 0; i < 3; i++) {
            print ("  [线程2] 工作中... %d\n", i);
            GLib.Thread.usleep (800000);  // 0.8 秒
        }
        return "线程2完成";
    });

    // 等待线程完成
    string result1 = thread1.join ();
    string result2 = thread2.join ();

    print ("结果1: %s\n", result1);
    print ("结果2: %s\n", result2);
    print ("主线程结束\n");
}

9.4.2 线程安全:互斥锁

// 共享数据
private int shared_counter = 0;
private GLib.Mutex mutex = GLib.Mutex ();

void worker (string name, int iterations) {
    for (int i = 0; i < iterations; i++) {
        mutex.lock ();
        shared_counter++;
        print ("[%s] 计数器: %d\n", name, shared_counter);
        mutex.unlock ();

        GLib.Thread.usleep (100000);  // 0.1 秒
    }
}

void main () {
    print ("=== 互斥锁示例 ===\n");

    var t1 = new GLib.Thread<void> ("t1", () => { worker ("线程1", 5); });
    var t2 = new GLib.Thread<void> ("t2", () => { worker ("线程2", 5); });
    var t3 = new GLib.Thread<void> ("t3", () => { worker ("线程3", 5); });

    t1.join ();
    t2.join ();
    t3.join ();

    print ("最终计数器: %d (应为 15)\n", shared_counter);
}

9.4.3 条件变量

private GLib.Mutex mutex = GLib.Mutex ();
private GLib.Cond cond = GLib.Cond ();
private bool data_ready = false;
private string shared_data = "";

void producer () {
    for (int i = 0; i < 5; i++) {
        GLib.Thread.usleep (1000000);  // 1 秒

        mutex.lock ();
        shared_data = "数据 #%d".printf (i);
        data_ready = true;
        print ("[生产者] 生产: %s\n", shared_data);
        cond.signal ();  // 通知消费者
        mutex.unlock ();
    }

    mutex.lock ();
    shared_data = "DONE";
    data_ready = true;
    cond.signal ();
    mutex.unlock ();
}

void consumer () {
    while (true) {
        mutex.lock ();
        while (!data_ready) {
            cond.wait (mutex);  // 等待信号
        }

        if (shared_data == "DONE") {
            mutex.unlock ();
            break;
        }

        print ("[消费者] 消费: %s\n", shared_data);
        data_ready = false;
        mutex.unlock ();
    }
}

void main () {
    print ("=== 生产者-消费者示例 ===\n");

    var producer_thread = new GLib.Thread<void> ("producer", producer);
    var consumer_thread = new GLib.Thread<void> ("consumer", consumer);

    producer_thread.join ();
    consumer_thread.join ();
    print ("完成\n");
}

9.4.4 线程池

void main () {
    // 使用 GLib 线程池
    var pool = new GLib.ThreadPool<WorkItem> (
        worker_func,
        4,       // 最大线程数
        false    // 不独占
    );

    // 添加任务
    for (int i = 0; i < 10; i++) {
        var item = new WorkItem ("任务_%d".printf (i));
        pool.add (item);
    }

    // 等待一段时间让任务完成
    GLib.Thread.usleep (5000000);
    print ("所有任务已提交\n");
}

public class WorkItem : Object {
    public string name { get; set; }

    public WorkItem (string name) {
        Object (name: name);
    }
}

void worker_func (owned WorkItem item) {
    print ("处理: %s (线程: %p)\n", item.name, (void*) GLib.Thread.self ());
    GLib.Thread.usleep (500000);  // 0.5 秒
}

9.4.5 异步队列

void main () {
    // 创建异步队列
    var queue = new GLib.AsyncQueue<string> ();

    // 生产者线程
    var producer_thread = new GLib.Thread<void> ("producer", () => {
        for (int i = 0; i < 10; i++) {
            string item = "消息 #%d".printf (i);
            queue.push (item);
            print ("[生产] %s\n", item);
            GLib.Thread.usleep (300000);
        }
        queue.push ("QUIT");
    });

    // 消费者线程
    var consumer_thread = new GLib.Thread<void> ("consumer", () => {
        while (true) {
            string? item = queue.pop ();  // 阻塞等待
            if (item == "QUIT") break;
            print ("[消费] %s\n", item);
        }
    });

    producer_thread.join ();
    consumer_thread.join ();
    print ("完成\n");
}

9.5 网络编程

9.5.1 TCP 客户端

void main () {
    try {
        // 解析地址
        var resolver = GLib.Resolver.get_default ();
        var addresses = resolver.lookup_by_name ("example.com", null);

        foreach (var addr in addresses) {
            print ("地址: %s\n", addr.to_string ());
        }

        // 创建 Socket 连接
        var client = new GLib.SocketClient ();
        var connection = client.connect (
            new GLib.NetworkAddress ("example.com", 80),
            null
        );

        print ("已连接到 %s\n",
               connection.get_remote_address ().to_string ());

        // 发送 HTTP 请求
        string request = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
        connection.get_output_stream ().write (request.data);

        // 读取响应
        var data_stream = new GLib.DataInputStream (
            connection.get_input_stream ()
        );

        string line;
        size_t length;
        int line_count = 0;
        while ((line = data_stream.read_line (null, out length)) != null) {
            print ("%s\n", line);
            line_count++;
            if (line_count > 20) break;  // 只显示前 20 行
        }

        connection.close (null);

    } catch (GLib.Error e) {
        printerr ("网络错误: %s\n", e.message);
    }
}

9.5.2 TCP 服务器

void main () {
    try {
        // 创建 TCP 服务器
        var server = new GLib.SocketService ();

        // 添加监听端口
        server.add_inet_port (8080, null);

        // 连接处理
        server.incoming.connect ((connection, source) => {
            handle_client.begin (connection);
            return true;
        });

        print ("服务器监听端口 8080...\n");

        // 运行主循环
        var loop = new GLib.MainLoop ();
        loop.run ();

    } catch (GLib.Error e) {
        printerr ("服务器错误: %s\n", e.message);
    }
}

private async void handle_client (GLib.SocketConnection connection) {
    try {
        var addr = connection.get_remote_address ().to_string ();
        print ("新连接: %s\n", addr);

        var input = new GLib.DataInputStream (connection.get_input_stream ());
        var output = connection.get_output_stream ();

        // 发送欢迎消息
        string welcome = "欢迎连接到 Vala 服务器!\n";
        output.write (welcome.data);

        // 读取客户端消息
        string? line;
        while ((line = yield input.read_line_async (
            GLib.Priority.DEFAULT, null)) != null) {
            print ("收到 [%s]: %s\n", addr, line);

            // 回显
            string response = "服务器收到: %s\n".printf (line);
            output.write (response.data);
        }

        print ("断开连接: %s\n", addr);
        connection.close (null);

    } catch (GLib.Error e) {
        printerr ("客户端错误: %s\n", e.message);
    }
}

9.5.3 UDP 通信

void main () {
    try {
        // 创建 UDP Socket
        var socket = new GLib.Socket (
            GLib.SocketFamily.IPV4,
            GLib.SocketType.DATAGRAM,
            GLib.SocketProtocol.UDP
        );

        // 绑定地址
        var addr = new GLib.InetSocketAddress (
            new GLib.InetAddress.from_string ("0.0.0.0"),
            9999
        );
        socket.bind (addr, true);

        print ("UDP 服务器监听端口 9999...\n");

        // 接收数据
        uint8 buffer[1024];
        GLib.SocketAddress sender_addr;

        while (true) {
            ssize_t received = socket.receive_from (
                out sender_addr, buffer, null
            );

            if (received > 0) {
                string message = (string) buffer;
                message = message.substring (0, received);
                print ("收到来自 %s: %s\n",
                       sender_addr.to_string (), message);

                // 回复
                string reply = "已收到: %s".printf (message);
                socket.send_to (sender_addr, reply.data, null);
            }
        }

    } catch (GLib.Error e) {
        printerr ("UDP 错误: %s\n", e.message);
    }
}

9.6 调用 C 库

9.6.1 使用 [CCode] 注解

// 绑定 C 函数
[CCode (cheader_filename = "math.h")]
namespace MathNative {
    [CCode (cname = "sin")]
    public static double sin (double x);

    [CCode (cname = "cos")]
    public static double cos (double x);

    [CCode (cname = "sqrt")]
    public static double sqrt (double x);
}

// 绑定自定义 C 函数
[CCode (cheader_filename = "mylib.h")]
namespace MyLib {
    [CCode (cname = "my_function")]
    public static int my_function (int x, int y);
}

void main () {
    // 使用绑定的 C 函数
    print ("sin(π/2) = %f\n", MathNative.sin (3.14159 / 2));
    print ("cos(0) = %f\n", MathNative.cos (0));
    print ("sqrt(16) = %f\n", MathNative.sqrt (16));
}

9.6.2 创建 VAPI 绑定文件

// mylib.vapi —— C 库的 Vala 绑定
[CCode (cheader_filename = "mylib.h")]
namespace MyLib {
    // 函数绑定
    [CCode (cname = "mylib_init")]
    public static int init ();

    [CCode (cname = "mylib_cleanup")]
    public static void cleanup ();

    // 结构体绑定
    [CCode (cname = "MyLibConfig")]
    public struct Config {
        public string name;
        public int version;
        public bool debug;
    }

    // 回调绑定
    [CCode (cname = "MyLibCallback", has_target = true)]
    public delegate void Callback (string message);

    // 枚举绑定
    [CCode (cname = "MyLibStatus")]
    public enum Status {
        OK = 0,
        ERROR = -1,
        TIMEOUT = -2
    }
}

9.6.3 调用系统命令

void main () {
    // 使用 Posix 命名空间
    print ("PID: %d\n", Posix.getpid ());
    print ("UID: %d\n", Posix.getuid ());
    print ("GID: %d\n", Posix.getgid ());

    // 获取环境变量
    string? home = GLib.Environment.get_variable ("HOME");
    print ("HOME: %s\n", home);

    string? path = GLib.Environment.get_variable ("PATH");
    print ("PATH: %s\n", path ?? "未设置");

    // 设置环境变量
    GLib.Environment.set_variable ("MY_VAR", "hello", true);
    print ("MY_VAR: %s\n", GLib.Environment.get_variable ("MY_VAR"));

    // 获取系统信息
    print ("主机名: %s\n", GLib.Environment.get_host_name ());
    print ("用户名: %s\n", GLib.Environment.get_user_name ());
    print ("用户目录: %s\n", GLib.Environment.get_home_dir ());
    print ("临时目录: %s\n", GLib.Environment.get_tmp_dir ());
    print ("当前目录: %s\n", GLib.Environment.get_current_dir ());
}

9.6.4 信号处理

void main () {
    // 设置信号处理器
    GLib.Unix.signal_add (Posix.Signal.INT, () => {
        print ("\n收到 SIGINT (Ctrl+C)\n");
        return GLib.Source.REMOVE;
    });

    GLib.Unix.signal_add (Posix.Signal.TERM, () => {
        print ("\n收到 SIGTERM\n");
        return GLib.Source.REMOVE;
    });

    GLib.Unix.signal_add (Posix.Signal.USR1, () => {
        print ("\n收到 SIGUSR1\n");
        return GLib.Source.CONTINUE;
    });

    print ("进程 PID: %d\n", Posix.getpid ());
    print ("发送信号: kill -USR1 %d\n", Posix.getpid ());
    print ("按 Ctrl+C 退出\n");

    var loop = new GLib.MainLoop ();
    loop.run ();
}

9.7 定时器和调度

void main () {
    int count = 0;

    // 定时器:每秒执行
    GLib.Timeout.add (1000, () => {
        count++;
        print ("定时器触发: %d\n", count);

        if (count >= 5) {
            print ("定时器停止\n");
            return GLib.Source.REMOVE;  // 移除定时器
        }
        return GLib.Source.CONTINUE;  // 继续
    });

    // Idle 回调:空闲时执行
    uint idle_count = 0;
    GLib.Idle.add (() => {
        idle_count++;
        if (idle_count > 100) {
            return GLib.Source.REMOVE;
        }
        // 不要在这里做重活
        return GLib.Source.CONTINUE;
    });

    // 延迟执行
    GLib.Timeout.add_once (3000, () => {
        print ("3 秒延迟执行\n");
    });

    var loop = new GLib.MainLoop ();
    loop.run ();
}

9.8 业务场景:系统监控工具

// 系统信息结构
public class SystemInfo : Object {
    public int cpu_usage { get; set; }
    public int64 mem_total { get; set; }
    public int64 mem_used { get; set; }
    public int64 disk_total { get; set; }
    public int64 disk_free { get; set; }

    public double mem_usage_percent {
        get {
            if (mem_total == 0) return 0;
            return (double) mem_used / mem_total * 100;
        }
    }
}

// 获取系统信息
public SystemInfo get_system_info () {
    var info = new SystemInfo ();

    // 读取内存信息
    try {
        string content;
        GLib.FileUtils.get_contents ("/proc/meminfo", out content);
        string[] lines = content.split ("\n");

        foreach (string line in lines) {
            if (line.has_prefix ("MemTotal:")) {
                info.mem_total = parse_kb (line) * 1024;
            } else if (line.has_prefix ("MemAvailable:")) {
                int64 available = parse_kb (line) * 1024;
                info.mem_used = info.mem_total - available;
            }
        }
    } catch (GLib.Error e) {
        printerr ("读取内存信息失败: %s\n", e.message);
    }

    // 读取磁盘信息
    try {
        int exit_status;
        string std_out;
        GLib.Process.spawn_command_line_sync (
            "df -B1 / | tail -1",
            out std_out, null, out exit_status
        );
        string[] parts = std_out.strip ().split_set (" \t");
        if (parts.length >= 4) {
            info.disk_total = int64.parse (parts[1]);
            info.disk_free = int64.parse (parts[3]);
        }
    } catch (GLib.Error e) {
        printerr ("读取磁盘信息失败: %s\n", e.message);
    }

    return info;
}

int64 parse_kb (string line) {
    string[] parts = line.split_set (" \t");
    foreach (string part in parts) {
        if (part.length > 0 && part[0] >= '0' && part[0] <= '9') {
            return int64.parse (part);
        }
    }
    return 0;
}

string format_bytes (int64 bytes) {
    if (bytes >= 1073741824) {
        return "%.1f GB".printf ((double) bytes / 1073741824);
    } else if (bytes >= 1048576) {
        return "%.1f MB".printf ((double) bytes / 1048576);
    }
    return "%lld B".printf (bytes);
}

void print_bar (string label, double percent) {
    int width = 30;
    int filled = (int) (percent / 100 * width);
    string bar = "";
    for (int i = 0; i < width; i++) {
        bar += i < filled ? "█" : "░";
    }
    print ("  %s: [%s] %.1f%%\n", label, bar, percent);
}

void main () {
    print ("=== 系统监控工具 ===\n\n");

    // 定时刷新
    GLib.Timeout.add (2000, () => {
        var info = get_system_info ();

        // 清屏
        print ("\033[2J\033[H");
        print ("=== 系统监控 ===\n");
        print ("按 Ctrl+C 退出\n\n");

        print_bar ("内存", info.mem_usage_percent);
        print ("  内存: %s / %s\n\n",
               format_bytes (info.mem_used),
               format_bytes (info.mem_total));

        double disk_percent = info.disk_total > 0
            ? (double) (info.disk_total - info.disk_free) / info.disk_total * 100
            : 0;
        print_bar ("磁盘", disk_percent);
        print ("  磁盘: %s 可用 / %s 总计\n",
               format_bytes (info.disk_free),
               format_bytes (info.disk_total));

        return GLib.Source.CONTINUE;
    });

    var loop = new GLib.MainLoop ();
    GLib.Unix.signal_add (Posix.Signal.INT, () => {
        loop.quit ();
        return GLib.Source.REMOVE;
    });
    loop.run ();
}

9.9 注意事项

⚠️ 系统编程常见陷阱

  1. 线程安全:GTK 不是线程安全的,不要在非主线程操作 UI
  2. 文件描述符泄漏:确保关闭所有打开的文件描述符
  3. 竞态条件:使用互斥锁保护共享数据
  4. 信号处理:在信号处理器中只调用异步信号安全的函数
  5. 阻塞操作:不要在主线程执行阻塞的 I/O 操作
  6. 权限:某些操作需要 root 权限

9.10 扩展阅读

资源链接
GLib I/Ohttps://docs.gtk.org/gio/
GLib 线程https://docs.gtk.org/glib/threads.html
GLib 网络https://docs.gtk.org/gio/networking.html
POSIX 手册man 7 posix
Unix 信号man 7 signal
Vala POSIX 绑定https://valadoc.org/posix.html

9.11 总结

要点说明
文件 I/O推荐使用 GLib.FileUtils / FileStream
进程GLib.Process.spawn_* 或 GLib.Subprocess
线程GLib.Thread + Mutex + Cond
网络GLib.SocketClient / SocketService
C 绑定[CCode] 注解 + VAPI 文件
信号GLib.Unix.signal_add
定时器GLib.Timeout.add / Idle.add

下一章我们将学习 D-Bus 集成。→ 第 10 章:D-Bus 集成