网站模板下载大全,网站后台模板 jquery,wdcp搭建网站,ps做专业网站文章目录 POSIX共享内存shm_openftruncatemmapPOXIS共享内存文件的位置munmapclose POXIS信号量POXIS信号量的共同操作sem_waitsem_postsem_getvaluesem_t有名信号量sem_opensem_closesem_unlink 无名信号量sem_init - 初始化信号量sem_destroy - 销毁信号量 多进程注意事项问题… 文章目录 POSIX共享内存shm_openftruncatemmapPOXIS共享内存文件的位置munmapclose POXIS信号量POXIS信号量的共同操作sem_waitsem_postsem_getvaluesem_t有名信号量sem_opensem_closesem_unlink 无名信号量sem_init - 初始化信号量sem_destroy - 销毁信号量 多进程注意事项问题一问题二 封装**有名信号量的封装** 无名信号量的封装 --对多线程使用的封装POSIX共享内存的封装 关于其原理有许多其他博客讲过这里不再赘述主要是编者自己写了一份封装以及一些使用共享内存需要注意事项。 POSIX共享内存
关键函数
shm_open int shm_open(const char *name, int oflag, mode_t mode); name是共享内存的名字因为其实POXIS的共享内存是由文件映射的这个名字是最好不要和已有共享内存的名字的重复否则用的就是同一个共享内存了 oflag类似于打开文件的标志一般用 O_CREAT | O_TRUNC | O_RDWR |O_EXCL等与文件一样的使用权限符。 * O_CREAT 创建共享内存如果已经存在就打开 * O_TRUNC 打开之后会清空这个文件的意思这里就是指清空共享内存的内容 * O_EXCL : 一般用于打开时检测是否已经存在了存在就返回错误。 mode用来表示你所创建的共享内存的权限。因为前面说过了这里其实是文件的映射可以传0666一样要减去umask 返回值是一个文件描述符是搭配后面映射到内存地址空间的。小于0自然就是失败了这和打开文件一样需要判断。 oflag参数与open函数的flags一样必须含有O_RDONLY或O_RDWR标准。 mode参数与open函数的mode一样是指定权限位。如果没有指定O_CREAT标志那么该参数可以指定为0。
ftruncate int ftruncate(int fd, off_t length); fd: 是已打开文件的文件描述符 length: 是指定的新文件大小。 函数的返回值表示操作的成功或失败。如果成功返回值为0如果失败返回值为-1并设置相应的错误码来指示错误类型。 一般搭配mmap使用一般来说
mmap void *mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset); addr指定映射区域的起始地址。通常设置为NULL让系统选择地址。length映射区域的长度以字节为单位。通常不超过你上面设置的大小prot映射区域的保护标志指定映射区域的内存访问权限可以是PROT_READ、PROT_WRITE和PROT_EXEC的组合。//EXEC是可执行程序的意思flags指定映射的类型和其他选项例如MAP_SHARED或MAP_PRIVATE。 MAP_SHARED 当使用MAP_SHARED标志时映射区域和原始文件或其他对象是共享的。对映射区域的写操作会反映到原始文件中即写操作会更新文件的内容。同样如果其他进程修改了共享文件的内容这些修改也会反映到映射区域中。这种映射方式允许多个进程通过访问相同的映射区域来共享数据。 MAP_PRIVATE 当使用MAP_PRIVATE标志时映射区域是私有的。对映射区域的写操作不会反映到原始文件中。相反它们会创建一个映射区域的私有副本即所谓的写时复制copy-on-write行为。这意味着初始时映射区域和原始文件的内容是共享的但一旦对映射区域进行写操作系统会在需要时复制相应的页面以确保私有映射区域中的修改不会影响原始文件或其他进程对同一文件的映射。 fd文件描述符通常是由open或shm_open返回的文件描述符用于指定要映射的文件。offset从文件开头起算的偏移量以字节为单位指定映射区域的起始位置。
返回值映射在你内存地址空间的并且已经计算过偏移量的地址。
做完上面三步你就可以直接使用这个共享内存了。
POXIS共享内存文件的位置
如果你向查看你创建的共享内存文件以及信号量ls /dev/shm
munmap int munmap(void addr[.length], size_t length); addr指向要解除映射的内存区域的起始地址的指针。这个地址应该是之前 mmap 调用返回的指针。length要解除映射的内存区域的长度以字节为单位。
close int close(int fd); 不多介绍之间你创建打开文件是打开了文件描述符的。 为了防止文件描述符泄露是需要你关闭的。经过测试你在映mmap之后呢关闭不影响使用的 POXIS信号量
分为有名信号量与无名信号量
POXIS信号量的共同操作
sem_wait int sem_wait(sem_t *sem); 取走信号量 除此之外还有 sem_trywait等函数课自行查阅手册。
sem_post int sem_post(sem_t *sem); 增加信号量 sem_getvalue int sem_getvalue(sem_t *sem, int *sval); 查询信号量值 sem_t
信号量类型
有名信号量
推荐多进程不同进程之间使用有名信号量
sem_open sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value); name是一个标识信号量的字符串。oflag用来确定是创建信号量还是连接已有的信号量。 oflag的参数可以为0O_CREAT或O_EXCL如果为0表示打开一个已存在的信号量如果为O_CREAT表示如果信号量不存在就创建一个信号量如果存在则打开被返* 回此时mode和value都需要指定如果为O_CREAT|O_EXCL表示如果信号量存在则返回错误。mode 用于创建信号量时指定信号量的权限位和open函数一样包括S_IRUSR(用户读)、S_IWUSR用户写、S_IRGRP组读、S_IWGRP组写、S_IROTH、S_IWOTH。//其实和0666的文件权限一个用法你可以直接传八进制一样受umaskvalue 表示创建信号量时信号量的初始值。
sem_close int sem_close(sem_t *sem); 关闭信号量如果函数执行成功则返回0如果失败则返回-1。 sem_unlink int sem_unlink(const char *name); 如果函数成功则返回 0如果失败则返回 -1 并设置 errno 以指示错误原因。 **引用计数调用 sem_unlink 并不会立即销毁信号量对象。只有在最后一个通过 sem_open 打开该信号量的进程调用 sem_close 后信号量对象才会被销毁。如果还有其他进程打开了该信号量则 sem_unlink 只是从文件系统中删除名称信号量对象仍然存在。 持久性通过 sem_open 创建的信号量默认是持久的这意味着即使创建它们的进程已经终止信号量仍然存在。sem_unlink 用于删除这些持久的信号量。
错误处理如果尝试删除一个不存在的信号量sem_unlink 将返回 -1 并设置 errno 为 ENOENT。**
无名信号量
一般只在多线程使用因为有名信号量在多线程中使用其实更容易操作。 为什么 因为无名信号量一旦创建那就算是在父子进程之间也是各自有一份这样就导致没法使用同一个信号量。除非你创建在共享内存那就比较麻烦了。 但是线程的优势就在于共用一块进程地址空间
sem_init - 初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value); sem指向要初始化的信号量的指针。 pshared决定信号量的作用域。如果pshared为0则信号量用于线程间的同步如果pshared不为0则信号量用于进程间的同步。 value信号量的初始值。
sem_destroy - 销毁信号量 int sem_destroy(sem_t *sem); sem指向已初始化的信号量的指针 多进程注意事项
问题一
编者在使用多进程时父进程播种想用每个子进程进程生成随机数。 但是生成的随机数所有子进程都是一样的原因是随机数种子两个子进程用的都是同一个。
编者一开始想到这个问题于是在各自子进程创建之处播种。 但是依然不行。原因是CPU执行速度过于快可能在你播种之处c语言的time是秒级别的。会更新不上导致种子一样。
解决办法srand(time(NULL)%getpid());//模上对应的pid或者和pid做一些运算来独特种子。
问题二
当你在使用多进程对一个文件进行读写的时候。 比如显示器尤其是你在写的时候你虽然使用了信号量来保证了读写顺序的问题。 但是是有语言级别的缓冲区的你在写的时候仅仅只是写入了语言缓冲区而这个时候如果你就解除信号量的限制让其他进程来进行读写。 你这个进程的读写数据就依旧留存在语言缓冲区也就是进程地址空间的并没有真正写入文件。 当你有多个进程如此那么很有可能就会出现一些无法预料的问题。
封装
有名信号量的封装
#include stdio.h
#include stdlib.h
#include unistd.h
#include sys/sem.h
#include fcntl.h
#include semaphore.h
#include string//信号量的封装
class Sem{sem_t* _sem;std::string _pathname;public:Sem( int Soruce_InitNums 0,std::string lpathname Sem_Name,int mode O_CREAT | O_RDWR ,int mod 0666){_pathname lpathname;_sem sem_open(lpathname.c_str(),mode,mod,Soruce_InitNums);}//放入资源void Push(){sem_post(_sem);}//拿走资源void Get(){sem_wait(_sem);}int Val(){int val;sem_getvalue(_sem,val);return val;}std::string name(){return _pathname;}~Sem(){sem_close(_sem);//最后一个信号量释放时这样就可以删除因为如果有信号量的链接是不会上删除的。if (sem_unlink(_pathname.c_str()) -1) { perror(sem_unlink); } }//禁用复制重载和拷贝Sem(Sem) delete;Sem operator(Sem) delete;
};无名信号量的封装 --对多线程使用的封装 #include pthread.h
#include semaphore.h//信号量的封装
class Sem{sem_t _sem;public://多进程的无名信号量编者就不实现了Sem( int Soruce_InitNums 0,int process_share 0){sem_init(_sem,process_share,Soruce_InitNums);}//放入资源void Push(){sem_post(_sem);}//拿走资源void Get(){sem_wait(_sem);}~Sem(){sem_destroy(_sem);}//禁用复制重载和拷贝Sem(Sem) delete;Sem operator(Sem) delete;
};
POSIX共享内存的封装
#include Common.hpp
#include fcntl.h
#include sys/mman.h
#include unistd.h
#include iostreamclass ShareMemory{int _shm_fd;char* _shm_ptr;int _size;std::string _pathname;//表示可以挂接bool _attach 0 ;public:char* Ptr_ToShm(){return _shm_ptr;}//提供默认共享内存的开创和挂接读写方式处理认为时可执行ShareMemory(std::string lpathname pathname,int mode O_RDWR | O_CREAT,int mod 0777,int lshmSize shmSize){_pathname lpathname;_shm_fd shm_open(lpathname.c_str(),mode,mod);if(_shm_fd0){perror(shm_open);exit(errno);}int _size lshmSize;if (ftruncate(_shm_fd, _size)){perror(ftruncate error);exit(-1);}_shm_ptr (char*)mmap(NULL,_size,PROT_READ|PROT_WRITE,MAP_SHARED,_shm_fd,0);close(_shm_fd);_attach 1;if(_shm_ptrMAP_FAILED){perror(mmap);} }void change_size(int length){if (ftruncate(_shm_fd, _size length)){perror(ftruncate error);exit(-1);}}//如果你不满足默认挂接这里给你提供接口自行挂接,同时你可以借助这个函数//且如果你传输的参数比现有的空间更大那么会自动进行给你重新调整共享内存大小void* Map_POSIX(void* addr, size_t length, int prot, int flags,off_t offset){if(offset length _size){change_size(_size);}if(_attach){return _shm_ptr (char*)mmap(addr,length,prot,flags,_shm_fd,offset);}else{std::cerr还未创建/打开共享内存\n;}return nullptr;}~ShareMemory(){//解除映射并且关闭对象if(munmap(_shm_ptr,_size)-1){perror(mumap);}//有关联时无法删除因此每次析构都调用不影响使用也可以防止忘记if(shm_unlink(_pathname.c_str())-1){perror(shm_unlink);}}
};