2 つのゴルーチンを実行して VM を変更し、同じインスタンス上で libvirt-go パッケージを使用して同じ VM を破棄するとどうなりますか?

王林
リリース: 2024-02-08 21:48:12
転載
1113 人が閲覧しました

我运行 2 个 goroutine 来修改虚拟机并在同一实例上使用 libvirt-go 包销毁同一虚拟机会发生什么?

php Xiaobian Yuzai あなたが言及した問題には、libvirt-go パッケージを使用して同じインスタンス上で仮想マシンを同時に破棄することが含まれます。この場合、2 つのゴルーチンが同時に仮想マシンを変更する可能性があり、予期しない結果が生じる可能性があります。ゴルーチンは同時実行時に実行順序を保証できないため、競合状態やデータ競合が発生し、仮想マシンの破壊失敗やデータ破損などの異常が発生する可能性があります。この状況を回避するには、ミューテックス ロックまたはその他の同時実行制御メカニズムを使用して、一度に 1 つの goroutine だけが仮想マシンを変更できるようにします。これにより、操作の原子性と一貫性が確保され、不要な問題が回避されます。

質問内容

ご存知のとおり、libvirt はスレッドセーフです。 ただし、同じリソース上で動作する 2 つのゴルーチンを同時に実行すると (仮想マシンの変更や削除など)、曖昧な状態のままになります。 libvirt はゴルーチンの実行順序をどのように決定しますか?

これは私が試したコードです:

リーリー

手順が正常に完了したため、ドメインが破棄され、再作成できるようになりました。 しかし、再度実行すると次のエラーが発生します:

package main

import (
    "fmt"

    "github.com/libvirt/libvirt-go"
)

func main() {
    conn, err := libvirt.NewConnect("qemu:///system")
    if err != nil {
        fmt.Printf("Failed to connect to libvirt: %v\n", err)
        return
    }
    defer conn.Close()

    // Create a new VM
    domainXML := `
        <domain type='kvm'>
            <name>myvm</name>
            <memory unit='KiB'>1048576</memory>
            <vcpu placement='static'>1</vcpu>
            <os>
                <type arch='x86_64' machine='pc-i440fx-2.9'>hvm</type>
                <boot dev='hd'/>
            </os>
            <devices>
                <disk type='file' device='disk'>
                    <driver name='qemu' type='qcow2'/>
                    <source file='path/to/disk'/>
                    <target dev='vda' bus='virtio'/>
                    <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
                </disk>
            </devices>
        </domain>`

    dom, err := createVM(conn, domainXML)
    if err != nil {
        fmt.Printf("Failed to create VM: %v\n", err)
        return
    }

    go modifyVMMemory(dom, 2*1024*1024) // 2 GiB

    go deleteVM(dom)

}

func createVM(conn *libvirt.Connect, domainXML string) (*libvirt.Domain, error) {
    dom, err := conn.DomainCreateXML(domainXML, 0)
    if err != nil {
        return nil, err
    }
    return dom, nil
}

func modifyVMMemory(dom *libvirt.Domain, newMemory uint64) error {
    err := dom.SetMaxMemory(newMemory)
    if err != nil {
        return err
    }
    fmt.Print("Modified VM")
    return nil
}

func deleteVM(dom *libvirt.Domain) error {
    err := dom.Destroy()
    if err != nil {
        return err
    }

    err = dom.Undefine()
    if err != nil {
        return err
    }

    fmt.Print("Deleted VM")

    return nil
}

ログイン後にコピー

解決策

スレッド セーフティとは、複数のスレッドが同時に同じ接続を使用するときに、コードがメモリ破損の問題に悩まされないことを意味します。

取得されるセマンティックな動作はまだ未定義です。

libvirt QEMU/KVM ドライバーは、クライアント アプリケーションと libvirtd (または virtqemud) デーモンの間で RPC レイヤーを使用します。したがって、最初に非決定性があり、ゴルーチンが最初に実行されます。 libvirt-go-module API が CGo 経由で C ibvirt.so ライブラリを呼び出すと、ネイティブ オペレーティング システム スレッドにロックされ、libvirt 上で実行されます。 .so は内部的に同期して、どちらが最初に RPC メッセージをネットワーク上に送信するかを決定します。 libvirtd デーモン プロセスにも多数のスレッドがあり、RPC メッセージは理論的には FIFO によって処理されますが、libvirtd の内部 API ロジックは引き続きロックを競合するため、通信時にQEMU を使用すると、対話時に不確実性がさらに高まります。基本的に、SetMaxMemoryDestroy API 呼び出しはどちらの順序でも実行できます。順序を保証する必要がある場合は、DestroySetMaxMemory

が完了した後にのみ呼び出されるように、アプリケーション内でそれらをシリアル化する必要があります。

最後に IIUC の Go コードは堅牢ではありません。これは、main() メソッドが 2 つの goroutine を生成しますが、どちらの完了も待機しないためです。ああ、Go プロセスは、どちらかの goroutine が完全に実行される前に終了する可能性が高くなります。おそらくこれが、VM がすでに存在しているというエラー メッセージが表示される理由です。プロセスが終了する前に deleteVM ゴルーチンが実行されることはありません。

以上が2 つのゴルーチンを実行して VM を変更し、同じインスタンス上で libvirt-go パッケージを使用して同じ VM を破棄するとどうなりますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:stackoverflow.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!