UNIX下C语言编程与实践53-UNIX 共享内存控制:shmctl 函数与共享内存管理

在 UNIX 共享内存的生命周期中,shmctl 函数是当之无愧的"管理中枢"------它承担着共享内存的属性查询、属性修改与资源释放三大核心职责。本文将以 ipcshm 程序为核心实例,详细讲解 shmctl 函数的功能、参数与常用命令,深入解析 shmid_ds 结构与共享内存管理的关联,并结合实战场景说明共享内存管理的重要性及常见问题解决方案。

一、shmctl 函数:共享内存的控制核心

shmctl 函数的功能是"对共享内存执行控制操作",与消息队列的 msgctl、信号量的 semctl 类似,是共享内存生命周期管理的关键接口。

1.1 函数原型与参数解析

shmctl 函数定义在 <sys/shm.h> 头文件中,原型如下:

复制代码
#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

三个核心参数的作用 ipcshm 程序实例对应关系如下表所示:

参数名 数据类型 核心作用 实例中的取值
shmid int 共享内存的唯一标识 ID,由 shmget 函数返回,指定操作的目标共享内存 229377ipcshm 程序操作的共享内存 ID)
cmd int 控制命令,指定对共享内存执行的操作类型(如查询、修改、删除) IPC_STAT(查询属性)、IPC_SET(修改属性)、IPC_RMID(删除)
buf struct shmid_ds * 指向 shmid_ds 结构的指针,用于存储(IPC_STAT)或修改(IPC_SET)共享内存属性;IPC_RMID 命令时可设为 NULL &bufIPC_STAT/IPC_SET 时)、NULLIPC_RMID 时)

函数返回值:执行成功时返回 0;失败时返回 -1,并通过 errno 标识错误类型(如 EACCES 表示权限不足、EINVAL 表示 shmid 无效)。

1.2 与 shmid_ds 结构的关联

shmctl 函数通过 buf 参数与该结构交互: - IPC_STAT 命令:内核将共享内存的当前属性写入 buf 指向的 shmid_ds 结构; - IPC_SET 命令:内核从 buf 指向的 shmid_ds 结构中读取属性,更新共享内存的对应字段; - IPC_RMID 命令:无需 shmid_ds 结构,直接删除共享内存。

给出 shmid_ds 结构定义如下(核心字段与管理功能强相关):

复制代码
struct shmid_ds {
    struct ipc_perm shm_perm;    /* 共享内存的权限信息(所有者、组、访问权限) */
    int shm_segsz;               /* 共享内存的大小(单位:字节) */
    __kernel_time_t shm_atime;   /* 最后一次调用 shmat 的时间(附加映射时间) */
    __kernel_time_t shm_dtime;   /* 最后一次调用 shmdt 的时间(解除映射时间) */
    __kernel_time_t shm_ctime;   /* 最后一次修改属性的时间(如 IPC_SET 操作) */
    __kernel_ipc_pid_t shm_cpid; /* 创建共享内存的进程 PID */
    __kernel_ipc_pid_t shm_lpid; /* 最后一次操作共享内存的进程 PID */
    unsigned short shm_nattch;   /* 当前附加(映射)到该共享内存的进程数 */
    unsigned short shm_unused;   /* 兼容字段,未使用 */
    void *shm_unused2;           /* 兼容字段,未使用 */
    void *shm_unused3;           /* 兼容字段,未使用 */
};

二、shmctl 核心命令详解

ipcshm 程序完整演示了 shmctl 的三种核心命令------IPC_STAT(查询)、IPC_SET(修改)、IPC_RMID(删除),以下结合实例逐一讲解。

2.1 IPC_STAT:查询共享内存属性

功能 :将 shmid 对应的共享内存当前属性,读取到 buf 指向的 shmid_ds 结构中,供用户程序监控共享内存状态(如大小、附加进程数、权限等)。

(1)操作流程( ipcshm 程序的 'v' 操作)
  1. 定义 struct shmid_ds 变量,用于存储属性数据;
  2. 调用 shmctl(shmid, IPC_STAT, &buf),将共享内存属性读取到 buf 中;
  3. 解析 buf 的字段,提取所需信息(如 shm_segszshm_nattch)并展示。
(2)实例代码片段

ipcshm 程序的 StatShm 函数通过 IPC_STAT 查询属性,核心代码如下:

