网站seo优化主要有哪些手段seo批量建站
1. 从应用层看shm msg sem的区别与联系
操作\通信方式 共享内存 消息队列 信号量 申请资源 使用 shmget
函数申请共享内存段,指定内存段的大小和访问权限等参数。使用 msgget
函数创建或获取一个消息队列,指定消息队列的标识符和访问权限等参数。使用 semget
函数创建或获取一个信号量集,指定信号量集的标识符和信号量数量等参数。操作资源 使用 shmat
函数将共享内存段附加到当前进程的地址空间,之后可以通过指针进行常规读写操作。操作完成后,使用shmdt
函数将共享内存段从当前进程的地址空间分离。使用 msgsnd
函数向消息队列发送消息,指定消息队列的标识符、消息的类型和内容等参数。使用msgrcv
函数从消息队列接收消息,指定消息队列的标识符、消息的类型和接收缓冲区等参数。使用 semop
函数对信号量集进行操作,可以执行P操作(等待信号量)、V操作(释放信号量)等。释放资源 使用 shmctl
函数对共享内存段进行控制操作,如删除共享内存段(指定IPC_RMID
命令)。使用 msgctl
函数对消息队列进行控制操作,如删除消息队列(指定IPC_RMID
命令)。使用 semctl
函数对信号量集进行控制操作,如删除信号量集(指定IPC_RMID
命令)。
因为遵循着同一个标准,所以三者的操作都大差不。
并且,Linux底层对整个资源的管理也有很多相似之处。
我们一直强调“先描述、再组织”,现在来看一下这三种通信方式的组织结构是不是差不多。
可以直接在机器上通过man xxxctl 查看!
man shmctl
共享内存对应属性的数据模块
消息队列:
信号量:
三者的第一个成员都是
struct ipc_perm
结构,这个结构用于存放当前资源的相关权限以及标识符!例如其中的__key
表示资源在系统中的编号,另外还有对应的uid
表示持有当前资源的用户,三者除了有struct ipc_perm
结构外,还有一些其他成员也基本类似。
在单独学习某一种资源的时候,xxxget就是用key_t 类型的 key去生成一块资源。
同时,key也变成了区分ipc资源的唯一标识符。
由此不难看出,整个操作系统几乎都是以同样的方式在管理整个IPC,三种不同的资源遵守同样的组织方法。
在之前的共享内存的代码中,可以通过如下来获取各个IPC资源的信息:
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>int _shmid; // 假设已经定义并初始化void ShmMeta()
{struct shmid_ds buffer; // 系统提供的数据类型int n = shmctl(_shmid, IPC_STAT, &buffer);if (n < 0) return;std::cout << "Last attach time: " << buffer.shm_atime << std::endl;std::cout << "Process ID of creator: " << buffer.shm_cpid << std::endl;std::cout << "Last change time: " << buffer.shm_ctime << std::endl;std::cout << "Number of current attaches: " << buffer.shm_nattch << std::endl;std::cout << "Key: " << buffer.shm_perm.key << std::endl; // 使用标准的成员名
}
2. 从内核角度看三者的联系与区别
kernel_thread identifier - Linux source code v6.12.6 - Bootlin Elixir Cross Referencer
可用以上链接查linux源码。
现在来学习一下管理IPC资源的内部结构
在Linux 2.6版本的内核中,使用ipc_ids:
ipc_ids的名字是否与shm_ids,sem_ids,msg_ids联系很大?
每个ipc_ids里有一个ipc_id_ary,这个结构体的定义也被清晰写出,是一个size+一个柔性数组构成,柔性数组里记录的主要是ipc_perm的内容。
这个结构中的struct kern_ipc_perm
实际上就是应用层的struct ipc_perm:
struct kern_ipc_perm {key_t key;spinlock_t lock;int deleted;uid_t uid;gid_t gid;uid_t cuid;gid_t cgid;mode_t mode;unsigned long seq;void *security;
};
可以说,内核的ipc_id_ary数组就就记录下了所有ipc资源。
我们使用kern_ipc_perm就可以去类似的构建shm_perm等等
struct shmid_kernel {struct kern_ipc_perm shm_perm;struct file *shm_file;int id;unsigned long shm_nattch;unsigned long shm_segsz;time_t shm_atim;time_t shm_dtim;time_t shm_ctim;pid_t shm_cprid;pid_t shm_lprid; };
因为每一个
struct ipc_ids
对象都有一个struct kern_ipc_perm
成员,这个指针指向着一个动态开辟的空间(柔性数组)
又因为C语言中首元素地址就是整个结构体的地址
故:假设p就是ipc_id_ary
此时操作系统就可以通过管理
struct kern_ipc_perm *p
统一对三个资源进行管理,而因为struct kern_ipc_perm
中含有相关的成员,例如key
,所以这个key
实际上是一个三个资源共有的成员,也就是说三个资源管理的是同一种key
其实,在CPP的角度,这就是多态的表现。其中
struct kern_ipc_perm
就是基类(父类),struct shmid_kernel
、struct msg_queue
和struct sem_array
就是派生类(子类)
3. 再次理解共享内存
在shm_kernel中,有一个特殊的变量
为什么内存中也会有这个接口?
这是因为,共享内存的本质也是打开一个文件,但是不调用文件相关的接口,所以快。
既然如此,被 共享内存 在 内存 中打开的文件就需要将对应的地址映射到进程的虚拟地址空间,在进程地址空间结构的
vm_area_struct
中也存在一个结构如下:
这就是我们之前聊的在mm_struct中管理具体的_start和_end的结构体,原来它里面的确存在一个对应的file*结构体,用于接受共享内存打开的内容文件。
通过这个file*指针,就可以将共享内存这个文件映射到进程地址空间 ,因此在shmat的时候,挂接就是挂接到这个file*指针上,进程可以直接使用这个指针!
同样,如果需要将用户打开的文件映射到进程地址空间,可以使用mmap
接口:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
需要注意,如果文件已经映射到了进程的地址空间,但是文件在取消映射之前已经关闭,此时不会自动取消映射,所以关闭文件后还需要手动解除映射,可以使用
munmap
接口取消映射