在Go中实现硬件检测和加载Linux内核模块,可以通过os/exec包执行系统命令,或者使用syscall包直接调用系统调用。以下是一个完整的示例,展示如何检测和加载所需的内核模块,并处理网络和硬盘相关的模块。
方法1:使用os/exec执行modprobe命令
这种方法直接调用系统的modprobe命令,类似于原始bash脚本的功能。
package main
import (
"fmt"
"os/exec"
"strings"
)
func loadKernelModules() error {
// 获取所有设备的MODALIAS
cmd := exec.Command("grep", "-h", "MODALIAS", "/sys/bus/*/devices/*/uevent")
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("failed to grep MODALIAS: %v", err)
}
// 提取MODALIAS值
aliases := strings.Split(strings.TrimSpace(string(output)), "\n")
for _, alias := range aliases {
parts := strings.Split(alias, "=")
if len(parts) < 2 {
continue
}
modalias := parts[1]
// 使用modprobe加载模块
modprobeCmd := exec.Command("modprobe", "-abq", modalias)
if err := modprobeCmd.Run(); err != nil {
// 忽略错误,因为某些模块可能已加载或不存在
fmt.Printf("modprobe failed for %s: %v\n", modalias, err)
} else {
fmt.Printf("Loaded module for alias: %s\n", modalias)
}
}
return nil
}
func main() {
if err := loadKernelModules(); err != nil {
fmt.Printf("Error: %v\n", err)
}
}
方法2:手动解析设备并加载特定模块
针对网卡和硬盘驱动,可以更精确地检测设备并加载对应的模块。以下示例展示如何检测网络接口和块设备,并加载相应的内核模块。
package main
import (
"fmt"
"io/ioutil"
"os/exec"
"path/filepath"
"strings"
)
// 检测网络接口并加载驱动
func loadNetworkDrivers() error {
netPath := "/sys/class/net"
devices, err := ioutil.ReadDir(netPath)
if err != nil {
return fmt.Errorf("failed to read network devices: %v", err)
}
for _, device := range devices {
devicePath := filepath.Join(netPath, device.Name(), "device", "modalias")
modalias, err := ioutil.ReadFile(devicePath)
if err != nil {
continue // 忽略无法读取modalias的设备
}
alias := strings.TrimSpace(string(modalias))
if alias != "" {
cmd := exec.Command("modprobe", "-abq", alias)
if err := cmd.Run(); err != nil {
fmt.Printf("Failed to load module for %s: %v\n", device.Name(), err)
} else {
fmt.Printf("Loaded network driver for %s\n", device.Name())
}
}
}
return nil
}
// 检测硬盘设备并加载驱动
func loadStorageDrivers() error {
blockPath := "/sys/block"
devices, err := ioutil.ReadDir(blockPath)
if err != nil {
return fmt.Errorf("failed to read block devices: %v", err)
}
for _, device := range devices {
// 跳过虚拟设备(如loop、ram)
if strings.HasPrefix(device.Name(), "loop") || strings.HasPrefix(device.Name(), "ram") {
continue
}
devicePath := filepath.Join(blockPath, device.Name(), "device", "modalias")
modalias, err := ioutil.ReadFile(devicePath)
if err != nil {
continue
}
alias := strings.TrimSpace(string(modalias))
if alias != "" {
cmd := exec.Command("modprobe", "-abq", alias)
if err := cmd.Run(); err != nil {
fmt.Printf("Failed to load module for %s: %v\n", device.Name(), err)
} else {
fmt.Printf("Loaded storage driver for %s\n", device.Name())
}
}
}
return nil
}
func main() {
if err := loadNetworkDrivers(); err != nil {
fmt.Printf("Network driver error: %v\n", err)
}
if err := loadStorageDrivers(); err != nil {
fmt.Printf("Storage driver error: %v\n", err)
}
}
说明:
- 方法1 复制了原始bash脚本的行为,通过
grep和modprobe加载所有检测到的模块。
- 方法2 更针对性地处理网络和存储设备,通过读取
/sys/class/net和/sys/block目录下的modalias文件,并使用modprobe加载对应模块。
- 错误处理被简化,某些模块加载失败是正常的(例如模块已加载或不存在)。
- 需要以root权限运行这些代码,因为
modprobe需要特权。
这些示例可以直接编译运行在Linux系统上,用于硬件初始化和模块加载。