shmat函数用法

shmat 函数用于将共享内存段附加到进程的地址空间,是 System V 共享内存 API 的一部分。以下是其详细用法:


函数原型

1
2
3
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

参数说明

  1. shmid

    • 共享内存标识符,由 shmget() 函数创建或获取。
    • 示例:shmid = shmget(key, size, IPC_CREAT | 0666);
  2. shmaddr

    • 指定附加到进程地址空间的地址,通常设为 NULL,由系统自动选择合适地址。
    • 若指定非 NULL 地址,需确保对齐和可用性(不推荐手动指定)。
  3. shmflg

    • 标志位,常用选项:
      • SHM_RDONLY: 以只读方式附加共享内存。
      • 0: 默认读写权限。
    • 示例:shmflg = SHM_RDONLY;(仅读)或 shmflg = 0;(可读写)。

返回值

  • 成功:返回指向共享内存段的指针(char * 类型,可直接读写)。
  • 失败:返回 (void *) -1,错误信息可通过 errno 获取。

使用步骤

  1. 创建/获取共享内存段
    使用 shmget() 获取共享内存标识符:

    1
    2
    3
    key_t key = ftok("/path/to/file", 'A'); // 生成唯一键值
    int shmid = shmget(key, sizeof(data), IPC_CREAT | 0666);
    if (shmid == -1) { perror("shmget"); exit(1); }
  2. 附加共享内存到进程
    调用 shmat() 附加共享内存:

    1
    2
    char *shm_ptr = (char *)shmat(shmid, NULL, 0);
    if (shm_ptr == (void *)-1) { perror("shmat"); exit(1); }
  3. 读写操作
    通过返回的指针直接访问共享内存:

    1
    2
    sprintf(shm_ptr, "Hello, Shared Memory!");  // 写入数据
    printf("Read data: %s\n", shm_ptr); // 读取数据
  4. 分离共享内存
    使用 shmdt() 断开连接:

    1
    if (shmdt(shm_ptr) == -1) { perror("shmdt"); exit(1); }
  5. 删除共享内存(可选)
    当所有进程分离后,可通过 shmctl() 删除:

    1
    shmctl(shmid, IPC_RMID, NULL); // 标记为删除

完整示例

写入进程(writer.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
key_t key = ftok("shmfile", 65);
int shmid = shmget(key, 1024, IPC_CREAT | 0666);
char *str = (char *)shmat(shmid, NULL, 0);

strcpy(str, "Hello from Writer!");
printf("Writer sent: %s\n", str);

shmdt(str);
return 0;
}

读取进程(reader.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
key_t key = ftok("shmfile", 65);
int shmid = shmget(key, 1024, 0666 | IPC_CREAT);
char *str = (char *)shmat(shmid, NULL, SHM_RDONLY);

printf("Reader received: %s\n", str);
shmdt(str);
shmctl(shmid, IPC_RMID, NULL); // 删除共享内存
return 0;
}

注意事项

  1. 同步机制
    共享内存不提供进程间同步,需结合信号量或互斥锁避免竞态条件。

  2. 生命周期
    即使所有进程分离,共享内存仍存在,除非显式删除(shmctl(IPC_RMID))。

  3. 权限设置
    创建时需指定权限(如 0666),确保其他进程有访问权限。

  4. 指针安全
    避免在共享内存中存储绝对指针,不同进程的地址空间可能不同。

  5. 错误处理
    始终检查 shmat 返回值是否为 (void *)-1,并处理 errno


通过上述步骤,可高效利用 shmat 实现进程间大数据量通信。

/dev/zero设备作用及用法

/dev/zero 是类 Unix 系统(如 Linux、macOS)中的一个特殊设备文件,它提供无限的零字节流(ASCII 字符 \0,即二进制 0x00)。以下是它的核心作用及典型用法:


