1062e2e16f
- 重构 api-intro 7 个 Vue 组件为更紧凑的左右布局 - 重构 api-design 相关组件 - 重构 transistor-to-cpu 相关组件 - 统一使用 demo-root -> demo-header -> demo-layout -> info-box 结构 - 扩写文章内容为 MIT 讲义风格
447 lines
10 KiB
Vue
447 lines
10 KiB
Vue
<template>
|
||
<div class="pmf-collab-demo">
|
||
<div class="demo-header">
|
||
<span class="title">进程、内存、文件系统的协作</span>
|
||
<span class="subtitle">三大管理模块如何协同工作</span>
|
||
</div>
|
||
|
||
<div class="demo-content">
|
||
<div class="collab-scene">
|
||
<div class="scene-title">场景选择:</div>
|
||
<div class="scene-buttons">
|
||
<button
|
||
v-for="scene in scenes"
|
||
:key="scene.id"
|
||
:class="['scene-btn', { active: activeScene === scene.id }]"
|
||
@click="activeScene = scene.id"
|
||
>
|
||
{{ scene.icon }} {{ scene.name }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="collab-visualization">
|
||
<div class="vis-container">
|
||
<!-- 进程区域 -->
|
||
<div class="zone process-zone">
|
||
<div class="zone-header">
|
||
<span class="zone-icon">🔄</span>
|
||
<span class="zone-name">进程管理</span>
|
||
</div>
|
||
<div class="zone-content">
|
||
<div class="process-list">
|
||
<div
|
||
v-for="proc in processes"
|
||
:key="proc.id"
|
||
class="process-item"
|
||
:class="{ active: proc.id === currentProcessId }"
|
||
>
|
||
<span class="proc-name">{{ proc.name }}</span>
|
||
<span class="proc-pid">PID: {{ proc.pid }}</span>
|
||
<span class="proc-state">{{ proc.state }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 内存区域 -->
|
||
<div class="zone memory-zone">
|
||
<div class="zone-header">
|
||
<span class="zone-icon">🧠</span>
|
||
<span class="zone-name">内存管理</span>
|
||
</div>
|
||
<div class="zone-content">
|
||
<div class="memory-grid">
|
||
<div
|
||
v-for="(block, index) in memoryBlocks"
|
||
:key="index"
|
||
class="memory-block"
|
||
:class="{
|
||
allocated: block.allocated,
|
||
process: block.processId
|
||
}"
|
||
:title="`地址: ${block.address}, 大小: ${block.size}KB`"
|
||
>
|
||
<div v-if="block.allocated" class="block-info">
|
||
{{ getProcessName(block.processId) }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文件系统区域 -->
|
||
<div class="zone filesystem-zone">
|
||
<div class="zone-header">
|
||
<span class="zone-icon">📁</span>
|
||
<span class="zone-name">文件系统</span>
|
||
</div>
|
||
<div class="zone-content">
|
||
<div class="file-tree">
|
||
<div
|
||
v-for="file in files"
|
||
:key="file.id"
|
||
class="file-item"
|
||
:class="{ active: file.id === currentFileId }"
|
||
>
|
||
<span class="file-icon">{{ getIcon(file.type) }}</span>
|
||
<span class="file-name">{{ file.name }}</span>
|
||
<span v-if="file.size" class="file-size">{{
|
||
file.size
|
||
}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="explanation">
|
||
<div class="exp-title">
|
||
{{ currentSceneData.title }}
|
||
</div>
|
||
<div class="exp-content">
|
||
<div
|
||
v-for="(step, index) in currentSceneData.steps"
|
||
:key="index"
|
||
class="exp-step"
|
||
>
|
||
<span class="step-number">{{ index + 1 }}</span>
|
||
<span class="step-text">{{ step }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
|
||
const activeScene = ref('launch')
|
||
const currentProcessId = ref(null)
|
||
const currentFileId = ref(null)
|
||
|
||
const scenes = [
|
||
{
|
||
id: 'launch',
|
||
name: '启动程序',
|
||
icon: '🚀'
|
||
},
|
||
{
|
||
id: 'memory-access',
|
||
name: '内存访问',
|
||
icon: '💾'
|
||
},
|
||
{
|
||
id: 'file-access',
|
||
name: '文件读写',
|
||
icon: '📄'
|
||
},
|
||
{
|
||
id: 'context-switch',
|
||
name: '进程切换',
|
||
icon: '🔄'
|
||
}
|
||
]
|
||
|
||
const processes = ref([
|
||
{ id: 1, name: '浏览器', pid: 1001, state: '运行中' },
|
||
{ id: 2, name: '音乐播放器', pid: 1002, state: '等待中' },
|
||
{ id: 3, name: '代码编辑器', pid: 1003, state: '运行中' }
|
||
])
|
||
|
||
const memoryBlocks = ref([
|
||
{ address: '0x1000', size: 256, allocated: true, processId: 1 },
|
||
{ address: '0x2000', size: 128, allocated: true, processId: 2 },
|
||
{ address: '0x3000', size: 512, allocated: true, processId: 3 },
|
||
{ address: '0x4000', size: 1024, allocated: false, processId: null },
|
||
{ address: '0x5000', size: 512, allocated: false, processId: null },
|
||
{ address: '0x6000', size: 256, allocated: false, processId: null },
|
||
{ address: '0x7000', size: 128, allocated: false, processId: null }
|
||
])
|
||
|
||
const files = ref([
|
||
{ id: 1, name: 'config.json', type: 'json', size: '2KB' },
|
||
{ id: 2, name: 'user_data.db', type: 'db', size: '50MB' },
|
||
{ id: 3, name: 'cache', type: 'folder', size: '' },
|
||
{ id: 4, name: 'song.mp3', type: 'audio', size: '5MB' }
|
||
])
|
||
|
||
const sceneData = {
|
||
launch: {
|
||
title: '场景1:启动程序(浏览器)',
|
||
steps: [
|
||
'1. 用户双击浏览器图标',
|
||
'2. 进程管理创建新进程(PID: 1004)',
|
||
'3. 内存管理分配内存空间(代码段、数据段、堆、栈)',
|
||
'4. 文件系统读取配置文件和缓存数据'
|
||
]
|
||
},
|
||
'memory-access': {
|
||
title: '场景2:程序运行时申请内存',
|
||
steps: [
|
||
'1. 浏览器加载大图片,需要更多内存',
|
||
'2. 进程通过系统调用请求内存(malloc)',
|
||
'3. 内存管理查找可用内存块(如:0x4000)',
|
||
'4. 将内存块标记为"已分配",返回地址给程序'
|
||
]
|
||
},
|
||
'file-access': {
|
||
title: '场景3:保存文件',
|
||
steps: [
|
||
'1. 用户在浏览器点击"保存图片"',
|
||
'2. 进程发起文件写入系统调用',
|
||
'3. 文件系统查找空闲磁盘空间',
|
||
'4. 将数据写入磁盘,更新文件分配表'
|
||
]
|
||
},
|
||
'context-switch': {
|
||
title: '场景4:切换到音乐播放器',
|
||
steps: [
|
||
'1. 用户点击音乐播放器窗口',
|
||
'2. 操作系统暂停浏览器进程',
|
||
'3. 调度器加载音乐播放器进程上下文',
|
||
'4. CPU开始执行音乐播放器代码'
|
||
]
|
||
}
|
||
}
|
||
|
||
const currentSceneData = computed(
|
||
() => sceneData[activeScene.value] || sceneData.launch
|
||
)
|
||
|
||
const getProcessName = (id) => {
|
||
const proc = processes.value.find((p) => p.id === id)
|
||
return proc?.name || '系统'
|
||
}
|
||
|
||
const getIcon = (type) => {
|
||
const icons = {
|
||
json: '📋',
|
||
db: '🗄️',
|
||
folder: '📁',
|
||
audio: '🎵'
|
||
}
|
||
return icons[type] || '📄'
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.pmf-collab-demo {
|
||
border: 1px solid var(--vp-c-divider);
|
||
background: var(--vp-c-bg-soft);
|
||
border-radius: 12px;
|
||
padding: 1.5rem;
|
||
margin: 1.5rem 0;
|
||
}
|
||
|
||
.demo-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.demo-header .title {
|
||
font-weight: 700;
|
||
font-size: 1.1rem;
|
||
}
|
||
.demo-header .subtitle {
|
||
color: var(--vp-c-text-2);
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.scene-buttons {
|
||
display: flex;
|
||
gap: 0.75rem;
|
||
flex-wrap: wrap;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.scene-btn {
|
||
padding: 0.6rem 1rem;
|
||
background: var(--vp-c-bg);
|
||
border: 2px solid var(--vp-c-divider);
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 0.9rem;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.scene-btn:hover {
|
||
border-color: var(--vp-c-brand);
|
||
}
|
||
|
||
.scene-btn.active {
|
||
background: var(--vp-c-brand);
|
||
border-color: var(--vp-c-brand);
|
||
color: white;
|
||
}
|
||
|
||
.collab-visualization {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.collab-visualization {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
.zone {
|
||
border: 2px solid var(--vp-c-divider);
|
||
border-radius: 8px;
|
||
padding: 1rem;
|
||
background: var(--vp-c-bg);
|
||
}
|
||
|
||
.zone-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
margin-bottom: 0.75rem;
|
||
font-weight: 600;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.zone-icon {
|
||
font-size: 1.2rem;
|
||
}
|
||
.zone-name {
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.process-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.process-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0.5rem;
|
||
background: var(--vp-c-bg-soft);
|
||
border-radius: 4px;
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.process-item.active {
|
||
border: 2px solid var(--vp-c-brand);
|
||
background: var(--vp-c-brand-soft);
|
||
}
|
||
|
||
.proc-name,
|
||
.proc-pid,
|
||
.proc-state {
|
||
flex: 1;
|
||
}
|
||
|
||
.proc-state {
|
||
color: var(--vp-c-text-2);
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.memory-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.memory-block {
|
||
aspect-ratio: 2;
|
||
border: 1px solid var(--vp-c-divider);
|
||
border-radius: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 0.75rem;
|
||
position: relative;
|
||
background: var(--vp-c-bg-soft);
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.memory-block.allocated {
|
||
background: var(--vp-c-brand-soft);
|
||
border-color: var(--vp-c-brand);
|
||
}
|
||
|
||
.memory-block.process {
|
||
border: 2px solid var(--vp-c-brand);
|
||
}
|
||
|
||
.block-info {
|
||
font-weight: 600;
|
||
color: var(--vp-c-brand);
|
||
font-size: 0.7rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.file-tree {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.file-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding: 0.5rem;
|
||
background: var(--vp-c-bg-soft);
|
||
border-radius: 4px;
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
.file-item.active {
|
||
border: 2px solid var(--vp-c-brand);
|
||
background: var(--vp-c-brand-soft);
|
||
}
|
||
|
||
.file-name {
|
||
flex: 1;
|
||
}
|
||
.file-size {
|
||
color: var(--vp-c-text-2);
|
||
font-size: 0.7rem;
|
||
}
|
||
|
||
.explanation {
|
||
background: var(--vp-c-bg);
|
||
border: 1px solid var(--vp-c-divider);
|
||
border-radius: 8px;
|
||
padding: 1rem;
|
||
}
|
||
|
||
.exp-title {
|
||
font-weight: 600;
|
||
margin-bottom: 0.75rem;
|
||
font-size: 0.95rem;
|
||
color: var(--vp-c-brand);
|
||
}
|
||
|
||
.exp-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.exp-step {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
font-size: 0.85rem;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.step-number {
|
||
color: var(--vp-c-brand);
|
||
font-weight: 600;
|
||
flex-shrink: 0;
|
||
}
|
||
</style>
|