The issue
S返回结果将具有的总缓冲区大小in bytes.因此,结果2020
是2020
字节,这正好足以存储505
个Int32
(当然,每个4字节),这意味着最后一个有效索引是504
.
Why does it return a size and not a count?
这只是我的猜测,但我怀疑这是因为它方便了API在C中的使用,在C中,这个结果可能会直接传递给malloc
.
如果它们返回计数而不是字节数,调用者将不得不自己转换它.由于许多libproc API可以返回几种不同 struct 中的一种,使用sizeof
与错误的 struct (对于您正在标记的特定类型的请求)可能会导致缓冲区太小(存在缓冲区溢出漏洞的风险)或过大(存在缓冲区下溢漏洞的风险).
Working examples
下面的代码片段显示了拨打proc_listpids
的正常呼叫:
func getAllPIDs1() -> [pid_t] {
let expectedSize = proc_listpids(UInt32(PROC_ALL_PIDS), 0, nil, 0)
guard expectedSize != -1 else {
fatalError("Something went wrong... but what exactly?")
}
let stride = Int32(MemoryLayout<pid_t>.stride)
let expectedCount = Int(expectedSize / stride)
return Array(unsafeUninitializedCapacity: expectedCount) { buffer, initializedCount in
let initializedSize = proc_listpids(UInt32(PROC_ALL_PIDS), 0, &buffer, Int32(initializedCount))
initializedCount = Int(initializedSize / stride)
}
}
如果您使用proc_listallpids
,事情就会简单一些,它只是本例(source)的一个方便的包装器:
func getAllPIDs2() -> [pid_t] {
let exptectedCount = proc_listallpids(nil, 0)
guard exptectedCount != -1 else {
fatalError("Something went wrong... but what exactly?")
}
let expectedSize = Int(exptectedCount) * MemoryLayout<pid_t>.stride
return Array(unsafeUninitializedCapacity: expectedSize) { buffer, initializedCount in
initializedCount = Int(proc_listallpids(&buffer, Int32(initializedCount)))
}
}
以下是一些注意事项:
- 这两个都是大小合适的数组,这听起来很理想,但有一个问题
- 如果新进程在第一次和第二次调用
proc_listpids
,那么您的缓冲区将太小,无法容纳它.
- 这两个调用通常相继发生得非常快(因此这不太可能),如果您的进程在两个调用之间被抢占和挂起,时间可能会更长,从而留下更多的变化.
- 也许最好将
proc_listpids
的结果用于
初步猜测大小,然后再增加一些额外的考虑因素
可能弹出的任何新进程.
- 您可以使用
Array.init(unsafeUninitializedCapacity:initializingWith:)
为SWIFT数组分配缓冲区,并直接对其进行初始化,而无需任何中间分配或副本.
- 当心,这个接口基本上跟
malloc
差不多,误用会导致
大问题.
Extra
如果您发现自己调用了许多不同的libproc API,我发现定义一个帮助器来抽象这段舞蹈的某些部分非常有用(例如这些计数计算、整数强制转换、断言等):
private func extractIntoArray<T>(
of _: T.Type,
expectedSize: Int32,
using body: (UnsafeMutablePointer<T>) throws -> Int32
) rethrows -> [T] {
let stride = Int32(MemoryLayout<T>.stride)
// Sanity check: this can catch a lot of cases where you provide the wrong
// expected structure for the particular "type" of call you're making.
assert(expectedSize.isMultiple(of: stride))
let expectedCount = Int(expectedSize / stride)
return try Array(unsafeUninitializedCapacity: expectedCount) { buffer, initializedCount in
let initializedSize = try body(buffer.baseAddress!)
assert(initializedSize.isMultiple(of: stride))
initializedCount = Int(initializedSize / stride)
}
}
struct LibProcError: Error {}
func getAllPIDs() throws -> [pid_t] {
let bufferSize = proc_listpids(UInt32(PROC_ALL_PIDS), 0, nil, 0)
guard bufferSize != -1 else {
throw LibProcError()
}
return try extractIntoArray(of: pid_t.self, expectedSize: bufferSize, using: { bufferP in
proc_listpids(UInt32(PROC_ALL_PIDS), 0, &buffer, Int32(initializedCount))
})
}