复制代码
int StatShm(int shmid){
    char resp[10];
    struct shmid_ds buf;
    memset(&buf, 0, sizeof(buf));
    memset(resp, 0, sizeof(resp));
    
    // 步骤 1:读取共享内存属性到 buf 中
    shmctl(shmid, IPC_STAT, &buf);
    
    // 步骤 2:解析字段并打印
    fprintf(stderr, "T ID KEY MODE OWNER GROUP NATTCH SEGSZ CPID LPID\n");
    fprintf(stderr, "m %6d %#6x %s %6d %6d %6d %10d %10d %10d\n", 
        shmid, buf.shm_perm.__key, 
        GetFileMode(buf.shm_perm.mode, resp), // 转换权限为可读格式(如 rw-rw-rw-)
        buf.shm_perm.uid, buf.shm_perm.gid, 
        buf.shm_nattch, buf.shm_segsz, 
        buf.shm_cpid, buf.shm_lpid);
    return 0;
}
(3)执行结果
复制代码
[bill@billstone Unix_study]$ ./ipcshm 229377 v 
T ID KEY MODE OWNER GROUP NATTCH SEGSZ CPID LPID 
m 229377 0x7d0 rw-rw-rw- 500 500 0 100 1796 0

输出字段与 shmid_ds 的映射关系: - KEY=0x7d0buf.shm_perm.__key; - MODE=rw-rw-rw-buf.shm_perm.mode=0666; - NATTCH=0buf.shm_nattch(无进程映射); - SEGSZ=100buf.shm_segsz(共享内存大小 100 字节); - CPID=1796buf.shm_cpid(创建进程 PID)。

2.2 IPC_SET:修改共享内存属性

功能 :根据 buf 指向的 shmid_ds 结构,修改共享内存的指定属性。仅允许修改以下字段 (内核限制,其他字段修改无效): - shm_perm.uid:共享内存所有者的用户 ID; - shm_perm.gid:共享内存所有者的组 ID; - shm_perm.mode:共享内存的访问权限(如从 0644 改为 0666); - shm_qbytes:无(共享内存无此字段,消息队列特有)。

(1)操作流程
  1. 先调用 shmctl(shmid, IPC_STAT, &buf),读取当前属性到 buf 中(避免覆盖未修改的字段);
  2. 修改 buf 中的允许修改字段(如 buf.shm_perm.mode);
  3. 调用 shmctl(shmid, IPC_SET, &buf),将修改后的属性写入内核。
(2)实例代码

以下代码演示如何通过 IPC_SET 修改共享内存的权限(从 0644 改为 0666),符合编程风格:

复制代码
#include <sys/shm.h>
#include <stdio.h>
#include <errno.h>

//错误检查宏
#define VerifyErr(a, b) \
    if (a) { fprintf(stderr, "%s failed. errno: %d\n", (b), errno); return 1; } \
    else { fprintf(stderr, "%s success.\n", (b)); }

int main() {
    int shmid = 229377; // 共享内存 ID(从 ipcs -m 中获取)
    struct shmid_ds buf;

    // 步骤 1:读取当前属性
    int ret = shmctl(shmid, IPC_STAT, &buf);
    VerifyErr(ret < 0, "shmctl (IPC_STAT before set)");
    fprintf(stderr, "修改前权限:0%o\n", buf.shm_perm.mode & 0777);

    // 步骤 2:修改权限字段(改为 0666)
    buf.shm_perm.mode = 0666;

    // 步骤 3:执行修改
    ret = shmctl(shmid, IPC_SET, &buf);
    VerifyErr(ret < 0, "shmctl (IPC_SET)");

    // 步骤 4:验证修改结果
    ret = shmctl(shmid, IPC_STAT, &buf);
    VerifyErr(ret < 0, "shmctl (IPC_STAT after set)");
    fprintf(stderr, "修改后权限:0%o\n", buf.shm_perm.mode & 0777);

    return 0;
}
(3)执行结果
复制代码
shmctl (IPC_STAT before set) success.
修改前权限:0644
shmctl (IPC_SET) success.
shmctl (IPC_STAT after set) success.
修改后权限:0666

2.3 IPC_RMID:删除共享内存

