inurl 网站建设/南通企业网站制作

inurl 网站建设,南通企业网站制作,模板网站的弊端,网站克隆下来了然后再怎么做一、按通信范围分类 同一主机进程通信 传统IPC方式: 管道(无名管道、有名管道)信号(Signal) System V IPC: 共享内存(效率最高)消息队列信号量 POSIX IPC(较新标准&#…

一、按通信范围分类

  1. 同一主机进程通信

    • 传统IPC方式:
      • 管道(无名管道、有名管道)
      • 信号(Signal)
    • System V IPC:
      • 共享内存(效率最高)
      • 消息队列
      • 信号量
    • POSIX IPC(较新标准):
      • POSIX消息队列
      • POSIX信号量
      • POSIX共享内存
  2. 跨主机进程通信

    • Socket网络通信

二、通信原理

  • 进程空间独立,必须通过内核中转
  • 内核在内核空间建立通信机制,用户空间通过系统调用访问

三、管道通信

1.无名管道(Pipe)

特性:

  • 半双工通信(数据单向流动)
  • 仅用于亲缘关系进程(父子/兄弟进程)
  • 数据遵循FIFO原则
  • 最大容量64KB(可通过fcntl(fd, F_GETPIPE_SZ)查询)

解释:

    双工     ---发送和接收可以同时进行 --手机,电话
    半双工   ---发送端 和 接收端  同一个时刻之后有一个起效 ---对讲机 
    单工     ---发送端 接收端固定 --- 广播 

(一)函数原型

#include <unistd.h>
int pipe(int pipefd[2]);

(二)参数说明

  • pipefd[2]:输出参数,用于接收两个文件描述符的数组
    • pipefd[0]:管道的读端(从该描述符读取数据)
    • pipefd[1]:管道的写端(向该描述符写入数据)

(三)返回值

  • 成功返回 0
  • 失败返回 -1,并设置 errno
    • EMFILE:进程打开的文件描述符过多
    • ENFILE:系统文件表已满
    • EFAULT:非法地址空间

(四)基础用法示例

#include<stdio.h>
#include<unistd.h>
#include <sys/wait.h>
#include<string.h>
#include <stdlib.h>int main(int argc, const char *argv[])
{int fd[2];int fd1[2];int ret = pipe(fd);ret = pipe(fd1);char buf[20] = {0};char buf1[1024] = {0};if(ret < 0){perror("pipe fail");return -1;}pid_t pid = fork();if(pid < 0){perror("fork fail");return -1;}if(pid > 0){close(fd[0]);close(fd1[1]);while(1){printf("f> ");fgets(buf,sizeof(buf),stdin);write(fd[1],buf,strlen(buf)+1);//加1是要保证输入的是字符串if(strncmp(buf,"quit",4) == 0){wait(NULL);exit(EXIT_SUCCESS);}read(fd1[0],buf1,sizeof(buf));printf("buf1 = %s\n",buf1);}}else if(pid == 0){close(fd[1]);close(fd1[0]);while(1){read(fd[0],buf,sizeof(buf));if(strncmp(buf,"quit",4) == 0){printf("child exit....\n");exit(EXIT_SUCCESS);}printf("buf = %s\n",buf);sprintf(buf1,"child %s",buf);write(fd1[1],buf1,strlen(buf1)+1);}}return 0;
}

(五)关键特性说明

1. 数据流向
  • 单向流动:数据从写端(pipefd[1])流向读端(pipefd[0]
  • 半双工:同一时刻只能有一个方向的数据流
2. 原子性保证
  • 当写入数据量 ≤ PIPE_BUF(POSIX 要求 ≥ 512字节)时,保证写入操作的原子性
  • 可通过命令查看具体值:
    cat /proc/sys/fs/pipe-max-size  # 最大容量(默认 1MB)
    ulimit -a                       # 查看 PIPE_BUF 值(通常 4096)
    
3. 阻塞行为
场景读端行为写端行为
管道空 & 写端开放阻塞等待数据-
管道满 & 读端开放-阻塞直到有空间
所有读端关闭-触发 SIGPIPE 信号(默认终止进程)
所有写端关闭read 返回 0(EOF)-

四、mkfifo

(一)函数原型

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

(二)参数说明

  • pathname:管道文件的路径(建议使用绝对路径)
  • mode:文件权限(实际权限受 umask 影响,建议搭配 umask(0) 使用)

(三)返回值

  • 成功返回 0
  • 失败返回 -1,并设置 errno
    • EEXIST:文件已存在
    • ENOENT:路径不存在
    • EACCES:权限不足
    • ENOSPC:磁盘空间不足


(四)基础用法示例

写入端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
//./a.out fifo_A2B fifo_B2A
int main(int argc, const char *argv[])
{if (argc != 3){printf("Usage: %s <fifo_A2B> <fifo_B2A>\n",argv[0]);return -1;}if(mkfifo(argv[1],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}if(mkfifo(argv[2],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}int fd1 = open(argv[1],O_WRONLY);int fd2 = open(argv[2],O_RDONLY);if (fd1 < 0 || fd2 < 0){perror("open fail");return -1;}pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}char buf[1024];if (pid > 0){close(fd2);while (1){printf("> ");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = '\0';write(fd1,buf,strlen(buf)+1);if (strncmp(buf,"quit",4) == 0){wait(NULL);printf("father exit......\n");exit(0);}}}else if (pid == 0){close(fd1);while (1){printf("c> ");read(fd2,buf,sizeof(buf));printf("%s \n",buf);if (strncmp(buf,"quit",4) == 0){printf("child exit......\n");exit(0);}}}return 0;
}
读取端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>//./a.out fifo_A2B fifo_B2A
int main(int argc, const char *argv[])
{if (argc != 3){printf("Usage: %s <fifo_A2B> <fifo_B2A>\n",argv[0]);return -1;}if(mkfifo(argv[1],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}if(mkfifo(argv[2],0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}int fd1 = open(argv[1],O_RDONLY);int fd2 = open(argv[2],O_WRONLY);if (fd1 < 0 || fd2 < 0){perror("open fail");return -1;}pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}char buf[1024];if (pid > 0){close(fd1);while (1){printf("> ");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = '\0';write(fd2,buf,strlen(buf)+1);if (strncmp(buf,"quit",4) == 0){wait(NULL);printf("father exit......\n");exit(0);}}}else if (pid == 0){close(fd2);while (1){printf("c> ");read(fd1,buf,sizeof(buf));printf("%s \n",buf);if (strncmp(buf,"quit",4) == 0){printf("child exit......\n");exit(0);}}}return 0;
}

(五)打开模式与阻塞关系表

打开方式读端行为写端行为是否推荐使用
O_RDONLY阻塞直到有写端打开-✅ 推荐
O_WRONLY-阻塞直到有读端打开✅ 推荐
O_RDWR立即返回(破坏FIFO语义)立即返回(破坏FIFO语义)❌ 禁止使用
`O_RDONLYO_NONBLOCK`立即返回(无数据返回0,需检查errno)-
`O_WRONLYO_NONBLOCK`-立即返回(无读端时返回ENXIO错误)

五、卸载管道unlink

(一)函数原型

#include <unistd.h>
int unlink(const char *pathname);  // 正确拼写为 pathname

(二)功能说明

  1. 核心作用

    • 删除文件系统中的一个目录项
    • 当文件引用计数归零时释放磁盘空间
  2. 对FIFO的特殊行为

    • 立即删除文件系统入口(文件不再可见)
    • 已打开的管道描述符仍可继续使用
    • 实际文件资源在所有进程关闭描述符后释放

(三)参数说明

参数说明
pathname要删除的有名管道完整路径
示例:"/tmp/my_fifo"


(四)返回值

返回值说明
0删除成功
-1删除失败,可通过 errno 获取错误原因

(五)错误处理

errno 值触发场景处理方法
EACCES权限不足(文件/目录不可写)检查文件权限或使用 sudo
ENOENT文件不存在先检查文件是否存在
EISDIR路径是目录改用 rmdir 删除目录
EBUSY文件正在被使用(某些系统)关闭所有进程的文件描述符

(六)使用示例

1. 基础用法
const char *fifo_path = "/tmp/my_fifo";// 创建并删除管道
if (mkfifo(fifo_path, 0666) == -1) {perror("mkfifo error");exit(EXIT_FAILURE);
}// 使用管道...// 删除管道文件
if (unlink(fifo_path) == -1) {perror("unlink error");exit(EXIT_FAILURE);
}
2. 安全删除模式
#include <stdlib.h>
#include <signal.h>// 注册退出清理函数
void cleanup() {if (unlink("/tmp/my_fifo") == -1 && errno != ENOENT) {perror("cleanup error");}
}int main() {atexit(cleanup);  // 正常退出时调用signal(SIGTERM, cleanup);  // 捕获终止信号signal(SIGINT, cleanup);   // 捕获Ctrl+C// ...其他代码...
}


(七)重要注意事项

  1. 延迟释放机制

    int fd = open("/tmp/fifo", O_RDONLY);
    unlink("/tmp/fifo");  // 立即删除文件系统入口
    read(fd, buf, size);  // 仍然可以正常读取数据
    close(fd);            // 此时真正释放资源
    
  2. 多进程场景

    • 建议由最后退出的进程执行删除
    • 可使用文件锁协调删除操作:
      flock(fd, LOCK_EX);
      unlink(path);
      flock(fd, LOCK_UN);
      
  3. 临时文件最佳实践

    // 创建临时管道(自动删除)
    char tmp_path[] = "/tmp/fifo_XXXXXX";
    mktemp(tmp_path);         // 生成唯一名称
    mkfifo(tmp_path, 0600);  // 严格权限
    unlink(tmp_path);         // 立即标记删除
    

(八)与 remove() 的区别

特性unlink()remove()
标准来源POSIX 系统调用C标准库函数
目录处理不能删除目录可删除空目录
封装实现原始系统调用内部调用 unlink/rmdir
错误返回通过 errno 获取通过返回值判断
推荐场景删除文件/FIFO跨平台文件删除

(九)开发建议

  1. 在程序启动时清理旧管道:

    if (access(fifo_path, F_OK) == 0) {unlink(fifo_path);
    }
    
  2. 使用绝对路径避免歧义:

    // 错误示例
    unlink("my_fifo");  // 可能误删其他目录文件// 正确示例
    unlink("/var/run/myapp_fifo");
    
  3. 监控文件状态:

    struct stat st;
    if (stat(fifo_path, &st) == 0) {if (S_ISFIFO(st.st_mode)) {// 确认是管道文件再删除unlink(fifo_path);}
    }
    

📌 关键点:unlink 只是删除文件链接,实际资源释放需要等待所有引用关闭。对于管道文件的清理,建议结合引用计数和进程生命周期管理来实现安全删除。

六、信号处理的注册函数

(一)信号本质

  • 软中断机制:操作系统内核向进程发送的异步通知
  • 中断模型对比
    类型中断源处理程序响应方式
    硬件中断硬件设备内核ISR立即处理
    信号软件事件用户定义处理函数异步延迟处理

(二)核心API - signal()

#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);

参数说明

参数类型说明
signumint要处理的信号编号(如 SIGINT)
handlersighandler_t信号处理函数指针

特殊 handler 值

作用
SIG_DFL恢复默认处理
SIG_IGN忽略该信号

返回值

  • 成功:返回之前的处理函数指针
  • 失败:返回 SIG_ERR((sighandler_t)-1)

(三)信号处理函数

1. 函数原型
void handler(int signo) {/* 信号处理逻辑 */
}

2. 重要特性

  • 参数 signo:接收到的信号编号(可通过 strsignal(signo) 获取信号名)
  • 执行环境:在进程的上下文随机时刻运行(异步执行)
  • 限制
    • 禁止使用不可重入函数(如 malloc、printf)
    • 应尽量保持处理逻辑简单

(四)关键信号列表

信号默认行为可否捕获说明
SIGHUP1Terminate终端挂断
SIGINT2Terminate键盘中断(Ctrl+C)
SIGQUIT3Core Dump键盘退出(Ctrl+\)
SIGILL4Core Dump非法指令
SIGABRT6Core Dumpabort() 产生
SIGFPE8Core Dump算术异常
SIGKILL9Terminate强制终止
SIGSEGV11Core Dump无效内存访问
SIGPIPE13Terminate管道破裂
SIGALRM14Terminate定时器超时
SIGTERM15Terminate终止信号
SIGCHLD17Ignore子进程状态改变
SIGSTOP19Stop Process暂停进程(Ctrl+Z)
SIGCONT18Continue继续执行被暂停的进程

(五)信号处理流程

  1. 信号产生:由内核、进程或终端产生
  2. 信号递送:被目标进程接收
  3. 处理响应
    • 执行默认操作
    • 执行自定义处理函数
    • 忽略信号(SIG_IGN)

(六)典型应用场景

  1. 进程控制:处理 Ctrl+C 终止请求
  2. 异常处理:捕获段错误进行日志记录
  3. 进程通信:通过 SIGUSR1/SIGUSR2 自定义协议
  4. 资源管理:处理子进程退出(SIGCHLD)
  5. 定时操作:使用 SIGALRM 实现超时机制

七、发送信号函数 kill()

(一)函数原型

#include <sys/types.h>
#include <signal.h>int kill(pid_t pid, int sig);

(二)参数详解

参数类型说明
pidpid_t目标进程标识符,支持多种特殊值
sigint要发送的信号编号(可通过 kill -l 查看信号列表)
pid 的取值规则:
作用范围
> 0发送给指定PID的进程
= 0发送给当前进程所在进程组的所有进程
= -1发送给所有有权限发送的进程(相当于广播)
< -1发送给进程组ID为 `
常用信号编号:
信号说明
SIGHUP1终端挂断
SIGINT2中断(Ctrl+C)
SIGQUIT3退出(Ctrl+\)
SIGKILL9强制终止(不可捕获)
SIGTERM15终止信号(默认)
SIGUSR110用户自定义信号1
SIGUSR212用户自定义信号2

(三)返回值

返回值说明
0成功发送信号
-1发送失败,可通过 errno 获取错误原因
常见错误码:
errno说明
EINVAL无效信号编号
EPERM权限不足(不能向目标进程发送信号)
ESRCH目标进程/进程组不存在
EFAULT非法内存访问

(四)使用示例

1. 终止指定进程
pid_t target_pid = 1234;if (kill(target_pid, SIGTERM) == -1) {if (errno == EPERM) {fprintf(stderr, "Permission denied to send signal\n");} else if (errno == ESRCH) {fprintf(stderr, "Process %d does not exist\n", target_pid);} else {perror("kill failed");}
}
2. 发送给进程组
// 发送SIGKILL给进程组5678的所有进程
if (kill(-5678, SIGKILL) == -1) {perror("Failed to kill process group");
}
3. 广播信号
// 向所有有权限的进程发送SIGUSR1(慎用!)
if (kill(-1, SIGUSR1) == -1) {perror("Broadcast failed");
}

(五)重要注意事项

  1. 权限控制

    • 普通用户只能向自己的进程发送信号
    • root 用户可以向所有进程发送信号
    • 发送 SIGKILL 需要额外权限
  2. 信号处理限制

    // SIGKILL 和 SIGSTOP 永远不能被捕获或忽略
    signal(SIGKILL, SIG_IGN);  // 此调用会失败
    
  3. 僵尸进程

    • 无法向僵尸进程(Zombie)发送信号
    • 发送信号给僵尸进程会返回 ESRCH 错误
  4. 原子性保证

    • 当向进程组发送信号时,保证所有目标进程都能收到信号后才返回

(六)扩展应用

1. 进程存在性检测
// 通过发送空信号(0)检测进程是否存在
int process_exists(pid_t pid) {if (kill(pid, 0) == 0) {return 1;  // 存在} else {if (errno == ESRCH) return 0;  // 不存在else return -1;  // 权限不足等其他错误}
}
2. 实现 kill 命令
// 类似命令行工具的实现
if (argc != 3) {fprintf(stderr, "Usage: %s <signal> <pid>\n", argv[0]);exit(EXIT_FAILURE);
}int sig = atoi(argv[1]);
pid_t pid = atoi(argv[2]);if (kill(pid, sig) == -1) {perror("kill error");exit(EXIT_FAILURE);
}

八、raise() 函数

1. 函数原型
#include <signal.h>
int raise(int sig);
2. 功能说明
  • 当前进程发送指定信号
  • 等价于 kill(getpid(), sig)
3. 返回值
返回值说明
0发送成功
非0发送失败(通常为无效信号)
4. 使用示例
// 触发段错误(用于调试)
raise(SIGSEGV);// 优雅退出
if (error_condition) {raise(SIGTERM);
}

九、alarm() 函数

1. 函数原型
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
2. 功能说明
  • 设置单次定时器,经过 seconds 秒后向进程发送 SIGALRM 信号
  • 特性
    • 每个进程只能有一个活跃的 alarm 定时器
    • 新调用会覆盖前一个定时器
    • 精度为秒级(实际误差可能达 ±1 秒)
3. 返回值
返回值说明
0首次设置定时器
剩余秒数覆盖已有定时器时的剩余时间
4. 使用示例

检测用户是否输入, 如果用户3s没有输入,   则超时一次, 如果超时3次自动退出

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>int cnt = 0;void do_signal(int signal)
{alarm(3);cnt++;printf("cnt = %d\n",cnt);if(cnt == 3){exit(0);}
}int main(int argc, const char *argv[])
{alarm(3);signal(SIGALRM,do_signal);char buf[1024];while(1){fgets(buf,sizeof(buf),stdin);printf("buf = %s\n",buf);alarm(3);cnt = 0;}return 0;
}

三、核心机制对比

特性raise()alarm()
信号源进程主动触发内核定时器触发
信号目标只能是当前进程当前进程
信号类型任意有效信号固定为 SIGALRM (14)
时间控制立即发送延迟发送(秒级精度)
主要应用场景异常处理/进程自终止超时控制/定时任务

十、pause() 函数详解

(一)函数原型

#include <unistd.h>
int pause(void);

(二)功能说明

  • 核心作用:使调用进程挂起,永久等待直到收到任意信号
  • 典型应用
    • 实现进程的事件驱动模型
    • 创建守护进程的主循环
    • 等待异步信号触发操作

(三)返回值

返回值说明
-1总是返回 -1(被信号中断后返回)
errno设置为 EINTR(被信号中断)

(四)使用示例

基础用法

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void handler(int sig) {printf("Received signal %d\n", sig);
}int main() {signal(SIGUSR1, handler);printf("PID=%d, waiting for signal...\n", getpid());pause();  // 阻塞直到收到信号printf("Resuming execution\n");return 0;
}

结合 alarm() 实现超时等待

void alarm_handler(int sig) {printf("Timeout!\n");
}int main() {signal(SIGALRM, alarm_handler);alarm(5);  // 设置5秒超时printf("Waiting for event (max 5s)...\n");pause();  // 可能被SIGALRM或目标信号中断if(errno == EINTR) {printf("Wait interrupted\n");}return 0;
}

十一、ftok() 函数详解

(一)函数原型

#include <sys/ipc.h>
#include <sys/types.h>key_t ftok(const char *pathname, int proj_id);

(二)功能说明

  • 核心作用:通过文件路径和项目ID生成唯一IPC键值
  • 典型应用:为共享内存、消息队列等System V IPC对象创建唯一标识符

(三)参数详解

参数类型说明
pathnameconst char*已存在的文件路径(建议选择稳定文件)
proj_idint项目标识符(仅使用低8位,范围0-255)

推荐参数选择

  • pathname
    • /etc/passwd(系统文件,稳定)
    • 应用程序专属配置文件
  • proj_id
    • 使用ASCII字符(如 'a' → 97)
    • 范围:0 ≤ proj_id ≤ 255

(四)返回值

返回值说明
有效key_t生成的IPC键值
(key_t)-1生成失败,检查errno:<br>• ENOENT(文件不存在)<br>• EACCES(无权限)

(五)工作原理

键值生成算法

key = (file_st.st_ino & 0xFFFF) | ((file_st.st_dev & 0xFF) << 16) | ((proj_id & 0xFF) << 24)
  • file_st.st_ino:文件inode号(取低16位)
  • file_st.st_dev:文件系统设备号(取低8位)
  • proj_id:用户指定值(取低8位)

键值冲突概率

  • 相同参数 → 相同键值
  • 不同文件 → 可能产生相同键值(概率低,但存在)

(六)使用示例

基础用法

#include <stdio.h>
#include <sys/ipc.h>int main() {const char *path = "/etc/passwd";int proj_id = 'A';  // ASCII 65key_t key = ftok(path, proj_id);if (key == (key_t)-1) {perror("ftok failed");return 1;}printf("Generated key: 0x%08x\n", (unsigned int)key);return 0;
}

错误处理

key_t key = ftok("/non/existent/file", 1);
if (key == (key_t)-1) {if (errno == ENOENT) {fprintf(stderr, "Path does not exist\n");} else if (errno == EACCES) {fprintf(stderr, "Permission denied\n");}exit(EXIT_FAILURE);
}

十二、共享内存创建/获取函数 shmget

(一)函数原型

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);

(二)核心功能

  • 创建新共享内存段:当 key 不存在且使用 IPC_CREAT
  • 获取已有共享内存:当 key 已存在
  • 独占创建检测:配合 IPC_EXCL 使用

(三)参数详解

参数类型说明
keykey_t共享内存键值(IPC_PRIVATE 或 ftok 生成)
sizesize_t共享内存大小(字节)
shmflgint权限标志与创建选项的组合

1. key 的取值规则

键值类型适用场景示例
IPC_PRIVATE亲缘关系进程间共享shmget(IPC_PRIVATE, ...)
自定义键值任意进程通过相同键值访问shmget(0x1234, ...)

2. shmflg 组合方式

标志说明
IPC_CREAT如果不存在则创建
IPC_EXCL配合 IPC_CREAT 使用,若存在则失败
权限模式八进制权限(如 0666

典型组合

// 创建新共享内存(如果存在则报错)
IPC_CREAT | IPC_EXCL | 0666// 获取或创建共享内存
IPC_CREAT | 0666

(四)返回值

返回值说明
正整数共享内存标识符(shmid)
-1失败,检查 errno:<br>• EEXIST(已存在)<br>• ENOENT(不存在)<br>• EACCES(权限不足)

(五)使用示例

1. 创建新共享内存

#define SHM_SIZE 4096int main() {key_t key = ftok("/tmp", 'A');if (key == -1) {perror("ftok failed");exit(EXIT_FAILURE);}// 创建共享内存(存在则失败)int shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666);if (shmid == -1) {if (errno == EEXIST) {fprintf(stderr, "Shared memory already exists\n");} else {perror("shmget failed");}exit(EXIT_FAILURE);}printf("Created shmid: %d\n", shmid);return 0;
}

2. 获取已有共享内存

int get_existing_shm(key_t key) {// 不指定大小,仅获取已有内存int shmid = shmget(key, 0, 0);if (shmid == -1) {perror("Access failed");exit(EXIT_FAILURE);}return shmid;
}

(六)共享内存生命周期

操作函数说明
创建shmget内核分配资源
附加到进程空间shmat映射到进程地址空间
分离shmdt解除映射关系
控制/删除shmctl设置状态或删除共享内存

重要特性

  • 共享内存会持续存在直到:
    • 所有进程分离(shmdt
    • 被显式删除(shmctl(..., IPC_RMID, ...)
    • 系统重启

(七)相关命令

# 查看共享内存段
ipcs -m# 删除指定共享内存
ipcrm -m <shmid># 查看系统限制
cat /proc/sys/kernel/shmmax  # 最大单段内存大小

十三、共享内存附加函数 shmat

(一)函数原型

#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

(二)核心功能

  • 内存映射:将共享内存段附加到进程的地址空间
  • 访问控制:设置读写权限(完整访问/只读)
  • 地址管理:自动或手动指定映射地址

(三)参数详解

参数类型说明
shmidint共享内存标识符(由shmget获得)
shmaddrconst void*建议映射地址(通常设为NULL由系统自动分配)
shmflgint附加标志:<br>• 0 - 可读写<br>• SHM_RDONLY - 只读

(四)返回值

返回值说明
有效指针映射到进程空间的起始地址
(void*)-1附加失败,检查errno:<br>• EINVAL(无效shmid)<br>• EACCES(权限不足)

(五)使用示例

基础用法

int shmid = shmget(ftok("/tmp", 'A'), 4096, IPC_CREAT | 0666);
if (shmid == -1) {perror("shmget failed");exit(EXIT_FAILURE);
}// 附加共享内存(可读写)
char *shm_ptr = (char*)shmat(shmid, NULL, 0);
if (shm_ptr == (void*)-1) {perror("shmat failed");exit(EXIT_FAILURE);
}// 使用共享内存
strcpy(shm_ptr, "Hello Shared Memory");
printf("Data in SHM: %s\n", shm_ptr);// 分离内存
if (shmdt(shm_ptr) == -1) {perror("shmdt failed");
}

只读模式

// 以只读方式附加
void *read_ptr = shmat(shmid, NULL, SHM_RDONLY);
if (read_ptr == (void*)-1) {perror("Read-only attach failed");exit(1);
}// 尝试写入会引发段错误
// strcpy(read_ptr, "Test"); // 危险操作!

(六)重要注意事项

1. 地址空间管理

场景建议方案
32位系统优先使用自动地址分配
需要固定地址使用shmaddr + 地址对齐检查
多段内存访问维护地址映射表

2. 生命周期控制

// 分离内存(不删除)
shmdt(shm_ptr);// 删除共享内存(需所有进程分离)
shmctl(shmid, IPC_RMID, NULL);

3. 跨平台差异

系统特性LinuxWindows
地址随机化默认启用(需设置shmaddr无类似机制
最大附加次数SHMMNI(通过sysctl查看)由系统资源限制

十四、共享内存控制函数 shmctl 

(一)函数原型

#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

(二)核心功能

  • 删除共享内存:通过 IPC_RMID 命令
  • 获取状态信息:通过 IPC_STAT 命令
  • 修改权限属性:通过 IPC_SET 命令

(三)参数说明

参数类型说明
shmidint共享内存标识符
cmdint控制命令:<br>• IPC_RMID - 删除<br>• IPC_STAT - 获取状态
bufstruct shmid_ds*用于存储/修改状态信息(删除时可设为NULL

(四)删除操作详解

1. 删除命令使用

// 标记删除共享内存
if (shmctl(shmid, IPC_RMID, NULL) == -1) {perror("Delete failed");exit(EXIT_FAILURE);
}

2. 删除机制

删除阶段系统行为
立即效果共享内存被标记为待删除,不再接受新进程附加
最终释放条件所有已附加进程执行shmdt分离后,内核自动释放资源
访问规则变化已附加进程仍可正常访问,新shmget请求会失败

(五)使用示例

1. 安全删除模式

struct shmid_ds shm_info;// 检查附加进程数
if (shmctl(shmid, IPC_STAT, &shm_info) == 0) {printf("当前附加进程数: %lu\n", shm_info.shm_nattch);if (shm_info.shm_nattch == 0) {// 立即删除shmctl(shmid, IPC_RMID, NULL);} else {// 标记延迟删除shmctl(shmid, IPC_RMID, NULL);printf("等待%lu个进程分离...\n", shm_info.shm_nattch);}
}

2. 错误处理

if (shmctl(shmid, IPC_RMID, NULL) == -1) {switch(errno) {case EINVAL:fprintf(stderr, "无效的shmid\n");break;case EACCES:fprintf(stderr, "权限不足\n");break;case EPERM:fprintf(stderr, "非创建者/root用户\n");break;default:perror("未知错误");}exit(EXIT_FAILURE);
}

(六)结构体信息

shmid_ds 关键字段

struct shmid_ds {struct ipc_perm shm_perm;  // 权限信息size_t          shm_segsz; // 内存大小time_t          shm_atime; // 最后附加时间time_t          shm_dtime; // 最后分离时间time_t          shm_ctime; // 最后修改时间pid_t           shm_cpid;  // 创建者PIDpid_t           shm_lpid;  // 最后操作PIDunsigned long   shm_nattch;// 当前附加进程数
};

(七)系统管理命令

# 查看所有共享内存段
ipcs -m# 强制删除共享内存(慎用!)
ipcrm -m <shmid># 查看内核参数
sysctl -a | grep shm

融合实列:

发送代码

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<string.h>
#include <signal.h>int main(int argc, const char *argv[])
{key_t key = ftok("/",'a');if(key < 0){perror("ftok fail");return -1;}printf("key is %#x\n",key);int shmid = shmget(key,1024,IPC_CREAT|0666);if(shmid < 0){perror("shget fail");return 1;}printf("shmid = %d\n",shmid);char *p = shmat(shmid,NULL,0);if(p == NULL){perror("shmat fail");return -1;}pid_t pid = *(pid_t*)p;while(1){fgets(p,1024,stdin);kill(pid,SIGUSR1);	}return 0;
}

接收代码

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#include<signal.h>void do_signo(int signo)
{
}int main(int argc, const char *argv[])
{key_t key = ftok("/",'a');if(key < 0){perror("ftok fail");return -1;}printf("key is %#x\n",key);int shmid = shmget(key,1024,IPC_CREAT|0666);if(shmid < 0){perror("shget fail");return 1;}printf("shmid = %d\n",shmid);char *p = shmat(shmid,NULL,0);if(p == NULL){perror("shmat fail");return -1;}signal(SIGUSR1,do_signo);*(pid_t*)p = getpid();while(1){pause();printf("buf = %s\n",p);}if(shmdt(p) < 0){perror("shmdt fail");return -1;}if(shmctl(shmid,IPC_RMID,NULL)<0){perror("shmctl fail");return -1;}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/896331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

考研/保研复试英语问答题库(华工建院)

华南理工大学建筑学院保研/考研 英语复试题库&#xff0c;由华工保研er和学硕笔试第一同学一起整理&#xff0c;覆盖面广&#xff0c;助力考研/保研上岸&#xff01;需要&#x1f447;载可到文章末尾见小&#x1f360;。 以下是主要内容&#xff1a; Part0 复试英语的方法论 Pa…

岳阳市美术馆预约平台(小程序论文源码调试讲解)

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

Python游戏编程之赛车游戏6-3

1 “敌人”汽车类的创建 在创建玩家汽车类之后&#xff0c;接下来创建“敌人”汽车类。“敌人”汽车类与玩家类一样&#xff0c;也是包含两个方法&#xff0c;一个是__init__()&#xff0c;另一个是move()。 1.1 __init__()方法 “敌人”汽车类的__init__()方法代码如图1所示…

TCP/UDP调试工具推荐:Socket通信图解教程

TCP/UDP调试工具推荐&#xff1a;Socket通信图解教程 一、引言二、串口调试流程三、下载链接 SocketTool 调试助手是一款旨在协助程序员和网络管理员进行TCP和UDP协议调试的网络通信工具。TCP作为一种面向连接、可靠的协议&#xff0c;具有诸如连接管理、数据分片与重组、流量和…

神经网络 - 神经元

人工神经元(Artificial Neuron)&#xff0c;简称神经元(Neuron)&#xff0c;是构成神经网络的基本单元&#xff0c;其主要是模拟生物神经元的结构和特性&#xff0c;接收一组输入信号并产生输出。 生物学家在 20 世纪初就发现了生物神经元的结构。一个生物神经元通常具有多个树…

蓝桥杯备考:贪心算法之矩阵消除游戏

这道题是牛客上的一道题&#xff0c;它呢和我们之前的排座位游戏非常之相似&#xff0c;但是&#xff0c;排座位问题选择行和列是不会改变元素的值的&#xff0c;这道题呢每每选一行都会把这行或者这列清零&#xff0c;所以我们的策略就是先用二进制把选择所有行的情况全部枚举…

ktransformers 上的 DeepSeek-R1 671B open-webui

ktransformers 上的 DeepSeek-R1 671B open-webui 一、下载GGUF模型1. 创建目录2. 魔塔下载 DeepSeek-R1-Q4_K_M3. 安装显卡驱动和cuda4. 显卡 NVIDIA GeForce RTX 4090 二、安装ktransformers1. 安装依赖2. 安装uv工具链3. 下载源码4. 创建python虚拟环境 三、编译ktransforme…

smolagents学习笔记系列(五)Tools-in-depth-guide

这篇文章锁定官网教程中的 Tools-in-depth-guide 章节&#xff0c;主要介绍了如何详细构造自己的Tools&#xff0c;在之前的博文 smolagents学习笔记系列&#xff08;二&#xff09;Agents - Guided tour 中我初步介绍了下如何将一个函数或一个类声明成 smolagents 的工具&…

形式化数学编程在AI医疗中的探索路径分析

一、引言 1.1 研究背景与意义 在数字化时代,形式化数学编程和 AI 形式化医疗作为前沿领域,正逐渐改变着我们的生活和医疗模式。形式化数学编程是一种运用数学逻辑和严格的形式化语言来描述和验证程序的技术,它通过数学的精确性和逻辑性,确保程序的正确性和可靠性。在软件…

【深度学习神经网络学习笔记(三)】向量化编程

向量化编程 向量化编程前言1、向量化编程2、向量化优势3、正向传播和反向传播 向量化编程 前言 向量化编程是一种利用专门的指令集或并行算法来提高数据处理效率的技术&#xff0c;尤其在科学计算、数据分析和机器学习领域中非常常见。它允许通过一次操作处理整个数组或矩阵的…

海康威视摄像头RTSP使用nginx推流到服务器直播教程

思路&#xff1a; 之前2020年在本科的时候&#xff0c;由于项目的需求需要将海康威视的摄像头使用推流服务器到网页进行直播。这里将自己半个月琢磨出来的步骤给大家发一些。切勿转载&#xff01;&#xff01;&#xff01;&#xff01; 使用网络摄像头中的rtsp协议---------通…

鸿蒙开发深入浅出03(封装通用LazyForEach实现懒加载)

鸿蒙开发深入浅出03&#xff08;封装通用LazyForEach实现懒加载&#xff09; 1、效果展示2、ets/models/BasicDataSource.ets3、ets/models/HomeData.ets4、ets/api/home.ets5、ets/pages/Home.ets6、ets/views/Home/SwiperLayout.ets7、后端代码 1、效果展示 2、ets/models/Ba…

【Rust中级教程】2.8. API设计原则之灵活性(flexible) Pt.4:显式析构函数的问题及3种解决方案

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 说句题外话&#xff0c;这篇文章一共5721个字&#xff0c;是我截至目前写的最长的一篇文章&a…

一周学会Flask3 Python Web开发-Jinja2模板过滤器使用

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 在Jinja2中&#xff0c;过滤器(filter)是一些可以用来修改和过滤变量值的特殊函数&#xff0c;过滤器和变量用一个竖线 | &a…

数据库 安装initializing database不通过

出现一下情况时&#xff1a; 处理方法&#xff1a; 将自己的电脑名称 中文改成英文 即可通过

嵌入式开发:傅里叶变换(5):STM32和Matlab联调验证FFT

目录 1. MATLAB获取 STM32 的原始数据 2. 将数据上传到电脑 3. MATLAB 接收数据并验证 STM32进行傅里叶代码 结果分析 STM32 和 MATLAB 联调是嵌入式开发中常见的工作流程&#xff0c;通常目的是将 STM32 采集的数据或控制信号传输到 MATLAB 中进行实时处理、分析和可视化…

微信小程序源码逆向 MacOS

前言 日常工作中经常会遇到对小程序的渗透测试&#xff0c;微信小程序的源码是保存在用户客户端本地&#xff0c;在渗透的过程中我们需要提取小程序的源码进行问题分析&#xff0c;本篇介绍如何在苹果电脑 MacOS 系统上提取微信小程序的源码。 0x01 微信小程序提取 在苹果电…

ubuntu-24.04.1-desktop 中安装 QT6.7

ubuntu-24.04.1-desktop 中安装 QT6.7 1 环境准备1.1 安装 GCC 和必要的开发包:1.2 Xshell 连接 Ubuntu2 安装 Qt 和 Qt Creator:2.1 下载在线安装器2.2 在虚拟机中为文件添加可执行权限2.3 配置镜像地址运行安装器2.4 错误:libxcb-xinerama.so.0: cannot open shared objec…

从最小依赖角度谈静态库与动态库的选择及配置策略

文章目录 1. 前言2. 静态库与动态库&#xff1a;依赖最小化的抉择2.1 静态库概述2.2 动态库概述2.3 依赖最小化角度的选择建议 3. 运行时库配置策略&#xff1a;/MT 与 /MD 的取舍3.1 /MT 与 /MD 的优劣比较3.2 配置选择的建议 4. 实际案例与配置示例4.1 静态库示例&#xff08…

【深度学习神经网络学习笔记(二)】神经网络基础

神经网络基础 神经网络基础前言1、Logistic 回归2、逻辑回归损失函数3、梯度下降算法4、导数5、导数计算图6、链式法则7、逻辑回归的梯度下降 神经网络基础 前言 Logistic 回归是一种广泛应用于统计学和机器学习领域的广义线性回归模型&#xff0c;主要用于解决二分类问题。尽…