网站建设概合肥最好的网站建设

当前位置: 首页 > news >正文

网站建设概,合肥最好的网站建设,西安网站建设中企建站,服装网站建设公司哪家好Linux文件系统 inode 节点 #xff08;index node#xff09;#xff1a;给每个文件赋予一个称为 i 节点的数据结构。 inode 一开始是存储在硬盘中的#xff0c;只有当文件被打开的时候#xff0c;其对应的 i 节点才加载到内存中。 总结#xff1a; Linux 中#xff0c…Linux文件系统 inode 节点 index node给每个文件赋予一个称为 i 节点的数据结构。 inode 一开始是存储在硬盘中的只有当文件被打开的时候其对应的 i 节点才加载到内存中。 总结 Linux 中用户态通过读写文件的 Api 进行系统调用在内核态中上层是虚拟文件操作系统 VFS它为用户态提供统一接口屏蔽底层实现细节VFS 层定义了底层具体的文件系统需要实现的接口VFS 层往下对接不同的具体的文件系统如 ext4具体的文件系统再去操作磁盘的文件块信息 Linux 中每个文件对应一个称为 iNode 的数据结构inode中包含了文件的元数据以及若干的块地址信息inode一开始存储在磁盘中当文件被打开时inode节点会被加载到内存当中 每个进程的task_struct中包含files_struct结构体files_struct中又包含一个fd数组fd_arrayfd_array中则包含对应文件的文件操作符filefile文件操作符是通过inode去读写文件的inode中定义了inode_options而具体的底层文件系统则实现了inode_options中定义的对应读写接口的具体方法
管道 Linux 进程间通信方式管道、共享内存、信号量、消息队列 ① 匿名管道 ② 命名管道 管道的实现 一个文件可以同时被多个进程访问 所以我们可以使用文件来实现进程间的通信管道就是基于文件系统来实现的。 实现进程和其子进程之间的管道通信 父进程在复制子进程时会把父进程的相关信息全部拷贝过来其中就包括file_struct结构体而这个结构体中就包含了文件读写inode的两个文件描述符一个 file_0 只读 一个 file_1 只写由于是复制的所以父子进程的这俩文件描述符是指向的同一个文件的inode。 此时把父进程的 file_0 只读文件描述符 close 掉把子进程的 file_1 只写文件描述符 close 掉父进程只保留只写文件描述符子进程只保留只读文件描述符这样父进程就可以和子进程通信了父进程只写子进程只读半双工。 匿名管道的实现 匿名管道底层实现 匿名管道通过虚拟文件系统 VFS 调用底层的 pipefs 内存文件系统也就是说底层实现是基于 pipefs 文件系统的。 pipefs 文件系统的数据结构https://www.processon.com/view/link/62822757e401fd36f6bcc5dd 管道在内存中的实现本质就是一段内核 buffer 内存不同的文件操作符一个读一个写对这段 buffer 进行读写操作。 关于 ps -ef | grep systemd 命令背后的匿名管道的底层实现数据结构 命名管道底层实现流程图 总结 管道是基于文件系统来实现的也就是多个进程对同一个文件进行读写来实现进程间通信 进程和子进程之间的管道通信父进程在fork子进程时会把父进程相关信息全部拷贝过来其中包括file_struct结构体file_struct中包含了文件读写inode的两个文件描述符一个file_0只读一个file_1只写由于是复制的所以父子文件的这俩文件描述符是指向同一个文件的inode, 此时把父进程的file_0只读 fd 关闭掉然后再把子进程的file_1只写 fd 关闭掉父进程只保留只写 fd 子进程只保留只读 fd 这样父进程就可以和子进程进行通信了父进程写子进程读。 匿名管道的虚拟文件系统 VFS 对应的底层文件系统实现是基于 pipefs 内存文件系统 管道在内存中的实现本质就是一段内核 buffer 内存不同的文件操作符一个读一个写对这段 buffer 进行读写操作。 用户态read/write → 内核态 VFStask_struct→ files_struct → fd_array → fds[0] fds[1] → file0 file1 → file_opts → inode → pipe_inode_info → pipe_bufs
共享内存 shared memory 创建共享内存 shmget - allocates a System V shared memory segment #include sys/ipc.h #include sys/shm.h // 返回根据 key 生成的 shmid int shmget(key_t key, size_t size, int shmflg); 参数含义 key唯一标识新创建的共享内存 size共享内存的大小向上取整成PAGE_SIZE的倍数 shmflg一些标志信息 IPC_CREAT根据 key 判断对应的共享内存段是否存在如果不存在则创建如果存在则返回已经存在的共享内存段 IPC_EXCL 和 IPC_CREAT 一起用如果已经存在 key 对应的共享内存 则失败 读写权限信息
映射共享内存 shmat — 映射共享内存到进程的虚拟地址空间返回映射的虚拟内存段的起始地址shmdt — 解除映射如果成功返回 0否则返回 -1 #include sys/types.h #include sys/shm.hvoid *shmat(int shmid, const void *shmaddr, int shmflg); int shmdt(const void *shmaddr); 参数含义 shmid共享内存的唯一标识 id即填入由 shmget 函数返回的值shaddr: 内存映射起始地址如果是NULL的话内核会分配shaflg是一组标志位通常为0。 注意创建和映射共享内存操作只是在内核中维护一些数据结构并没有真的分配物理内存。真正分配物理内存是在访问这块虚拟内存地址中的数据发生缺页异常时由缺页异常处理程序维护进程页表中的虚拟页号和物理页号的映射关系的。 这里进程 A 和进程 B 访问的是同一块物理内存上的相同的物理页。 参考代码 共享内存的底层原理是基于 tmpfs 文件系统https://www.processon.com/view/link/6277c3921e085327716f5971 总结 共享内存的原理不同进程的虚拟内存地址会映射到相同的物理内存上这样两个进程通过访问同一块物理内存达到通信的目的。(一般情况下不同进程的虚拟地址是映射到不同物理地址的) 在创建共享内存时并没有真的分配物理内存真的分配是进程在读、写数据的时候发生缺页异常由缺页异常处理程序分配共享内存物理内存的页号到进程的虚拟页表中 共享内存的底层原理是基于 tmpfs 文件系统 Linux中一切皆文件
问题mmap 内存映射和 shm 共享内存有什么区别 Linux 中的内存映射是指将一块虚拟地址内存空间和一个文件对象关联起来以初始化这块虚拟内存的内容文件对象可以是一个普通磁盘文件也可以是一个匿名文件一块只包含二进制零的物理内存mmap 内存映射时被映射的对象可以是一个磁盘文件也可以是一个请求二进制零的匿名对象。如果是前者在发生缺页异常时缺页异常处理程序除了需要维护页表外还需要将磁盘文件内容加载到物理内存中如果是后者则就相当于将一块物理内存和虚拟内存进行映射。shm 共享内存映射是直接每个进程将虚拟内存映射到同一块物理内存不涉及到磁盘文件。shm 保存在物理内存这样读写的速度要比磁盘要快但是存储量不是特别大。所以可以简单的认为 mmap 主要是用于映射磁盘文件的而 shm 是直接用于映射物理内存的mmap 有一个好处是把文件保存在磁盘上当设备机器重启时这个文件还保存了操作系统同步的映像所以 mmap 不会丢失但是 shm 就会丢失。 信号量 在一个进程内多个线程同时更新共享资源有数据并发安全问题解决方案有 ① 原子操作② 锁机制 - 管程③ 信号量 多个进程同时更新共享内存共享资源也有数据安全问题解决方案信号量 IPC 的信号量 (semaphore) 原理思想和并发编程中的信号量是一样的但是两者的实现完全不同 IPC 的信号量实现很复杂是在内核态中实现的而并发编程中的信号量是在用户态实现基于原子操作实现 IPC 的信号量是操作系统层面用于解决多个进程之间的共享内存并发读写问题并发编程中的信号量用于解决同一个进程的多个线程之间的共享资源读写问题
一个是在内核态实现的一个是应用程序代码中实现的。 参考代码 消息队列 创建消息队列msgget - get a System V message queue identifier #include sys/types.h #include sys/ipc.h #include sys/msg.hint msgget(key_t key, int msgflg); // 函数返回返回新创建的消息队列的 id参数含义 key唯一标识新创建的消息队列 msgflg一些标志信息 IPC_CREAT根据 key 判断对应的共享内存段是否存在如果不存在则创建如果存在则返回已经存在的共享内存段 IPC_EXCL和 IPC_CREAT 一起用如果已经存在 key 对应的共享内存则失败 读写权限信息
发送和接收消息msgsnd, msgrcv - System V message queue operations #include sys/types.h #include sys/ipc.h #include sys/msg.hint msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); // 返回值成功返回0失败返回-1 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtype, int msgflg); // 成功返回接收个数失败返回-1msqid: 由msgget函数返回的消息队列的标识符msgp: 消息缓冲区指针指针指向准备发送/接收的消息msgflg: 为0表示阻塞方式设置IPC_NOWAIT表示非阻塞方式异步接发消息msgsz: 是msgp指向的消息长度这个长度不含保存消息类型的那个long int长整型msgtype: 等于0那么读取消息队列中的第一条消息 大于0那么读取消息队列中的第 msgtype 条消息这里是读取类型等于msgtype的第一条消 小于0那么读取小于等于msgtype绝对值最小的 msgtype 的消息 参考代码 int main() {int mq_id get_mq_id(); struct msg_buffer buffer;printf(enter message type: ); scanf(%d, buffer.mtype);printf(enter message contenit:);scanf(%s, buffer.mtext);int len strlen(buffer.mtext) 1;if (msgsnd(mq_id, buffer, len, IPC_NOWAIT) -1) { perror(fail to send message.);exit(1); }return 0; }#include string.h #include mq.h int main() {int mq_id get_mq_id(); struct msg_buffer buffer; int type;scanf(%d, type);if (msgrcv(mq_id, buffer, 1024, type, IPC_NOWAIT) -1) { perror(fail to recv message.);exit(1); }printf(received message type : %d, text: %s, \n, buffer.mtype, buffer.mtext); return 0; }