功能 :永久删除 shmid 对应的共享内存,释放其占用的内核资源(包括物理内存区域)。强调,此操作不可逆 ,且有特殊逻辑: - 若当前有进程映射该共享内存(shm_nattch > 0),内核不会立即删除,而是将其标记为"待删除"(dest 状态); - 当所有进程解除映射(shm_nattch = 0)后,内核自动释放内存资源。

(1)操作流程(ipcshm 程序的 'd' 操作)
  1. 通过 ipcs -m 确认 shmid 对应的共享内存存在;
  2. 调用 shmctl(shmid, IPC_RMID, NULL)(无需 shmid_ds 结构,buf 设为 NULL);
  3. 通过 ipcs -m 验证删除结果(若 shm_nattch=0,则共享内存消失)。
(2)实例代码片段

ipcshm 程序删除共享内存的核心代码:

复制代码
// 若参数为 'd',则删除共享内存
else if(strcmp(argv[2], "d") == 0){ 
    VerifyErr(shmctl(shmid, IPC_RMID, NULL) < 0, "Delete Shm"); 
}
(3)执行结果
复制代码
[bill@billstone Unix_study]$ ./ipcshm 229377 d 
Delete Shm success. 
# 删除后查询,共享内存已消失(因 shm_nattch=0)
[bill@billstone Unix_study]$ ipcs -m 
------ Shared Memory Segments -------- 
key        shmid      owner      perms      bytes      nattch     status

文中的资源泄漏警示 :若不调用 IPC_RMID 删除共享内存,即使创建者进程退出,内存仍会残留(ipcs -m 可见),长期积累会耗尽内核内存。ipcshm 程序的 'd' 操作正是为解决此问题而设计。

三、shmid_ds 结构与共享内存监控

文中 ipcshm 程序的核心功能是"查询共享内存属性",而这依赖于 shmid_ds 结构的关键字段。以下详细讲解与管理相关的字段,及如何通过这些字段监控共享内存状态。

3.1 核心字段与监控场景

字段名 数据类型 核心含义 监控与管理场景
shm_perm.mode unsigned short 共享内存的访问权限(如 0666 表示所有者、组、其他均有读写权限) 检查是否有进程无权限访问:若 mode=0600,其他用户调用 shmat 会失败(errno=EACCES),需通过 IPC_SET 开放权限
shm_segsz int 共享内存的大小(字节),由 shmgetsize 参数指定 确认内存大小是否满足需求:如文中 shm1.c 需要 10*1024 字节,若 shm_segsz=100,则会导致数据写入溢出,需重新创建更大的内存
shm_nattch unsigned short 当前映射该共享内存的进程数(附加数) 判断共享内存是否仍在使用:若 shm_nattch>0,删除(IPC_RMID)后会标记为 dest,需等待所有进程解除映射;若 shm_nattch=0,删除后立即释放资源
shm_cpid/shm_lpid __kernel_ipc_pid_t 创建进程 PID / 最后一次操作进程 PID 追踪共享内存的创建者与使用者:若 shm_lpid 对应的进程已退出,但 shm_nattch>0,可能存在映射未解除的问题,需通过 ps 查找残留进程
shm_atime/shm_dtime __kernel_time_t 最后附加映射时间 / 最后解除映射时间 判断共享内存的活跃程度:若 shm_atime 距今已久(如超过 7 天),且 shm_nattch=0,说明该内存已闲置,可安全删除以释放资源

3.2 监控程序实例

以下程序通过 IPC_STAT 读取 shmid_ds 字段,实现共享内存的状态监控,符合文中的编程风格:

复制代码
#include <sys/shm.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>

// 错误检查宏
#define VerifyErr(a, b) \
    if (a) { fprintf(stderr, "%s failed. errno: %d\n", (b), errno); return 1; }

