进程间通信ipc

以下是针对 **D-Bus、Socket、Binder(Android)、mmap 和钩子(Hook)** 的 Python IPC 实现代码示例及解析:

---

### 一、D-Bus(Linux 桌面 IPC)
D-Bus 是 Linux 桌面环境的核心 IPC 机制,用于进程间通信(如系统服务与应用的交互)。

#### 1. **Python 实现(使用 `pydbus` 库)**
```bash
pip install pydbus
```

#### 2. **服务端(发布 D-Bus 服务)**
```python
from pydbus import SessionBus
from gi.repository import GLib

class CalculatorService:
    dbus = """
    <node>
        <interface name='com.example.Calculator'>
            <method name='Add'>
                <arg type='i' name='a' direction='in'/>
                <arg type='i' name='b' direction='in'/>
                <arg type='i' name='result' direction='out'/>
            </method>
        </interface>
    </node>
    """

    def Add(self, a, b):
        return a + b

bus = SessionBus()
bus.publish("com.example.Calculator", CalculatorService())

loop = GLib.MainLoop()
loop.run()
```

#### 3. **客户端(调用 D-Bus 服务)**
```python
from pydbus import SessionBus

bus = SessionBus()
proxy = bus.get("com.example.Calculator")
result = proxy.Add(10, 20)
print(f"Result: {result}")  # 输出 30
```

#### **特点**
- 适用于 Linux 桌面环境(如 GNOME/KDE)
- 支持信号(Signal)、方法(Method)、属性(Property)等复杂交互

---

### 二、Socket(跨平台 IPC)
Socket 是最通用的 IPC 方式,支持跨平台通信。

#### 1. **TCP Socket 示例**
```python
# 服务端
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 12345))
s.listen(1)

conn, addr = s.accept()
data = conn.recv(1024)
print(f"Received: {data.decode()}")
conn.send(b"Hello from Server")
conn.close()

# 客户端
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 12345))
s.send(b"Hello from Client")
response = s.recv(1024)
print(f"Server Response: {response.decode()}")
s.close()
```

#### 2. **Unix Domain Socket(高性能本地通信)**
```python
# 服务端
import socket, os

sock_path = '/tmp/uds.sock'
if os.path.exists(sock_path):
    os.unlink(sock_path)

s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.bind(sock_path)
s.listen(1)
conn, _ = s.accept()

data = conn.recv(1024)
conn.send(b"Ack via Unix Socket")
conn.close()

# 客户端
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect('/tmp/uds.sock')
s.send(b"Hello via Unix Socket")
print(s.recv(1024).decode())
s.close()
```

---

### 三、Binder(Android 专用 IPC)
Binder 是 Android 特有的 IPC 机制,标准 Python 无法直接使用,但可通过以下方式实现:

#### 1. **通过 Android NDK + Python 绑定(复杂方案)**
需编写 C/C++ Binder 服务并通过 Python 的 `ctypes` 调用:
```python
# 假设已编译 libbinder.so
from ctypes import CDLL, c_char_p

lib = CDLL('./libbinder.so')
lib.init_binder_service.restype = c_char_p

# 调用 Binder 服务
response = lib.call_binder_method(b"get_data")
print(f"Binder Response: {response.decode()}")
```

#### 2. **替代方案(使用 Android 的 `aidl` 工具)**
- 定义 AIDL 接口文件(`IMyService.aidl`)
- 生成 Java 服务端/客户端代码
- 通过 `pyjnius` 调用 Java 代码:
```python
from jnius import autoclass

MyService = autoclass('com.example.MyService')
service = MyService()
result = service.getResult(10, 20)
print(f"Binder Result: {result}")
```

---

### 四、共享内存(mmap)
通过内存映射文件实现高速数据共享。

#### **Python 代码示例**
```python
# 进程 A(写入)
import mmap, os

shm_fd = os.open('/dev/shm/mmap_example', os.O_CREAT | os.O_RDWR)
os.ftruncate(shm_fd, 1024)  # 分配 1KB
shm = mmap.mmap(shm_fd, 1024, access=mmap.ACCESS_WRITE)
shm.write(b"Data from Process A")
shm.close()

# 进程 B(读取)
shm_fd = os.open('/dev/shm/mmap_example', os.O_RDONLY)
shm = mmap.mmap(shm_fd, 1024, access=mmap.ACCESS_READ)
data = shm.read(1024).strip(b'\x00')
print(f"Read: {data.decode()}")
shm.close()
os.unlink('/dev/shm/mmap_example')
```

#### **同步优化(添加信号量)**
```python
import posix_ipc  # pip install posix_ipc

# 创建信号量
sem = posix_ipc.Semaphore(
    "/mmap_semaphore",
    flags=posix_ipc.O_CREAT,
    initial_value=1
)

# 进程 A(写入前加锁)
sem.acquire()
shm.write(b"Atomic Data")
sem.release()

# 进程 B(读取前加锁)
sem.acquire()
data = shm.read(1024)
sem.release()
```

---

### 五、钩子(Hook)
通过拦截系统调用或函数实现 IPC 监控。

#### 1. **Python 函数钩子(装饰器)**
```python
def hook_function(original_func):
    def wrapper(*args, **kwargs):
        print(f"Hooked: {original_func.__name__}")
        return original_func(*args, **kwargs)
    return wrapper

# 示例:钩住 socket.send()
import socket

original_send = socket.socket.send
socket.socket.send = hook_function(original_send)

s = socket.socket()
s.connect(('localhost', 80))
s.send(b"GET / HTTP/1.1\r\n")  # 触发钩子输出
```

#### 2. **LD_PRELOAD 钩子(C 扩展)**
通过 C 库拦截系统调用:
```c
// hook.c
#include <stdio.h>
#include <dlfcn.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags) {
    printf("Hook: Data sent via socket %d\n", sockfd);
    ssize_t (*original_send)(int, const void*, size_t, int) = dlsym(RTLD_NEXT, "send");
    return original_send(sockfd, buf, len, flags);
}
```

编译并注入:
```bash
gcc -shared -fPIC -o libhook.so hook.c -ldl
LD_PRELOAD=./libhook.so python3 my_script.py
```

---

### 六、性能对比与选择建议

| 机制       | 适用场景                  | 性能  | 复杂度 | 跨平台性       |
|------------|---------------------------|-------|--------|----------------|
| **D-Bus**  | Linux 桌面服务通信        | 中    | 高     | Linux 专用     |
| **Socket** | 通用网络/本地通信         | 中高  | 低     | 全平台         |
| **Binder** | Android 系统级通信        | 高    | 极高   | Android 专用   |
| **mmap**   | 高速大数据共享            | 极高  | 中     | 类 Unix 系统   |
| **Hook**   | 监控/修改系统行为         | 依赖  | 极高   | 依赖钩子实现   |

#### **建议**
- 优先选择 **Unix Domain Socket**(本地通信)或 **mmap**(高性能共享内存)
- Android 环境使用 **Binder**(需结合 Java/NDK)
- 需要监控 IPC 行为时使用 **Hook**(调试或安全场景)

 

posted on 2025-05-07 19:11  吃草的青蛙  阅读(58)  评论(0)    收藏  举报

导航