作用

  1. 生成连续的零字节流

    • 读取 /dev/zero 会返回无限个 0x00 字节。
    • 写入 /dev/zero 会静默丢弃所有数据(类似黑洞设备 /dev/null),但可用于扩展文件大小。
  2. 高效初始化内存或文件

    • 用于快速填充文件或内存区域为零,避免手动写入零字节。

常见用法

1. 创建用零填充的文件

通过 dd 命令生成一个指定大小的全零文件:

1
2
# 创建一个 1GB 的空白文件(例如用于磁盘测试或加密容器)
dd if=/dev/zero of=zero_file.bin bs=1M count=1024
  • if=/dev/zero: 输入源为零字节流。
  • of=zero_file.bin: 输出文件。
  • bs=1M: 每次读写块大小为 1MB。
  • count=1024: 复制 1024 个块(总大小 1GB)。

2. 初始化内存(编程场景)

在 C 语言中,使用 mmap 将内存映射到 /dev/zero,创建匿名内存区域(初始为零):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <sys/mman.h>
#include <fcntl.h>

int main() {
int fd = open("/dev/zero", O_RDWR); // 打开设备
void *mem = mmap(
NULL, // 由系统选择地址
4096, // 映射大小(1页)
PROT_READ | PROT_WRITE, // 可读写
MAP_PRIVATE, // 私有映射(修改不写回设备)
fd, // 文件描述符
0 // 偏移量
);
close(fd);

// 使用初始为零的内存区域
memset(mem, 0, 4096); // 实际可能已为零,此操作冗余

munmap(mem, 4096); // 解除映射
return 0;
}

3. 快速清空交换分区或内存

(需谨慎操作)通过填充 /dev/zero 到交换设备,强制系统释放交换空间:

1
2
3
4
5
# 清空交换分区(以 /dev/sdX 为例,操作前需卸载)
sudo swapoff /dev/sdX
dd if=/dev/zero of=/dev/sdX bs=1M
sudo mkswap /dev/sdX
sudo swapon /dev/sdX

4. 扩展文件大小

向文件追加零字节(例如为虚拟机磁盘扩容):

1
2
# 扩展 existing_file.bin 的大小(增加 100MB)
dd if=/dev/zero bs=1M count=100 >> existing_file.bin

/dev/null/dev/random 的区别

设备文件 读取行为 写入行为 典型用途
/dev/zero 返回无限零字节 (0x00) 静默丢弃数据 初始化内存/文件、填充空白区域
/dev/null 立即返回 EOF(无数据) 静默丢弃数据 丢弃输出、模拟空设备
/dev/random 返回高质量随机字节(可能阻塞) 静默丢弃数据 生成加密密钥、安全随机数
/dev/urandom 返回非阻塞的伪随机字节 静默丢弃数据 非加密场景的随机数生成

注意事项

  1. 权限问题

    • /dev/zero 通常对所有用户可读可写(权限为 crw-rw-rw-)。
  2. 性能优化

    • 使用 dd 时,增大 bs(块大小)参数可提升效率(例如 bs=1M 代替 bs=512)。
  3. 内存映射替代方案

    • 在 Linux 中,MAP_ANONYMOUS 标志可直接创建匿名内存映射(无需打开 /dev/zero):
      1
      void *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  4. 避免无限读取

    • 直接读取 /dev/zero 会无限输出零字节,需通过工具(如 dd)限制读取量。

典型场景示例

加密文件初始化

创建用零填充的加密容器(如 VeraCrypt):

1
2
dd if=/dev/zero of=encrypted_container.img bs=1G count=10
veracrypt -c encrypted_container.img

测试磁盘写入速度

测试磁盘写入性能(覆盖缓存):

1
2
# 使用零数据测试写入速度
dd if=/dev/zero of=/tmp/testfile bs=1G count=1 oflag=direct

通过合理利用 /dev/zero,可以高效完成文件初始化、内存分配等任务,尤其适合需要快速填充零字节的场景。