// 时间戳转换为可读格式
void timestamp_to_str(time_t ts, char *str, size_t len) {
    struct tm *tm = localtime(&ts);
    strftime(str, len, "%Y-%m-%d %H:%M:%S", tm);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <shmid>\n", argv[0]);
        return 1;
    }
    int shmid = atoi(argv[1]);
    struct shmid_ds buf;
    char time_str[32];

    // 读取 shmid_ds 结构
    VerifyErr(shmctl(shmid, IPC_STAT, &buf) < 0, "shmctl (IPC_STAT)");

    // 打印监控信息(类似文档 ipcshm 程序输出)
    fprintf(stderr, "=== 共享内存监控信息 ===\n");
    fprintf(stderr, "共享内存 ID: %d\n", shmid);
    fprintf(stderr, "键值: 0x%x\n", buf.shm_perm.__key);
    fprintf(stderr, "访问权限: 0%o\n", buf.shm_perm.mode & 0777);
    fprintf(stderr, "所有者 UID: %d\n", buf.shm_perm.uid);
    fprintf(stderr, "所有者 GID: %d\n", buf.shm_perm.gid);
    fprintf(stderr, "内存大小: %d 字节\n", buf.shm_segsz);
    fprintf(stderr, "当前附加进程数: %d\n", buf.shm_nattch);
    fprintf(stderr, "创建进程 PID: %d\n", buf.shm_cpid);
    fprintf(stderr, "最后操作进程 PID: %d\n", buf.shm_lpid);
    
    // 时间转换与打印
    timestamp_to_str(buf.shm_atime, time_str, sizeof(time_str));
    fprintf(stderr, "最后附加时间: %s\n", buf.shm_atime == 0 ? "未附加过" : time_str);
    timestamp_to_str(buf.shm_dtime, time_str, sizeof(time_str));
    fprintf(stderr, "最后解除映射时间: %s\n", buf.shm_dtime == 0 ? "未解除过" : time_str);
    timestamp_to_str(buf.shm_ctime, time_str, sizeof(time_str));
    fprintf(stderr, "最后属性修改时间: %s\n", time_str);

    return 0;
}

执行结果(监控 shmid=229377 的共享内存):

复制代码
[bill@billstone Unix_study]$ ./shm_monitor 229377 
=== 共享内存监控信息 ===
共享内存 ID: 229377
键值: 0x7d0
访问权限: 0666
所有者 UID: 1000
所有者 GID: 1000
内存大小: 100 字节
当前附加进程数: 0
创建进程 PID: 1796
最后操作进程 PID: 0
最后附加时间: 未附加过
最后解除映射时间: 未解除过
最后属性修改时间: 2024-05-22 16:30:45

四、共享内存管理的重要性

结合实例与 UNIX 内核特性,共享内存管理的重要性主要体现在以下三个方面,直接影响系统稳定性与资源利用率。

4.1 避免内核资源泄漏

共享内存是内核分配的物理内存,若不通过 shmctl(IPC_RMID)ipcrm 显式删除,即使创建者进程退出,内存仍会残留(ipcs -m 可见)。文中 ipcshm 程序的 'd' 操作正是为解决此问题而设计------若长期不删除,会导致: - 内核内存被耗尽,新的 shmget 调用失败(errno=ENOMEM); - 系统性能下降,因内核需管理大量闲置共享内存。

管理建议 : 1. 程序中:在共享内存不再使用时(如数据传输完成),调用 shmctl(shmid, IPC_RMID, NULL) 删除; 2. 运维中:定期执行 ipcs -m | awk '$6==0 {print $2}' | xargs ipcrm -m,删除无进程映射(nattch=0)的共享内存。

4.2 确保数据访问安全

共享内存的访问权限(shm_perm.mode)控制着进程能否映射和读写内存。若权限设置不当(如文中设为 0666,所有用户均可读写),可能导致: - 未授权进程修改内存数据,引发数据错乱(如进程 A 写入的配置被进程 B 篡改); - 敏感数据泄露(如内存中存储的密码被其他用户读取)。

管理建议 : 1. 通过 IPC_STAT 定期检查权限,确保符合最小权限原则(如仅允许所有者读写,设为 0600); 2. 若需多进程访问,通过 IPC_SET 修改 shm_perm.gid,将相关进程加入同一组,权限设为 0660。

4.3 避免进程阻塞与死锁

共享内存的附加进程数(shm_nattch)反映了内存的使用状态: - 若 shm_nattch>0,删除(IPC_RMID)后内存会被标记为 dest,新进程无法映射,但已映射的进程仍可读写; - 若某进程依赖该内存但无法映射(因已被标记删除),会导致进程阻塞或功能异常。

