[英]Use the sysctlbyname function within a Linux Swift Package
我正在嘗試將 linux 支持添加到我的 swift 包庫中以獲取系統信息,但我不知道如何在 Swift 包中訪問 linux 上的sysctlbyname
函數。
對於所有檢測,該庫依賴於sysctlbyname
函數,該函數可以通過在 Apple 平台上導入Dariwn.sys.sysctl
輕松訪問,但是我找不到任何 Swift 方法在 linux 上訪問該函數,盡管您可以訪問通過在基本上任何 unix 平台上導入 sys/sysctl.h 在 C 中實現它。
所以我想知道如何在 linux 上的 Swift 庫中訪問該函數,以及是否可以在不使用 C 或其他非 Swift 東西的情況下訪問該函數,也是因為我想讓我的代碼與 Swift 游樂場兼容適用於蘋果系統的應用程序,它不支持具有 C 導入功能的 SPM 庫。
作為參考,我在這里留下了我的項目中負責與sysctlbyname
的代碼部分:
import Foundation
#if os(Linux)
import Glibc //? not sure about where i can find `sysctlbyname` in linux without using C headers
#else
import Darwin.sys.sysctl
#endif
///Generic protocol to allow easy fetching of values out of `sysctlbyname`
public protocol SysctlFetch{
static var namePrefix: String {get}
}
public extension SysctlFetch{
///Gets a `String` from the `sysctlbyname` function
static func getString(_ valueName: String) -> String?{
var size: size_t = 0
let name = namePrefix + valueName
var res = sysctlbyname(name, nil, &size, nil, 0)
if res != 0 {
return nil
}
var ret = [CChar].init(repeating: 0, count: size + 1)
res = sysctlbyname(name, &ret, &size, nil, 0)
return res == 0 ? String(cString: ret) : nil
}
///Gets an Integer value from the `sysctlbyname` function
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
var ret = T()
var size = MemoryLayout.size(ofValue: ret)
let res = sysctlbyname(namePrefix + valueName, &ret, &size, nil, 0)
return res == 0 ? ret : nil
}
///Gets a `Bool` value from the `sysctlbyname` function
static func getBool(_ valueName: String) -> Bool?{
guard let res: Int32 = getInteger(valueName) else{
return nil
}
return res == 1
}
}
還有一個如何在代碼中使用它的例子(詛咒它被用來檢索更多的東西):
///Kernel info
final class KernelInfo: SysctlFetch{
static var namePrefix: String{
#if os(Linux)
return "kernel."
#else
return "kern."
#endif
}
///The os kernel name
static var ostype: String?{
return Self.getString("ostype")
}
/* Other static vars here */
}
因此,可以將 C、C++ 或 Objective-C 目標添加到 Swift 包中,這樣就可以導入所需的系統頭文件,然后創建一些包裝函數,使 Swift 可以訪問所需的內容,但這會破壞 Swift Playgrounds 應用程序開發兼容性,因為它只支持 Swift 目標(一種可能的解決方法是將 C/C++ 目標放在單獨的 swift 包中,以便有條件地將其用作 linux 的依賴項,有關更多詳細信息,請參閱相關的 swift 包文檔)。
所以添加一個 C/C++ 目標可能已經解決了這個問題,但問題是在 Linux 內核版本 5.5 及更高版本中, sysctl
函數已被棄用,即使在較舊的內核上,它們也不適用於 Linux 支持的所有 cpu 架構,等等運行最近的內核或某些特定的非 x86 cpu 架構的計算機,這樣的 Swift 包將不會成功構建。
當前訪問sysctl
函數提供的信息的方法是直接從/proc/sys
目錄中的文件系統讀取它,它適用於所有支持的 cpu 架構,它是sysctl
命令行實用程序那個數據。
因此,只有在 linux 上,代碼必須像這樣修改,才能在所有平台上成功收集該數據:
import Foundation
#if os(Linux)
import Glibc //? not sure about where i can find `sysctlbyname` in linux without using C headers
#else
import Darwin.sys.sysctl
#endif
///Generic protocol to allow easy fetching of values out of `sysctlbyname`
public protocol SysctlFetch{
static var namePrefix: String {get}
}
public extension SysctlFetch{
#if !os(Linux)
///Gets a `String` from the `sysctlbyname` function
static func getString(_ valueName: String) -> String?{
var size: size_t = 0
let name = namePrefix + valueName
var res = sysctlbyname(name, nil, &size, nil, 0)
if res != 0 {
return nil
}
var ret = [CChar].init(repeating: 0, count: size + 1)
res = sysctlbyname(name, &ret, &size, nil, 0)
return res == 0 ? String(cString: ret) : nil
}
///Gets an Integer value from the `sysctlbyname` function
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
var ret = T()
var size = MemoryLayout.size(ofValue: ret)
let res = sysctlbyname(namePrefix + valueName, &ret, &size, nil, 0)
return res == 0 ? ret : nil
}
#else
///Gets a `String` from `/proc/sys`
static func getString(_ valueName: String) -> String?{
let path = "/proc/sys/" + (namePrefix + valueName).replacingOccurrences(of: ".", with: "/")
var contents = ""
do{
contents = try String(contentsOfFile: path)
}catch let err{
return nil
}
if contents.last == "\n"{
contents.removeLast()
}
return contents
}
///Gets an Integer value from `/proc/sys`
static func getInteger<T: FixedWidthInteger>(_ valueName: String) -> T?{
guard let str = getString(valueName) else { return nil }
return T(str)
}
#endif
///Gets a `Bool` value from the `sysctlbyname` function
static func getBool(_ valueName: String) -> Bool?{
guard let res: Int32 = getInteger(valueName) else{
return nil
}
return res == 1
}
}
所以最后我自己想通了,我希望這對任何必須做同樣事情的人有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.