我在试着拿到MacOS号的PID名单.librpoc.h包含此函数签名,但我找不到任何文档:

int proc_listpids(uint32_t type, uint32_t typeinfo, void *buffer, int buffersize) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

我在Swift分钟内try 了此示例代码:

let arr = UnsafeMutableRawPointer.allocate(byteCount: 5000 * MemoryLayout<Int32>.stride, alignment: MemoryLayout<Int32>.alignment)
let count = proc_listpids(UInt32(PROC_ALL_PIDS), 0, arr, Int32(5000 * MemoryLayout<Int32>.stride));
print("count \(count)")

let pointer = arr.assumingMemoryBound(to: Int32.self)
let pids = Array(UnsafeMutableBufferPointer(start:pointer, count:Int(count)))
let slice = pids[0..<Int(count)]
for i in 0..<Int(count) {
     print("index:\(i) pid:\(slice[i])")
}

起初,我认为count个是发现的PID的数量.但输出显示了更多元素:

count 2020
index:0 pid:7727
index:1 pid:7726
index:2 pid:7723
index:3 pid:7722
index:4 pid:7721
index:5 pid:7717
...
index:502 pid:94
index:503 pid:1
index:504 pid:0
index:505 pid:0
index:506 pid:0
...
index:2013 pid:0
index:2014 pid:0
index:2015 pid:0
index:2016 pid:0
index:2017 pid:0
index:2018 pid:0
index:2019 pid:0

因此,如何解释proc_listpids实际返回的数字是令人困惑的?

推荐答案

The issue

S返回结果将具有的总缓冲区大小in bytes.因此,结果20202020字节,这正好足以存储505Int32(当然,每个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)))
    }
}

以下是一些注意事项:

  1. 这两个都是大小合适的数组,这听起来很理想,但有一个问题
    • 如果新进程在第一次和第二次调用 proc_listpids,那么您的缓冲区将太小,无法容纳它.
      • 这两个调用通常相继发生得非常快(因此这不太可能),如果您的进程在两个调用之间被抢占和挂起,时间可能会更长,从而留下更多的变化.
    • 也许最好将proc_listpids的结果用于 初步猜测大小,然后再增加一些额外的考虑因素 可能弹出的任何新进程.
  2. 您可以使用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))
    })
}

C++相关问答推荐

这是一个合法的C Strdup函数吗?

struct 上的OpenMP缩减

具有交换链获取和命令缓冲区提交的同步-危险-读后写错误

在C++中使用函数指针的正确语法

我的程序在收到SIGUSR1信号以从PAUSE()继续程序时总是崩溃()

VS代码';S C/C++扩展称C23真关键字和假关键字未定义

X64:并发写入布尔数组

如何在下面的C代码中正确管理内存?

在创建动态泛型数组时,通过realloc对故障进行分段

将返回的char*设置为S在函数中定义的字符串文字可能会产生什么问题?

在C中访问数组中的特定值

在函数外部使用内联ASM时无法指定操作数

如果格式字符串的内存与printf的一个参数共享,会发生什么情况?

如何将大写/小写土耳其字母相互转换?

将数字的每一位数平方,并使用C将它们连接为一个数字(程序不能正确处理0)

当我在34mb的.mp4文件中使用FREAD时,我得到了一个分段错误,我如何解决它?

Makefile无法将代码刷新到ATmega328p

在哪里可以找到叮当返回码的含义?

GDB 跳过动态加载器代码

通过修改c中的合并排序对数组的偶数索引进行排序