管理建议 : 1. 删除前通过 IPC_STAT 检查 shm_nattch,若大于 0,先通过 ps -ef | grep <lpid> 查找使用进程,通知其解除映射(shmdt); 2. 程序中通过信号处理函数(如捕获 SIGTERM),在进程退出前调用 shmdt,避免 shm_nattch 异常累积。

五、常见错误与解决方案

结合文中的实例与实战经验,整理 shmctl 函数使用过程中常见的错误场景,分析原因并给出解决方案(部分思路参考文中隐含的错误处理逻辑)。

  • 错误 1:shmctl 执行 IPC_STAT 失败,errno = EINVAL

    原因: - shmid 无效(如已被删除,或不存在该 ID 的共享内存); - buf 指针为 NULLIPC_STAT 命令需 buf 存储属性,不能为 NULL);

    解决方案: 1. 通过 ipcs -m 确认 shmid 存在且状态正常(无 dest 标记); 2. 确保 buf 指向有效的 struct shmid_ds 变量(如 struct shmid_ds buf; shmctl(shmid, IPC_STAT, &buf);),而非 NULL

  • 错误 2:shmctl 执行 IPC_SET 失败,errno = EPERM

    原因: - 当前进程无权限修改属性(如非共享内存的所有者或 root 用户,却尝试修改 shm_perm.uid); - 试图修改不允许的字段(如 shm_segszshm_cpid,这些字段仅内核可修改);

    解决方案: 1. 通过 ipcs -m -i shmid 查看所有者(owner),切换到所有者用户或使用 root 权限执行; 2. 仅修改允许的字段(shm_perm.uidshm_perm.gidshm_perm.mode),避免修改 shm_segsz 等只读字段。

  • 错误 3:shmctl 执行 IPC_RMID 失败,errno = EACCES

    原因:当前进程无权限删除共享内存(如仅拥有读权限,却尝试执行删除操作);

    解决方案: 1. 通过 IPC_STAT 检查 shm_perm.mode,确保当前进程有写权限(如所有者权限为 0600,组和其他无权限); 2. 若为所有者,直接执行删除;若为其他用户,联系所有者或使用 root 权限。

  • 错误 4:shmid_ds 结构使用不当,导致属性读取或修改异常

    原因: - 未初始化 shmid_ds 结构(如内存中有随机值,修改时覆盖了未预期的字段); - 执行 IPC_SET 前未读取当前属性(IPC_STAT),导致未修改的字段被设为默认值(如 shm_atime 被设为 0);

    解决方案: 1. 使用 memset(&buf, 0, sizeof(buf)) 初始化 shmid_ds 结构(文中 StatShm 函数的做法); 2. 执行 IPC_SET 前必须先调用 IPC_STAT,确保 buf 中的非修改字段与内核一致。

六、拓展:共享内存与信号量的协同使用

文中虽未直接关联,但共享内存的高效性需配合同步机制(如信号量)才能发挥最大价值------共享内存负责数据传输,信号量负责控制进程读写顺序,避免并发冲突。以下结合文中的生产者-消费者模型,演示如何实现高效数据同步。

6.1 协同原理

模型设计(sema.c/semb.cshm1.c/shm2.c 的结合): - 共享内存 :存储产品数据(如字符数组),大小设为 5*1024(最多存储 5 个产品); - 信号量集合 :含 2 个信号量: 1. 信号量 0(sem_num=0):空闲资源数,初始值 5(最多容纳 5 个产品); 2. 信号量 1(sem_num=1):产品资源数,初始值 0(初始无产品); - 生产者流程 :P(信号量 0) → 写入数据到共享内存 → V(信号量 1); - 消费者流程:P(信号量 1) → 从共享内存读取数据 → V(信号量 0)。

6.2 文协同实例代码

复制代码
#include <sys/shm.h>
#include <sys/sem.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

// 错误检查宏
#define VerifyErr(a, b) \
    if (a) { fprintf(stderr, "%s failed. errno: %d\n", (b), errno); exit(1); }

// 信号量操作结构
struct sembuf sem_op_p = {0, -1, SEM_UNDO}; // P 操作:空闲资源数减 1
struct sembuf sem_op_v = {1, 1, SEM_UNDO};  // V 操作:产品资源数加 1

