在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
10-安全传输平台项目-第04天(统一通信组件-统一共享内存组件)
目录:
一、复习
二、安全传输平台项目—统一通信组件
1、客户端连接服务器
2、客户端连接池连接服务器
3、线程传参现象展示
4、线程传参内存冗余法
三、安全传输平台项目—统一共享内存组件
1、常见IPC
2、简单内存模型分析
3、共享内存操作函数—shmget
4、共享内存操作函数—shmat(shmdt了解)
5、共享内存操作函数—shmctl
6、Linux内核管理共享内存方法
7、共享内存操作函数接口
四、安全传输平台项目—密钥协商服务程序
1、客户端服务器密钥协商预说明
一、复习
1、wind下制作动态库
2、linux下制作动态库
3、makefile复习
4、统一通信组件—socket通信
5、统一通信组件-服务器端实现
>增加释放内存—free
#include <unistd.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <pthread.h> #include "poolsocket.h" void *mystart_routine(void *arg) { int ret = 0; int timeout = 3; int connfd = (int)arg; unsigned char *out = NULL; int outlen = 0; while (1) { //服务器端端接受报文 ret = sckServer_rev(connfd, timeout, &out, &outlen); //1 if (ret == Sck_ErrPeerClosed) { //printf("aaaaa "); printf("服务器端检测到客户端有一条连接已关闭 "); break; } else if (ret == Sck_ErrTimeOut) { printf("服务器端send超时 "); continue; } else if (ret != 0) { printf("服务器端 sckServer_send() err "); break; } printf("out:%s ", out); //回射 //服务器端发送报文 ret = sckServer_send(connfd, timeout, out, outlen); if (ret == Sck_ErrPeerClosed) { sck_FreeMem((void **)&out); printf("服务器端检测到客户端有一条连接已关闭 "); break; } else if (ret == Sck_ErrTimeOut) { sck_FreeMem((void **)&out); printf("服务器端send超时 "); continue; } else if (ret != 0) { sck_FreeMem((void **)&out); printf("服务器端 sckServer_send() err "); break; } sck_FreeMem((void **)&out); } sckServer_close(connfd); return NULL; } int main() { int ret = 0; int port = 8001; int listenfd = 0; int timeout = 3; int connfd = 0; pthread_t pid; //函数声明 //服务器端初始化 ret = sckServer_init(port, &listenfd); if (ret != 0) { printf("func sckServer_init() err:%d ", ret); return ret; } while (1) { ret = sckServer_accept(listenfd, timeout, &connfd); if (ret == Sck_ErrTimeOut) { printf("func sckServer_accept() Sck_ErrTimeOut "); continue; } else if (ret != 0) { ret = 2; printf("fun sckServer_accept() err :%d ", ret); break; } pthread_create(&pid, NULL, mystart_routine, (void *)(connfd)); } //服务器端环境释放 int sckServer_destroy(); printf("hello.... "); }
二、安全传输平台项目—统一通信组件
1、客户端连接服务器
》编写client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include "poolsocket.h" int main(void) { char *ip = "127.0.0.1"; int port = 8080; int time = 3; int connfd = -1; int ret = -1; unsigned char *data = "abcdefg"; int datalen = 5; unsigned char *out = NULL; int outlen = -1; //客户端 初始化 ret = sckClient_init(); if (ret != 0) { printf("sckClient_init error %d ", ret); return ret; } while (1) { //客户端 连接服务器 ret = sckClient_connect(ip, port, time, &connfd); if (ret == Sck_ErrTimeOut) { printf("---客户端连接服务器 超时 "); continue; } else if (ret != 0) { printf("客户端连接服务器 失败: errorNO:%d ", ret); break; } //客户端 发送报文 ret = sckClient_send(connfd, time, data, datalen); if (ret == Sck_ErrPeerClosed) { printf("---服务器关闭,客户端断开连接 "); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器接收数据 超时 "); continue; } else if (ret != 0) { printf("客户端发送数据失败:errorNO:%d ", ret); break; } sleep(1); //客户端 接受报文 ret = sckClient_rev(connfd, time, &out, &outlen); if (ret == Sck_ErrPeerClosed) { printf("---服务器关闭,客户端断开连接 "); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器发送数据 超时 "); continue; } else if (ret != 0) { printf("客户端接收数据失败:errorNO:%d ", ret); break; } } //客户端 关闭和服务端的连接 if (connfd != -1) sckClient_closeconn(connfd); //客户端 释放 sckClient_destroy(); return 0; }
创建目录test,把makefile、libitcastsocket.so、poolsocket.h放入test目录下。
>make
>./server
打开另一个终端,切换到test目录下,执行>./client,然后查看原终端的数据接收情况。
注意中文乱码,需要转换:
为什么会出现服务器检测到客户端发送数据超时?
2、客户端连接池连接服务器
》编写client2.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include "poolsocket.h" /* typedef struct _SCKClitPoolParam { char serverip[64]; int serverport; int bounds; //池容量 int connecttime; int sendtime; int revtime; }SCKClitPoolParam; */ void *mystart_routin(void *arg) { int ret = 0; int connfd = -1; void *handle = arg; unsigned char *data = "abcdefg"; int datalen = 5; unsigned char *out = NULL; int outlen = -1; // 获取一条连接池中的链接: ret = sckCltPool_getConnet(handle, &connfd); if (ret != 0) { printf("从连接池 获取 连接失败:%d ", ret); return NULL; } //可以增加发送数据的次数,flag=10,flag-- while (1) { ret = sckCltPool_send(handle, connfd, data, datalen); if (ret == Sck_ErrPeerClosed) { printf("---服务器关闭,客户端断开连接 "); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器接收数据 超时 "); continue; } else if (ret != 0) { printf("客户端发送数据失败:errorNO:%d ", ret); break; } ret = sckCltPool_rev(handle, connfd, &out, &outlen); if (ret == Sck_ErrPeerClosed) { printf("---服务器关闭,客户端断开连接 "); break; } else if (ret == Sck_ErrTimeOut) { printf("---服务器发送数据 超时 "); continue; } else if (ret != 0) { printf("客户端接收数据失败:errorNO:%d ", ret); break; } printf("------接收到 服务器回发数据:%s ", out); } sckCltPool_putConnet(handle, connfd, 0); return NULL; } int main(void) { int i = 0; int ret = 0; pthread_t pidArray[6] = {0}; SCKClitPoolParam clientPoolparam; strcpy(clientPoolparam.serverip, "127.0.0.1"); clientPoolparam.serverport = 8080; clientPoolparam.bounds = 10; clientPoolparam.connecttime = 3; clientPoolparam.sendtime = 3; clientPoolparam.revtime = 3; void *handle = NULL; //客户端 初始化 ret = sckCltPool_init(&handle, &clientPoolparam); if (ret != 0) { printf("sckCltPool_init error %d ", ret); return ret; } while (1) { for (i = 0; i < 6; i++) { pthread_create(&pidArray[i], NULL, mystart_routin, handle); //需要判断返回值 } for (i = 0; i< 6; i++) { pthread_join(pidArray[i], NULL); } } //销毁连接池 sckCltPool_destroy(handle); return 0; }
>make
>./server
打开另一个终端,切换到test目录下,执行>./client,然后查看原终端的数据接收情况。
注意:pthread_create(&pidArray[i], NULL, myclient_startroutine, handle);在客户端初始化后,从连接池取连接时,最后一个参数采用参数handle,这是句柄。而单进程的server的pthread_create(&pid, NULL, mystart_routine, (void *)(connfd));的最后一个参数connfd,是文件描述符。这是二者的区别。
3、线程传参现象展示
>vi a_server.c
#include <unistd.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <pthread.h> #include "poolsocket.h" void *mystart_routine(void *arg) { int ret = 0; int timeout = 3; int connfd = (int)arg; unsigned char *out = NULL; int outlen = 0; while (1) { //服务器端端接受报文 ret = sckServer_rev(connfd, timeout, &out, &outlen); //1 if (ret == Sck_ErrPeerClosed) { //printf("aaaaa "); printf("服务器端检测到客户端有一条连接已关闭 "); break; } else if (ret == Sck_ErrTimeOut) { printf("服务器端send超时 "); continue; } else if (ret != 0) { printf("服务器端 sckServer_send() err "); break; } printf("out:%s ", out); //回射 //服务器端发送报文 ret = sckServer_send(connfd, timeout, out, outlen); if (ret == Sck_ErrPeerClosed) { sck_FreeMem((void **)&out); printf("服务器端检测到客户端有一条连接已关闭 "); break; } else if (ret == Sck_ErrTimeOut) { sck_FreeMem((void **)&out); printf("服务器端send超时 "); continue; } else if (ret != 0) { sck_FreeMem((void **)&out); printf("服务器端 sckServer_send() err "); break; } sck_FreeMem((void **)&out); } sckServer_close(connfd); return NULL; } int main() { int ret = 0; int port = 8001; int listenfd = 0; int timeout = 3; int connfd = 0; pthread_t pid; //函数声明 //服务器端初始化 ret = sckServer_init(port, &listenfd); if (ret != 0) { printf("func sckServer_init() err:%d ", ret); return ret; } while (1) { ret = sckServer_accept(listenfd, timeout, &connfd); if (ret == Sck_ErrTimeOut) { printf("func sckServer_accept() Sck_ErrTimeOut "); continue; } else if (ret != 0) { ret = 2; printf("fun sckServer_accept() err :%d ", ret); break; } pthread_create(&pid, NULL, mystart_routine, (void *)(connfd)); } //服务器端环境释放 int sckServer_destroy(); printf("hello.... "); }
注意:pthread_create(&pid, NULL, mystart_routine, (void *)(connfd));最后一个参数强制转换的时候考虑32/64位的差异!
>vi a_client3err.c
#include <unistd.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <pthread.h> #include "poolsocket.h" typedef struct _ThreadInfo { void *handle;//线程池连接句柄 int iLoop;//一个线程被回调后会被执行多少次,跑多少圈,向服务器发送数据 int iArrayIndex; //线程数组的下标,第几个被创建出来的线程 }ThreadInfo; void* myclient_startroutine (void *arg) { int i = 0, ret = 0; int connfd = 0; char data[64] = {0}; int datalen = 0; unsigned char *out = NULL; int outlen = 0; ThreadInfo *pThreadInfo = (ThreadInfo *)arg; void *handle = pThreadInfo->handle; // //客户端 socket池 获取一条连接 ret = sckCltPool_getConnet(handle, &connfd); if (ret != 0) { printf("func sckCltPool_getConnet() err:%d ", ret); return NULL; } for (i=0; i<pThreadInfo->iLoop; i++) { //客户端 socket池 发送数据 memset(data, 0, sizeof(data)); sprintf(data, "第%d线程, 第%d圈", pThreadInfo->iArrayIndex, i+1); ret = sckCltPool_send(handle, connfd, data, strlen(data)); if (ret == Sck_ErrPeerClosed) { printf("sckCltPool_send 客户端检测到 服务器已经关闭 退出 "); break; } else if (ret == Sck_ErrTimeOut) { printf(" sckCltPool_send timeout "); break; } else if (ret != 0) { printf("fun sckServer_rev() err:%d ", ret); break; } //客户端 socket池 接受数据 ret = sckCltPool_rev(handle, connfd, &out, &outlen); //1 if (ret == Sck_ErrPeerClosed) { printf("sckCltPool_rev 客户端检测到 服务器已经关闭 退出 "); break; } else if (ret == Sck_ErrTimeOut) { printf(" sckCltPool_rev timeout "); break; } else if (ret != 0) { printf("fun sckCltPool_rev() err:%d ", ret); break; } printf("客户端 out:%s ", out); sck_FreeMem((void **)&out); } //客户端 socket池 把连接放回 socket池中 sckCltPool_putConnet(handle, connfd, 0); //0正常 1 return NULL; } int main(void) { int ret = 0, i = 0; char *ip = "127.0.0.1"; int port = 8001; int time = 3; int connfd = 0; int iLoop = 0; //圈数 int iThreadNum = 0 ; //线程数 void *handle = NULL; pthread_t pidArray[1024]; ThreadInfo threadInfo; memset(&threadInfo, 0, sizeof(ThreadInfo)); SCKClitPoolParam sckClitPoolParm; memset(&sckClitPoolParm, 0, sizeof(SCKClitPoolParam)); strcpy(sckClitPoolParm.serverip, "127.0.0.1"); sckClitPoolParm.serverport = 8001; sckClitPoolParm.bounds = 10; sckClitPoolParm.connecttime = 3; sckClitPoolParm.sendtime = 3; sckClitPoolParm.revtime = 3; printf(" 请输入线程的个数: "); scanf("%d", &iThreadNum); printf(" 请输入每个线程运行圈数: "); scanf("%d", &iLoop); if (iThreadNum >= 1024) { printf("iThreadNum大于1024 "); return 0; } //客户端 socket池初始化 ret = sckCltPool_init(&handle, &sckClitPoolParm); if (ret != 0) { printf("func sckCltPool_init() err:%d ", ret); return ret; } //启动多线程 for (i=0; i<iThreadNum; i++) { threadInfo.handle = handle; //alt + 鼠标键左键拖动 threadInfo.iLoop = iLoop; threadInfo.iArrayIndex = i + 1; pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)&threadInfo); } //主进程 等待子线程 结束 for (i=0; i<iThreadNum; i++) { pthread_join(pidArray[i], NULL); } //客户端 socket池 销毁连接 sckCltPool_destroy(handle); printf("client hello.... "); return 0; }
>make
>./a_server
打开另一个终端,切换到test目录下,执行>./a_client3err,然后查看原终端的数据接收情况。
注意:重新定义了一个结构体:
typedef struct _ThreadInfo { void *handle;//线程池连接句柄 int iLoop;//一个线程被回调后会被执行多少次,跑多少圈,向服务器发送数据 int iArrayIndex; //线程数组的下标,第几个被创建出来的线程 }ThreadInfo;
1)问题:pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)&threadInfo);中的最后一个参数需要加地址符号吗?
由于threadInfo是一个结构体,从大小及值拷贝需要的空间复杂度考虑,所以必须传入地址,不能传值。结论——结构体做参数,最好传地址!!!
2)问题:每次都是最后一个线程被回调然后执行n次?跑n圈?
分析:pthread_create传参最后一个参数threadInfo使用了地址传参。需要看4、线程传参内存冗余法
4、线程传参内存冗余法
》内存冗余分析图:
注意:形参和局部变量地位等同,都位于栈上。
原因:线程子函数栈空间调用了main函数栈空间的值,当线程几乎同时创建时,去读取main栈空间的值时,main栈空间的值i还在变化(自增)。所以需要为每个线程子函数创建属于自己的栈空间。
>vi a_client4.c
#include <unistd.h> #include <sys/types.h> #include <signal.h> #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <pthread.h> #include "poolsocket.h" typedef struct _ThreadInfo { void *handle; int iLoop; int iArrayIndex;//线程数组的下标 }ThreadInfo; void* myclient_startroutine (void *arg) { int i = 0, ret = 0; int connfd = 0; char data[64] = {0}; int datalen = 0; unsigned char *out = NULL; int outlen = 0; ThreadInfo *pThreadInfo = (ThreadInfo *)arg; void *handle = pThreadInfo->handle; // //客户端 socket池 获取一条连接 ret = sckCltPool_getConnet(handle, &connfd); if (ret != 0) { printf("func sckCltPool_getConnet() err:%d ", ret); return NULL; } for (i=0; i<pThreadInfo->iLoop; i++) { //客户端 socket池 发送数据 memset(data, 0, sizeof(data)); sprintf(data, "第%d线程, 第%d圈", pThreadInfo->iArrayIndex, i+1); ret = sckCltPool_send(handle, connfd, data, strlen(data)); if (ret == Sck_ErrPeerClosed) { printf("sckCltPool_send 客户端检测到 服务器已经关闭 退出 "); break; } else if (ret == Sck_ErrTimeOut) { printf(" sckCltPool_send timeout "); break; } else if (ret != 0) { printf("fun sckServer_rev() err:%d ", ret); break; } //客户端 socket池 接受数据 ret = sckCltPool_rev(handle, connfd, &out, &outlen); //1 if (ret == Sck_ErrPeerClosed) { printf("sckCltPool_rev 客户端检测到 服务器已经关闭 退出 "); break; } else if (ret == Sck_ErrTimeOut) { printf(" sckCltPool_rev timeout "); break; } else if (ret != 0) { printf("fun sckCltPool_rev() err:%d ", ret); break; } printf("客户端 out:%s ", out); sck_FreeMem((void **)&out); } //客户端 socket池 把连接放回 socket池中 sckCltPool_putConnet(handle, connfd, 0); //0正常 1 if (arg != NULL) free(arg); return NULL; } int main() { int ret = 0, i = 0; char *ip = "127.0.0.1"; int port = 8001; int time = 3; int connfd = 0; int iLoop = 0; //圈数 int iThreadNum = 0 ; //线程数 void *handle = NULL; pthread_t pidArray[1024]; //ThreadInfo threadInfo; //memset(&threadInfo, 0, sizeof(ThreadInfo)); SCKClitPoolParam sckClitPoolParm; memset(&sckClitPoolParm, 0, sizeof(SCKClitPoolParam)); strcpy(sckClitPoolParm.serverip, "127.0.0.1"); sckClitPoolParm.serverport = 8001; sckClitPoolParm.bounds = 20; //node: 客户端线程池的个数是10个 sckClitPoolParm.connecttime = 3; sckClitPoolParm.sendtime = 3; sckClitPoolParm.revtime = 3; printf(" 请输入线程的个数: "); scanf("%d", &iThreadNum); printf(" 请输入每个线程运行圈数: "); scanf("%d", &iLoop); if (iThreadNum >= 1024) { printf("iThreadNum大于1024 "); return 0; } //客户端 socket池初始化 ret = sckCltPool_init(&handle, &sckClitPoolParm); if (ret != 0) { printf("func sckCltPool_init() err:%d ", ret); return ret; } //启动多线程 for (i=0; i<iThreadNum; i++) { ThreadInfo *pInfo = (ThreadInfo *)malloc(sizeof(ThreadInfo)); pInfo->handle = handle; //alt + 鼠标键左键拖动 pInfo->iLoop = iLoop; pInfo->iArrayIndex = i + 1; pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)pInfo); } //主进程 等待子线程 结束 for (i=0; i<iThreadNum; i++) { pthread_join(pidArray[i], NULL); } //客户端 socket池 销毁连接 sckCltPool_destroy(handle); printf("client hello.... "); return 0; }
>make
>./a_server
打开另一个终端,切换到test目录下,执行>./a_client4,然后查看原终端的数据接收情况。
》总结(线程传参):
传值:
值传递。 传递的变量 是常量(不变)。
传地址:
传变量地址。 变量在回调函数内会被修改。
变量在回调函数内部使用时,会不断变化。
解决---- 内存冗余法。
三、安全传输平台项目—统一共享内存组件
(1)共享内存的特征
(2)共享内存常用的基础API
(3)Linux内核管理共享内存的方法
(4)函数接口封装
1、常见IPC
》共享内存的特征:
IPC: pipe: 简单。只能在有血缘关系的进程间通信。
fifo: 在无血缘关系的进程间通信。 内部数据只能读一次。
mmap: 在无血缘关系的进程间通信。 内部数据支持反复读取。
信号: 开销小。 携带数据简单。
本地 socket: 稳定性最好。 实现复杂。
共享内存 shared mem : 类似于 mmap。 效率最高IPC。
2、简单内存模型分析
3、共享内存操作函数—shmget
>man shmget
int shmget(key_t key, size_t size, int shmflg);
key_t key:16进制 非0数据。 0x0018 ----描述当前共享内存 状态。 shared --- private
size_t size: 大小
int shmflg:状态:读写执行权限 -- 8进制。 0644 。
IPC_CREAT(用于创建) | IPC_EXCL(用于判断是否存在)
返回值:成功 shmid, 失败-1, 设置errorno
>测试函数shmget(0x0001, 256, 0644 | IPC_CREAT);
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/mman.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> int main(void) { int shmid = -1; char *buf = NULL; shmid = shmget(0x0001, 256, 0644 | IPC_CREAT); if (shmid == -1) { perror("shmget error"); return -1; } printf("-------------open shm ok "); return 0; }
src = $(wildcard *.c) obj = $(patsubst %.c, %, $(src)) ALL:$(obj) CFLAGS = -Wall -g #LIBNAME = -litcastsocket #LIBFLAGS = -L$(HOME)/lib #LD_LIBRARY_PAHT=$(HOME)/lib $(obj):%:%.c gcc $< -o $@ $(CFLAGS) clean: -rm -rf $(obj) PHONY: ALL clean
基本操作:
》查看共享内存
>ipcs
》操作共享内存
[it01@localhost ~]$ ipcrm -m
ipcrm:选项需要一个参数 -- m
ipcrm: illegal option -- ?
usage: ipcrm [ [-q msqid] [-m shmid] [-s semid]
[-Q msgkey] [-M shmkey] [-S semkey] ... ]
>测试函数shmget(0x0018, 256, 0644 | IPC_CREAT | IPC_EXCL);
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/mman.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> int main(void) { int shmid = -1; char *buf = NULL; shmid = shmget(0x0018, 256, 0644 | IPC_CREAT | IPC_EXCL); if (shmid == -1) { perror("shmget error"); return -1; } printf("-------------open shm ok "); return 0; }
>ipcs
>./test_shm(会报错)
》如何清除某个用户下创建的共享内存?
需要用到脚本cleanipc:
#!/bin/bash #check parameters if [ "$#" != "2" ] then echo "usage: $0 user shm|sem|smg|all" exit 1 elif [ "$2" != "shm" -a "$2" != "sem" -a "$2" != "msg" -a "$2" != "all" ] then echo "usage: $0 user shm|sem|smg|all" exit 1 fi #delete the shm if [ "$2" = "shm" -o "$2" = "all" ] then START=`ipcs|sed -n '/Shared/='` END=`ipcs|sed -n '/Semaphore/='` for i in `ipcs|sed -n ${START},${END}p|cut -d ' ' -f 2 ` do ipcrm shm $i echo -e $i done fi #delete the sem if [ "$2" = "sem" -o "$2" = "all" ] then START=`ipcs|sed -n '/Semaphore/='` END=`ipcs|sed -n '/Message/='` for i in `ipcs|sed -n ${START},${END}p|grep $1|cut -d ' ' -f 2 ` do ipcrm sem $i echo -e $i done fi #delete the msg if [ "$2" = "msg" -o "$2" = "all" ] then START=`ipcs|sed -n '/Message/='` for i in `ipcs|sed -n ${START},$$p|grep $1|cut -d ' ' -f 2 ` do ipcrm msg $i echo -e $i done fi
>.cleanipc test04 all
注意:只是清除了用户创建的共享内存,系统级的共享内存仍然存在!
4、共享内存操作函数—shmat(shmdt了解)
>man shmat
void *shmat(int shmid, const void *shmaddr, int shmflg); 建立进程与共享内存的 关联关系。
shmid: shmget 返回值
shmaddr: 建议传入地址。默认NULL内核自动分配。
shmflg: SHM_RND 读写。
SHM_RDONLY 只读。
返回值:成功 映射内存首地址, 失败(void *)-1, 设置errorno
int shmdt(const void *shmaddr);
取消 进程与共享内存的 关联关系。
返回值:成功 0, 失败-1, 设置errorno
>测试shmat(shmid, NULL, SHM_RND);和shmdt(buf);
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/mman.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> int main(void) { int shmid = -1; char *buf = NULL; shmid = shmget(0x0018, 256, 0644 | IPC_CREAT);//返回共享内存的id if (shmid == -1) { perror("shmget error"); return -1; } printf("-------------open shm ok "); buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址 if (buf == (void *)-1) { perror("shmat error:"); return -1; } memcpy(buf, "hello", 5); printf("buf:%s ", buf); getchar(); shmdt(buf); printf("finish---dt----------- "); return 0; }
>make
打开另一个终端,输入>ipcs查看该共享内存的使用情况:
在原终端输入任意字符(如:回车)
再次在第二个终端输入>ipcs查看该共享内存的使用情况:
5、共享内存操作函数—shmctl
>man chmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf); Mark标记共享内存,即将被删除
shmid: shmget 返回值
cmd: IPC_RMID 删除 共享内存 (引用计数 变为 0)
对应 参数3 传 NULL
返回值:成功 0, 失败-1, 设置errorno
>测试shmctl(shmid, IPC_RMID, NULL);
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/mman.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> int main(void) { int shmid = -1; char *buf = NULL; shmid = shmget(0x0018, 256, 0644 | IPC_CREAT);//返回共享内存的id if (shmid == -1) { perror("shmget error"); return -1; } printf("-------------open shm ok "); buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址 if (buf == (void *)-1) { perror("shmat error:"); return -1; } memcpy(buf, "hello", 5); printf("buf:%s ", buf); getchar(); shmdt(buf); printf("finish---dt----------- "); getchar(); int ret = shmctl(shmid, IPC_RMID, NULL); if (ret == -1) { perror("shmctl error:"); return -1; } return 0; }
仍按照 4、共享内存操作函数—shmat(shmdt了解) 测试,当最后再次输入任意字符,如:换行,在另一个终端输入>ipcs 查看0x0018 的共享内存,发现已经没有了(计数为0)。
6、Linux内核管理共享内存方法
》问题抛出:A、B、C、D四个进程共享同一块共享内存,如果一个进程执行了shmctl函数(相当于Mark标记了一下),其他进程会怎么样?
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/mman.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> int main(void) { int shmid = -1; char *buf = NULL; shmid = shmget(0x0018, 256, 0644 | IPC_CREAT);//返回共享内存的id if (shmid == -1) { perror("shmget error"); return -1; } printf("-------------open shm ok "); buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址 if (buf == (void *)-1) { perror("shmat error:"); return -1; } memcpy(buf, "hello", 5); /* printf("buf:%s ", buf); getchar(); shmdt(buf); printf("finish---dt----------- "); */ printf("----Enter 执行 shmctl 删除共享内存--- "); getchar(); int ret = shmctl(shmid, IPC_RMID, NULL); if (ret == -1) { perror("shmctl error:"); return -1; } return 0; }
>make
分别打开四个终端,切换到相应目录下,执行>./test_shm
同时打开第5个终端,执行>ipcs 查看对比:
任选一个终端,执行回车(----Enter 执行 shmctl 删除共享内存---):
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000018 5734417 test04 644 256 4
-------------------------------------------------------------------------
0x00000000 5734417 test04 644 256 3 dest
对比总结:
key 0x0018 ---> 0x0000 PRIVATE 状态。——key值发生了变化
(当1号进程创建了0x0018的共享内存后,2、3、4号进程创建时,直接加入后共享0x0018内存,(5、6进程也可以加入)当3号进程执行了shmctl(IPC_RMID)后,共享内存0x0018变为私有,1、2、4仍然共享内存,key变为0x0000,但是5、6等新进程不能加入进来共享这块共享内存了。)
注意:private私有的意思是说针对新加入的进程5、6加入不进来了,而针对原来的1、2、4仍然是共享的。
》总结:Linux内核管理共享内存的方法:
key值 + 引用计数 技术。
问题:当打开四个终端,切换到相应目录下,执行>./test_shm,A、B、C、D都异常终止(执行Ctrl+C),A、B、C、D进程结束了,第5个终端执行>ipcs key值和引用计数分别是几???
总结:虽然A、B、C、D进程都结束了,但是共享内存仍然存在!
7、共享内存操作函数接口
>myipc_shm.h
// myipc_shm.h #ifndef _WBM_MY_SHM_H_ #define _WBM_MY_SHM_H_ #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef __cplusplus extern "C" { #endif //共享内存错误码 #define MYIPC_OK 0 //正确 #define MYIPC_ParamErr 301 //输入参数失败 #define MYIPC_NotEXISTErr 302 //共享内存不存在错误 #define MYIPC_CreateErr 303 //创建共享内存错误 //创建共享内存 若共享内存不存在,则创建 int IPC_CreatShm(int key, int shmsize, int *shmhdl); //打开共享内存 若共享内存不存在,返回错误 int IPC_OpenShm(int key, int shmsize, int *shmhdl); /*********************************************************************** 功能描述: 关联共享内存 参数说明: shmhdl [in] 共享的句柄 mapaddr [out] 共享内存首地址 返回值: 返回0函数执行成功;非0返回错误码 ************************************************************************/ int IPC_MapShm(int shmhdl, void **mapaddr); /*********************************************************************** 功能描述: 取消共享内存关联 参数说明: unmapaddr [in] 共享内存首地址 返回值: 返回0函数执行成功;非0返回错误码 ************************************************************************/ int IPC_UnMapShm(void *unmapaddr); /*********************************************************************** 功能描述: 删除共享内存 参数说明: shmhdl [in] 共享的句柄 返回值: 返回0函数执行成功;非0返回错误码 ************************************************************************/ int IPC_DelShm(int shmhdl); /*********************************************************************** 功能描述: 创建共享内存 通过种子文件 参数说明: shmname [in] 是共享内存名,系统中唯一标志 shmsize [in] 是要创建的共享内存的大小; shmhdl [out] 共享内存的句柄. 返回值: 返回0函数执行成功;非0返回错误码 ************************************************************************/ int IPC_CreatShmBySeedName(const char *shmname, int shmsize, int *shmhdl); #ifdef __cplusplus } #endif #endif
>myipc_shm.c
#define _OS_LINUX_ #if defined _OS_LINUX_ #include <stdio.h> #include <errno.h> #include <unistd.h> #include <memory.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/msg.h> #include "myipc_shm.h" #endif int shmflag = 0; int shmkey; //创建共享内存 若共享内存不存在,则创建 若存在使用原来的 int IPC_CreatShm(int key, int shmsize, int *shmhdl) { int tmpshmhdl = 0; int ret = 0; // 创建共享内存 // 若共享内存不存在则创建 // 若共享内存已存在使用原来的 tmpshmhdl = shmget(key, shmsize, IPC_CREAT|0666); if (tmpshmhdl == -1) //创建失败 { ret = MYIPC_ParamErr; printf("func shmget() err :%d ", ret); return ret; } *shmhdl = tmpshmhdl; return ret; } //打开共享内存 若共享内存不存在,返回错误 //参数 无意义 可填写0 int IPC_OpenShm(int key, int shmsize, int *shmhdl) { int tmpshmhdl = 0; int ret = 0; // 创建共享内存 // 若共享内存不存在则创建 // 若共享内存已存在使用原来的 tmpshmhdl = shmget(key, 0, 0); if (tmpshmhdl == -1) //打开失败 { ret = MYIPC_NotEXISTErr; //printf("func shmget() err :%d ", ret); return ret; } *shmhdl = tmpshmhdl; return ret; } /*********************************************************************** 功能描述: 创建共享内存 参数说明: shmname [in] 是共享内存名,系统中唯一标志 shmsize [in] 是要创建的共享内存的大小; shmhdl [out] 共享内存的句柄. 返回值: 返回0函数执行成功;非0返回错误码 ************************************************************************/ int IPC_CreatShmBySeedName(char *shmseedfile, int shmsize, int *shmhdl) { if(shmflag == 0) //判断接口中共享内存key是否已经存在 { shmkey = ftok(shmseedfile, 'c'); if (shmkey == -1) { perror("ftok"); return -1; } shmflag = 1; } //创建共享内存 *shmhdl = shmget(shmkey,shmsize,IPC_CREAT|0666); if (*shmhdl == -1) //创建失败 return -2; return 0; } /*********************************************************************** 功能描述: 关联共享内存 参数说明: shmhdl [in] 共享的句柄 mapaddr [out] 共享内存首地址 返回值: 返回0函数执行成功;非0返回错误码 ************************************************************************/ int IPC_MapShm(int shmhdl, void **mapaddr) { void *tempptr = NULL; //连接共享内存 tempptr = (void *)shmat(shmhdl,0,SHM_RND); if ((int)tempptr == -1) //共享内存连接失败 return -1; *mapaddr = tempptr; //导出共享内存首指针 return 0; } /*********************************************************************** 功能描述: 取消共享内存关联 参数说明: unmapaddr [in] 共享内存首地址 返回值: 返回0函数执行成功;非0返回错误码 ************************************************************************/ int IPC_UnMapShm(void *unmapaddr) { int rv; //取消连接共享内存 rv = shmdt((char *)unmapaddr); if (rv == -1) //取消连接失败 return -1; return 0; } /*********************************************************************** 功能描述: 删除共享内存 参数说明: shmhdl [in] 共享的句柄 返回值: 返回0函数执行成功;非0返回错误码 ************************************************************************/ int IPC_DelShm(int shmhdl) { int rv; //删除共享内存 rv = shmctl(shmhdl,IPC_RMID,NULL); if(rv < 0) //删除共享内存失败 return -1; return 0; }
四、安全传输平台项目—密钥协商服务程序
1、客户端服务器密钥协商预说明
在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。