int main() {
    key_t shm_key = 0x1234, sem_key = 0x5678;
    int shmid, semid;
    char *shm_ptr;

    // 1. 创建共享内存(大小 5*1024,权限 0666)
    shmid = shmget(shm_key, 5*1024, IPC_CREAT | 0666);
    VerifyErr(shmid < 0, "shmget");

    // 2. 创建信号量集合(2 个信号量,权限 0666)
    semid = semget(sem_key, 2, IPC_CREAT | 0666);
    VerifyErr(semid < 0, "semget");

    // 3. 初始化信号量(信号量 0=5,信号量 1=0)
    union semun sem_arg;
    sem_arg.val = 5;
    VerifyErr(semctl(semid, 0, SETVAL, sem_arg) < 0, "semctl (SETVAL 0)");
    sem_arg.val = 0;
    VerifyErr(semctl(semid, 1, SETVAL, sem_arg) < 0, "semctl (SETVAL 1)");

    // 4. 映射共享内存
    shm_ptr = (char *)shmat(shmid, 0, 0);
    VerifyErr(shm_ptr == (void *)-1, "shmat");

    // 5. 生产者操作:P(0) → 写数据 → V(1)
    semop(semid, &sem_op_p, 1); // P 操作:申请空闲资源
    strcpy(shm_ptr, "This is a product!"); // 写入产品数据
    fprintf(stderr, "生产者写入数据:%s\n", shm_ptr);
    semop(semid, &sem_op_v, 1); // V 操作:释放产品资源

    // 6. 解除映射(消费者进程会读取数据)
    VerifyErr(shmdt(shm_ptr) < 0, "shmdt");

    return 0;
}

协同优势: - 共享内存保证数据传输的高效性(无拷贝); - 信号量保证数据访问的安全性(避免生产者写满内存、消费者读取空内存); - 符合文中"高效 IPC + 同步机制"的设计思想,是高并发场景的经典搭配。

七、总结

本文对 shmctl 函数与共享内存管理的核心知识点进行了梳理,可总结为以下关键点:

  • shmctl 函数的定位 :作为共享内存的"管理中枢",通过 IPC_STATIPC_SETIPC_RMID 三种命令,实现属性查询、属性修改与资源删除,是共享内存生命周期管理的关键接口;
  • shmid_ds 结构的作用 :存储共享内存的所有属性,是 shmctl 命令的"数据载体",通过其字段(如 shm_nattchshm_perm.mode)可监控内存状态,为管理决策提供依据;
  • 管理的核心目标:避免内核资源泄漏(及时删除闲置内存)、确保数据访问安全(合理设置权限)、避免进程异常(监控附加进程数),这些是保障系统稳定性的关键;
  • 拓展应用:共享内存需配合同步机制(如信号量)才能实现安全高效的数据传输,文中的生产者-消费者模型是典型案例,体现了"数据传输 + 同步控制"的协同设计思想。

掌握 shmctl 函数的使用,不仅能应对文中的 ipcshm 程序等基础场景,更能在复杂系统(如数据库、高并发服务器)中实现共享内存的精细化管理。在实际开发中,需结合 shmget(创建)、shmat(映射)、shmdt(解除映射)与 shmctl(控制)四个函数,形成完整的共享内存操作流程,同时注重错误处理与资源监控,确保程序的稳定性与高效性。

相关推荐
黑马金牌编程3 小时前
Linux 服务器常见的性能调优
linux·运维·服务器·性能优化
liuyao_xianhui4 小时前
Linux_基本指令1
linux·运维·服务器
逆小舟5 小时前
【C/C++】指针
c语言·c++·笔记·学习
earthzhang20215 小时前
【1007】计算(a+b)×c的值
c语言·开发语言·数据结构·算法·青少年编程
liliangcsdn5 小时前
LLM时代基于unstructured解析非结构化pdf
linux·服务器·数据分析
迎風吹頭髮6 小时前
UNIX下C语言编程与实践63-UNIX 并发 Socket 编程:非阻塞套接字与轮询模型
java·c语言·unix
爱吃喵的鲤鱼6 小时前
仿mudou——Connection模块(连接管理)
linux·运维·服务器·开发语言·网络·c++
郝学胜-神的一滴6 小时前
使用Linux的read和write系统函数操作文件
linux·服务器·开发语言·数据库·c++·程序人生·软件工程
奔跑吧邓邓子7 小时前
【C语言实战(6)】解锁C语言循环密码:for循环实战探秘
c语言·实战·for循环