mirror of
https://github.com/kuhyx/praca_magisterska.git
synced 2026-07-04 12:03:01 +02:00
1010 lines
40 KiB
Python
1010 lines
40 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Generate ALL diagrams for PYTANIE 9: Procesy i wątki (SOI).
|
||
Replaces every ASCII diagram with a monochrome A4-printable PNG (300 DPI).
|
||
"""
|
||
|
||
import matplotlib
|
||
matplotlib.use('Agg')
|
||
import matplotlib.pyplot as plt
|
||
import matplotlib.patches as mpatches
|
||
from matplotlib.patches import FancyBboxPatch
|
||
import numpy as np
|
||
import os
|
||
|
||
DPI = 300
|
||
BG = 'white'
|
||
LN = 'black'
|
||
FS = 8
|
||
FS_TITLE = 11
|
||
FS_SMALL = 6.5
|
||
FS_LABEL = 9
|
||
OUTPUT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'img')
|
||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||
|
||
GRAY1 = '#E8E8E8'
|
||
GRAY2 = '#D0D0D0'
|
||
GRAY3 = '#B8B8B8'
|
||
GRAY4 = '#F5F5F5'
|
||
GRAY5 = '#C0C0C0'
|
||
|
||
|
||
def draw_box(ax, x, y, w, h, text, fill='white', lw=1.2, fontsize=FS,
|
||
fontweight='normal', ha='center', va='center', rounded=True,
|
||
edgecolor=LN, linestyle='-'):
|
||
if rounded:
|
||
rect = FancyBboxPatch((x, y), w, h, boxstyle="round,pad=0.05",
|
||
lw=lw, edgecolor=edgecolor, facecolor=fill,
|
||
linestyle=linestyle)
|
||
else:
|
||
rect = mpatches.Rectangle((x, y), w, h, lw=lw, edgecolor=edgecolor,
|
||
facecolor=fill, linestyle=linestyle)
|
||
ax.add_patch(rect)
|
||
ax.text(x + w/2, y + h/2, text, ha=ha, va=va, fontsize=fontsize,
|
||
fontweight=fontweight, wrap=True)
|
||
|
||
|
||
def draw_arrow(ax, x1, y1, x2, y2, lw=1.2, style='->', color=LN):
|
||
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
|
||
arrowprops=dict(arrowstyle=style, color=color, lw=lw))
|
||
|
||
|
||
def draw_double_arrow(ax, x1, y1, x2, y2, lw=1.2, color=LN):
|
||
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
|
||
arrowprops=dict(arrowstyle='<->', color=color, lw=lw))
|
||
|
||
|
||
def save_fig(fig, name):
|
||
path = os.path.join(OUTPUT_DIR, name)
|
||
fig.savefig(path, dpi=DPI, bbox_inches='tight', facecolor=BG, pad_inches=0.15)
|
||
plt.close(fig)
|
||
print(f" Saved: {path}")
|
||
|
||
|
||
def draw_table(ax, headers, rows, x0, y0, col_widths, row_h=0.4,
|
||
header_fill=GRAY2, row_fills=None, fontsize=FS, header_fontsize=None):
|
||
"""Draw a clean table on axes."""
|
||
if header_fontsize is None:
|
||
header_fontsize = fontsize
|
||
n_cols = len(headers)
|
||
n_rows = len(rows)
|
||
total_w = sum(col_widths)
|
||
|
||
# Header
|
||
cx = x0
|
||
for j, hdr in enumerate(headers):
|
||
draw_box(ax, cx, y0, col_widths[j], row_h, hdr, fill=header_fill,
|
||
fontsize=header_fontsize, fontweight='bold', rounded=False)
|
||
cx += col_widths[j]
|
||
|
||
# Rows
|
||
for i, row in enumerate(rows):
|
||
cy = y0 - (i + 1) * row_h
|
||
cx = x0
|
||
fill = GRAY4 if (i % 2 == 0) else 'white'
|
||
if row_fills and i < len(row_fills):
|
||
fill = row_fills[i]
|
||
for j, cell in enumerate(row):
|
||
fw = 'bold' if j == 0 else 'normal'
|
||
draw_box(ax, cx, cy, col_widths[j], row_h, cell, fill=fill,
|
||
fontsize=fontsize, fontweight=fw, rounded=False)
|
||
cx += col_widths[j]
|
||
|
||
|
||
# ============================================================
|
||
# 1. Process vs Thread comparison table
|
||
# ============================================================
|
||
def gen_process_vs_thread():
|
||
fig, ax = plt.subplots(figsize=(7.5, 4.5))
|
||
ax.set_xlim(0, 10)
|
||
ax.set_ylim(-4.5, 1.5)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Proces vs Wątek — porównanie', fontsize=FS_TITLE, fontweight='bold', pad=10)
|
||
|
||
headers = ['Cecha', 'Proces', 'Wątek']
|
||
col_w = [2.5, 3.5, 3.5]
|
||
rows = [
|
||
['Pamięć', 'Własna, izolowana', 'Współdzielona (heap)'],
|
||
['Tworzenie', '~1–10 ms', '~10–100 μs (100× szybciej)'],
|
||
['Przełączanie', '~1–5 μs (TLB flush)', '~0.1–0.5 μs (10×)'],
|
||
['Komunikacja', 'IPC (pipe, socket, shm)', 'Bezpośrednia (wspólna pam.)'],
|
||
['Izolacja', 'Pełna — awaria izolowana', 'Brak — może zabić proces'],
|
||
['Zastosowanie', 'Bezpieczeństwo, izolacja', 'Wydajność, współdzielenie'],
|
||
]
|
||
draw_table(ax, headers, rows, x0=0.25, y0=0.8, col_widths=col_w, row_h=0.55,
|
||
fontsize=7.5, header_fontsize=FS_LABEL)
|
||
|
||
# Analogy at bottom
|
||
ax.text(5.0, -4.2, 'Analogia: Proces = mieszkanie (własny adres) '
|
||
'Wątek = pokój w mieszkaniu (wspólna kuchnia = heap)',
|
||
ha='center', fontsize=FS, style='italic',
|
||
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
save_fig(fig, 'q9_process_vs_thread.png')
|
||
|
||
|
||
# ============================================================
|
||
# 2. Memory segments layout
|
||
# ============================================================
|
||
def gen_memory_layout():
|
||
fig, ax = plt.subplots(figsize=(6, 5))
|
||
ax.set_xlim(0, 10)
|
||
ax.set_ylim(0, 8)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Segmenty pamięci procesu', fontsize=FS_TITLE, fontweight='bold', pad=10)
|
||
|
||
segments = [
|
||
('STACK ↓', 'zmienne lokalne, adresy\npowrotu (każdy wątek WŁASNY)', GRAY1),
|
||
('...', '(wolna przestrzeń)', 'white'),
|
||
('HEAP ↑', 'malloc/new — dynamiczna\nalokacja (współdzielony)', GRAY4),
|
||
('BSS', 'zmienne globalne\nniezainicjalizowane (zerowane)', GRAY2),
|
||
('DATA', 'zmienne globalne\nzainicjalizowane', GRAY3),
|
||
('TEXT', 'kod maszynowy\n(read-only, współdzielony)', GRAY5),
|
||
]
|
||
bx, bw = 2.0, 2.5
|
||
seg_h = 0.9
|
||
gap = 0.05
|
||
top_y = 7.0
|
||
|
||
for i, (name, desc, color) in enumerate(segments):
|
||
y = top_y - i * (seg_h + gap)
|
||
draw_box(ax, bx, y, bw, seg_h, name, fill=color, fontsize=FS_LABEL,
|
||
fontweight='bold', rounded=False)
|
||
ax.text(bx + bw + 0.3, y + seg_h / 2, desc, fontsize=7.5, va='center')
|
||
|
||
# Address labels
|
||
ax.text(bx - 0.2, top_y + seg_h / 2, 'wysoki\nadres', fontsize=FS_SMALL,
|
||
va='center', ha='right', style='italic')
|
||
bottom_y = top_y - 5 * (seg_h + gap)
|
||
ax.text(bx - 0.2, bottom_y + seg_h / 2, 'niski\nadres', fontsize=FS_SMALL,
|
||
va='center', ha='right', style='italic')
|
||
|
||
# Arrows for growth
|
||
ax.annotate('', xy=(bx - 0.5, top_y - 0.1), xytext=(bx - 0.5, top_y + seg_h + 0.1),
|
||
arrowprops=dict(arrowstyle='->', lw=1.5, color=LN))
|
||
ax.text(bx - 0.9, top_y + 0.4, 'rośnie\nw dół', fontsize=FS_SMALL, ha='center')
|
||
|
||
heap_y = top_y - 2 * (seg_h + gap)
|
||
ax.annotate('', xy=(bx - 0.5, heap_y + seg_h + 0.1), xytext=(bx - 0.5, heap_y - 0.1),
|
||
arrowprops=dict(arrowstyle='->', lw=1.5, color=LN))
|
||
ax.text(bx - 0.9, heap_y + 0.5, 'rośnie\nw górę', fontsize=FS_SMALL, ha='center')
|
||
|
||
save_fig(fig, 'q9_memory_layout.png')
|
||
|
||
|
||
# ============================================================
|
||
# 3. Process states diagram
|
||
# ============================================================
|
||
def gen_process_states():
|
||
fig, ax = plt.subplots(figsize=(7, 3.5))
|
||
ax.set_xlim(0, 12)
|
||
ax.set_ylim(0, 5)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Stany procesu — diagram przejść', fontsize=FS_TITLE,
|
||
fontweight='bold', pad=10)
|
||
|
||
states = {
|
||
'NEW': (1.0, 2.5),
|
||
'READY': (3.5, 2.5),
|
||
'RUNNING': (6.5, 2.5),
|
||
'BLOCKED': (6.5, 0.5),
|
||
'TERMINATED': (10.0, 2.5),
|
||
}
|
||
fills = {
|
||
'NEW': GRAY4, 'READY': GRAY1, 'RUNNING': GRAY3,
|
||
'BLOCKED': GRAY2, 'TERMINATED': GRAY5,
|
||
}
|
||
bw, bh = 1.8, 0.9
|
||
for name, (x, y) in states.items():
|
||
draw_box(ax, x, y, bw, bh, name, fill=fills[name], fontsize=FS_LABEL,
|
||
fontweight='bold')
|
||
|
||
# Transitions
|
||
transitions = [
|
||
('NEW', 'READY', 'admit'),
|
||
('READY', 'RUNNING', 'dispatch\n(scheduler)'),
|
||
('RUNNING', 'TERMINATED', 'exit'),
|
||
('RUNNING', 'BLOCKED', 'I/O wait'),
|
||
]
|
||
for src, dst, label in transitions:
|
||
sx, sy = states[src]
|
||
dx, dy = states[dst]
|
||
if sy == dy: # horizontal
|
||
draw_arrow(ax, sx + bw, sy + bh/2, dx, dy + bh/2, lw=1.5)
|
||
mx = (sx + bw + dx) / 2
|
||
ax.text(mx, sy + bh/2 + 0.25, label, fontsize=FS_SMALL,
|
||
ha='center', va='bottom')
|
||
else: # vertical
|
||
draw_arrow(ax, sx + bw/2, sy, dx + bw/2, dy + bh, lw=1.5)
|
||
ax.text(sx + bw + 0.2, (sy + dy + bh) / 2, label,
|
||
fontsize=FS_SMALL, ha='left', va='center')
|
||
|
||
# BLOCKED → READY
|
||
bx, by = states['BLOCKED']
|
||
rx, ry = states['READY']
|
||
ax.annotate("", xy=(rx + bw/2, ry), xytext=(bx - 0.3, by + bh/2),
|
||
arrowprops=dict(arrowstyle='->', lw=1.5, color=LN,
|
||
connectionstyle='arc3,rad=0.3'))
|
||
ax.text(3.5, 0.7, 'I/O done', fontsize=FS_SMALL, ha='center')
|
||
|
||
# RUNNING → READY (preemption)
|
||
rux, ruy = states['RUNNING']
|
||
draw_arrow(ax, rux, ruy + bh, rx + bw, ry + bh, lw=1.2)
|
||
ax.text(5.0, 3.7, 'preempt /\ntimeout', fontsize=FS_SMALL, ha='center')
|
||
|
||
save_fig(fig, 'q9_process_states.png')
|
||
|
||
|
||
# ============================================================
|
||
# 4. Thread structure within process
|
||
# ============================================================
|
||
def gen_thread_structure():
|
||
fig, ax = plt.subplots(figsize=(8, 4.5))
|
||
ax.set_xlim(0, 10)
|
||
ax.set_ylim(0, 6)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Wątki wewnątrz procesu (PID=42)', fontsize=FS_TITLE,
|
||
fontweight='bold', pad=10)
|
||
|
||
# Shared memory region
|
||
draw_box(ax, 0.5, 3.5, 9.0, 1.8, '', fill=GRAY1, rounded=False, lw=2)
|
||
ax.text(5.0, 5.0, 'WSPÓŁDZIELONE', fontsize=FS, fontweight='bold', ha='center')
|
||
|
||
labels_shared = ['TEXT', 'DATA', 'BSS', 'HEAP', 'pliki', 'PID']
|
||
for i, lab in enumerate(labels_shared):
|
||
x = 1.0 + i * 1.4
|
||
draw_box(ax, x, 3.8, 1.1, 0.6, lab, fill=GRAY3, fontsize=FS,
|
||
fontweight='bold', rounded=False)
|
||
ax.text(x + 0.55, 4.6, lab, fontsize=FS_SMALL, ha='center', color='#555555')
|
||
|
||
# Per-thread regions
|
||
draw_box(ax, 0.5, 0.5, 9.0, 2.7, '', fill='white', rounded=False, lw=2,
|
||
linestyle='--')
|
||
ax.text(5.0, 2.95, 'PRYWATNE (każdy wątek)', fontsize=FS, fontweight='bold',
|
||
ha='center')
|
||
|
||
for i in range(3):
|
||
x = 1.0 + i * 3.0
|
||
tid = i + 1
|
||
draw_box(ax, x, 0.7, 2.3, 2.0, '', fill=GRAY4, rounded=False)
|
||
ax.text(x + 1.15, 2.4, f'Wątek {tid}', fontsize=FS_LABEL,
|
||
fontweight='bold', ha='center')
|
||
items = [f'stos_{tid}', f'rejestry_{tid}', f'PC_{tid}', f'TID={40+tid}']
|
||
for j, item in enumerate(items):
|
||
ax.text(x + 1.15, 2.0 - j * 0.35, item, fontsize=FS_SMALL,
|
||
ha='center', family='monospace')
|
||
|
||
save_fig(fig, 'q9_thread_structure.png')
|
||
|
||
|
||
# ============================================================
|
||
# 5. PCB structure
|
||
# ============================================================
|
||
def gen_pcb_structure():
|
||
fig, ax = plt.subplots(figsize=(5, 3.5))
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 5.5)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('PCB (Process Control Block)', fontsize=FS_TITLE,
|
||
fontweight='bold', pad=10)
|
||
|
||
fields = [
|
||
('PID', '42'),
|
||
('Stan', 'READY / RUNNING / BLOCKED'),
|
||
('Rejestry CPU', 'EAX, EBX, ESP, EIP ...'),
|
||
('Tablice stron', 'mapowanie wirtualne → fizyczne'),
|
||
('Otwarte pliki', 'fd[0], fd[1], fd[2] ...'),
|
||
('Priorytety', 'nice value, scheduling class'),
|
||
('Statystyki', 'CPU time, I/O count'),
|
||
]
|
||
|
||
top_y = 4.8
|
||
for i, (field, value) in enumerate(fields):
|
||
y = top_y - i * 0.55
|
||
draw_box(ax, 0.5, y, 2.2, 0.45, field, fill=GRAY2, fontsize=FS,
|
||
fontweight='bold', rounded=False)
|
||
draw_box(ax, 2.7, y, 4.5, 0.45, value, fill=GRAY4, fontsize=FS,
|
||
rounded=False)
|
||
|
||
ax.text(4.0, 0.3, 'Context switch = zapisz PCB starego → wczytaj PCB nowego',
|
||
fontsize=FS_SMALL, ha='center', style='italic')
|
||
|
||
save_fig(fig, 'q9_pcb_structure.png')
|
||
|
||
|
||
# ============================================================
|
||
# 6. Speed comparison
|
||
# ============================================================
|
||
def gen_speed_comparison():
|
||
fig, axes = plt.subplots(1, 2, figsize=(9, 3.5))
|
||
fig.suptitle('Szybkość — procesy vs wątki (benchmarki Linux)',
|
||
fontsize=FS_TITLE, fontweight='bold')
|
||
|
||
# Left: creation
|
||
ax = axes[0]
|
||
ops = ['fork()\n(nowy proces)', 'pthread_create()\n(nowy wątek)']
|
||
times = [3.0, 0.05] # ms
|
||
colors = [GRAY3, GRAY1]
|
||
bars = ax.barh(ops, times, color=colors, edgecolor=LN, height=0.5, linewidth=1.2)
|
||
ax.set_xlabel('Czas [ms]', fontsize=FS)
|
||
ax.set_title('Tworzenie', fontsize=FS_LABEL, fontweight='bold')
|
||
ax.set_xlim(0, 4.5)
|
||
for bar, t in zip(bars, times):
|
||
ax.text(bar.get_width() + 0.1, bar.get_y() + bar.get_height()/2,
|
||
f'{t} ms', va='center', fontsize=FS)
|
||
ax.text(2.5, -0.6, '~100× szybciej', fontsize=FS, ha='center',
|
||
fontweight='bold', transform=ax.get_xaxis_transform())
|
||
ax.tick_params(labelsize=FS)
|
||
|
||
# Right: context switch
|
||
ax = axes[1]
|
||
ops2 = ['Proces→Proces\n(TLB flush)', 'Wątek→Wątek\n(TLB warm)']
|
||
times2 = [3000, 300] # ns
|
||
bars2 = ax.barh(ops2, times2, color=colors, edgecolor=LN, height=0.5, linewidth=1.2)
|
||
ax.set_xlabel('Czas [ns]', fontsize=FS)
|
||
ax.set_title('Przełączanie kontekstu', fontsize=FS_LABEL, fontweight='bold')
|
||
ax.set_xlim(0, 4500)
|
||
for bar, t in zip(bars2, times2):
|
||
ax.text(bar.get_width() + 50, bar.get_y() + bar.get_height()/2,
|
||
f'{t} ns', va='center', fontsize=FS)
|
||
ax.text(2500, -0.6, '~10× szybciej', fontsize=FS, ha='center',
|
||
fontweight='bold', transform=ax.get_xaxis_transform())
|
||
ax.tick_params(labelsize=FS)
|
||
|
||
fig.tight_layout(rect=[0, 0.05, 1, 0.92])
|
||
save_fig(fig, 'q9_speed_comparison.png')
|
||
|
||
|
||
# ============================================================
|
||
# 7. Scenario table (when to use process vs thread)
|
||
# ============================================================
|
||
def gen_scenario_table():
|
||
fig, ax = plt.subplots(figsize=(8.5, 4.5))
|
||
ax.set_xlim(0, 11)
|
||
ax.set_ylim(-5.5, 1)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Kiedy proces, kiedy wątek? — typowe scenariusze',
|
||
fontsize=FS_TITLE, fontweight='bold', pad=10)
|
||
|
||
headers = ['Scenariusz', 'Wybór', 'Dlaczego?']
|
||
col_w = [3.5, 2.5, 4.5]
|
||
rows = [
|
||
['Serwer WWW (Apache)', 'Proces', 'izolacja klientów'],
|
||
['Serwer WWW (nginx)', 'Wątek / async', 'szybkość, cooperacja'],
|
||
['Przeglądarka (karty)', 'Proces', 'crash isolation'],
|
||
['Przeglądarka (JS+render)', 'Wątek', 'współdzielony DOM'],
|
||
['Gra (fizyka+rendering)', 'Wątek', 'współdzielony świat gry'],
|
||
['Kompilacja (make -j8)', 'Proces', 'izolacja, prostota'],
|
||
['Baza danych (zapytania)', 'Wątek', 'współdzielony cache'],
|
||
['Microservices', 'Proces (kontener)', 'izolacja, deployment'],
|
||
]
|
||
draw_table(ax, headers, rows, x0=0.25, y0=0.5, col_widths=col_w,
|
||
row_h=0.5, fontsize=7)
|
||
|
||
save_fig(fig, 'q9_scenario_table.png')
|
||
|
||
|
||
# ============================================================
|
||
# 8. IPC details: pipe, shared memory, socket (3-panel)
|
||
# ============================================================
|
||
def gen_ipc_details():
|
||
fig, axes = plt.subplots(1, 3, figsize=(11, 3.5))
|
||
fig.suptitle('Mechanizmy IPC — szczegóły', fontsize=FS_TITLE, fontweight='bold')
|
||
|
||
# Panel 1: Pipe
|
||
ax = axes[0]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 5)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Pipe (potok)', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
draw_box(ax, 0.2, 2.0, 1.8, 1.2, 'Proces A\n(ls)\nstdout', fill=GRAY1,
|
||
fontsize=FS)
|
||
draw_box(ax, 3.0, 2.0, 1.8, 1.2, 'Bufor\njądra\n(4 KB)', fill=GRAY2,
|
||
fontsize=FS, fontweight='bold')
|
||
draw_box(ax, 5.8, 2.0, 1.8, 1.2, 'Proces B\n(grep)\nstdin', fill=GRAY1,
|
||
fontsize=FS)
|
||
draw_arrow(ax, 2.0, 2.6, 3.0, 2.6, lw=1.5)
|
||
ax.text(2.5, 3.0, 'write()\nfd[1]', fontsize=FS_SMALL, ha='center')
|
||
draw_arrow(ax, 4.8, 2.6, 5.8, 2.6, lw=1.5)
|
||
ax.text(5.3, 3.0, 'read()\nfd[0]', fontsize=FS_SMALL, ha='center')
|
||
ax.text(4.0, 0.8, 'Jednokierunkowy\nBufor pełny → write() blokuje',
|
||
fontsize=FS_SMALL, ha='center', style='italic',
|
||
bbox=dict(boxstyle='round,pad=0.2', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
# Panel 2: Shared Memory
|
||
ax = axes[1]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 5)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Shared Memory', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
draw_box(ax, 0.3, 3.0, 2.2, 1.2, 'Proces A\nstrona 7', fill=GRAY1, fontsize=FS)
|
||
draw_box(ax, 5.5, 3.0, 2.2, 1.2, 'Proces B\nstrona 3', fill=GRAY1, fontsize=FS)
|
||
draw_box(ax, 2.8, 1.0, 2.4, 1.2, 'RAM\nramka 42', fill=GRAY3, fontsize=FS,
|
||
fontweight='bold')
|
||
draw_arrow(ax, 2.0, 3.0, 3.5, 2.2, lw=1.5)
|
||
draw_arrow(ax, 6.0, 3.0, 4.5, 2.2, lw=1.5)
|
||
ax.text(4.0, 0.3, 'Zero kopiowania!\nA pisze → B widzi od razu\n'
|
||
'Wymaga synchronizacji (semafor)',
|
||
fontsize=FS_SMALL, ha='center', style='italic',
|
||
bbox=dict(boxstyle='round,pad=0.2', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
# Panel 3: Socket
|
||
ax = axes[2]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 5)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Socket', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
# Network socket
|
||
draw_box(ax, 0.3, 3.2, 1.8, 0.9, 'Klient', fill=GRAY1, fontsize=FS, fontweight='bold')
|
||
draw_box(ax, 5.5, 3.2, 1.8, 0.9, 'Serwer', fill=GRAY1, fontsize=FS, fontweight='bold')
|
||
draw_double_arrow(ax, 2.1, 3.65, 5.5, 3.65, lw=1.5)
|
||
ax.text(3.8, 4.3, 'TCP/IP (sieciowy)', fontsize=FS, ha='center', fontweight='bold')
|
||
|
||
# Unix socket
|
||
draw_box(ax, 0.3, 1.3, 1.8, 0.9, 'Proces A', fill=GRAY4, fontsize=FS, fontweight='bold')
|
||
draw_box(ax, 5.5, 1.3, 1.8, 0.9, 'Proces B', fill=GRAY4, fontsize=FS, fontweight='bold')
|
||
draw_double_arrow(ax, 2.1, 1.75, 5.5, 1.75, lw=1.5)
|
||
ax.text(3.8, 2.4, 'Unix domain socket\n(/tmp/app.sock)', fontsize=FS,
|
||
ha='center', fontweight='bold')
|
||
|
||
ax.text(3.8, 0.5, 'Dwukierunkowy\nNajbardziej uniwersalny IPC',
|
||
fontsize=FS_SMALL, ha='center', style='italic',
|
||
bbox=dict(boxstyle='round,pad=0.2', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
fig.tight_layout(rect=[0, 0, 1, 0.9])
|
||
save_fig(fig, 'q9_ipc_details.png')
|
||
|
||
|
||
# ============================================================
|
||
# 9. IPC comparison table
|
||
# ============================================================
|
||
def gen_ipc_table():
|
||
fig, ax = plt.subplots(figsize=(8.5, 3.5))
|
||
ax.set_xlim(0, 11)
|
||
ax.set_ylim(-4.5, 1)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Porównanie mechanizmów IPC', fontsize=FS_TITLE, fontweight='bold', pad=10)
|
||
|
||
headers = ['Mechanizm', 'Kierunek', 'Szybkość', 'Zastosowanie']
|
||
col_w = [2.5, 2.0, 2.5, 3.5]
|
||
rows = [
|
||
['Pipe', 'jednokierunkowy', 'średnia', 'ls | grep'],
|
||
['Named Pipe', 'jednokierunkowy', 'średnia', 'demon → klient'],
|
||
['Shared Memory', 'dwukierunkowy', 'NAJSZYBSZA', 'video, bazy danych'],
|
||
['Message Queue', 'dwukierunkowy', 'średnia', 'wieloproducentowe'],
|
||
['Socket', 'dwukierunkowy', 'wolna (sieć)', 'klient-serwer'],
|
||
['Signal', 'jednokierunkowy', 'natychmiastowa', 'powiadomienia (nr)'],
|
||
]
|
||
draw_table(ax, headers, rows, x0=0.25, y0=0.5, col_widths=col_w,
|
||
row_h=0.5, fontsize=7.5)
|
||
|
||
save_fig(fig, 'q9_ipc_table.png')
|
||
|
||
|
||
# ============================================================
|
||
# 10. Race condition (simple x + bank timeline)
|
||
# ============================================================
|
||
def gen_race_condition():
|
||
fig, axes = plt.subplots(1, 2, figsize=(11, 5))
|
||
fig.suptitle('Wyścig (Race Condition) — przykłady', fontsize=FS_TITLE,
|
||
fontweight='bold')
|
||
|
||
# Panel 1: simple x increment
|
||
ax = axes[0]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 7)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Prosty wyścig: x = x + 1', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
# Timeline
|
||
steps_a = ['czytaj x (=0)', 'dodaj 1', 'zapisz x (=1)']
|
||
steps_b = ['czytaj x (=0)', 'dodaj 1', 'zapisz x (=1)']
|
||
ax.text(2.0, 6.3, 'Wątek A', fontsize=FS_LABEL, ha='center', fontweight='bold')
|
||
ax.text(6.0, 6.3, 'Wątek B', fontsize=FS_LABEL, ha='center', fontweight='bold')
|
||
ax.plot([2, 2], [0.8, 6.0], color=LN, lw=1)
|
||
ax.plot([6, 6], [0.8, 6.0], color=LN, lw=1)
|
||
|
||
for i, (sa, sb) in enumerate(zip(steps_a, steps_b)):
|
||
y = 5.3 - i * 1.2
|
||
draw_box(ax, 0.5, y, 3.0, 0.6, sa, fill=GRAY4, fontsize=FS)
|
||
draw_box(ax, 4.5, y - 0.3, 3.0, 0.6, sb, fill=GRAY1, fontsize=FS)
|
||
|
||
ax.text(4.0, 0.4, 'Wynik: x = 1 (powinno 2!)',
|
||
fontsize=FS, ha='center', fontweight='bold', color='#C62828',
|
||
bbox=dict(boxstyle='round', facecolor='#F8D7DA', edgecolor='#C62828'))
|
||
|
||
# Panel 2: bank account
|
||
ax = axes[1]
|
||
ax.set_xlim(0, 10)
|
||
ax.set_ylim(0, 7)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Konto bankowe: saldo = 1000 zł', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
ax.text(2.5, 6.3, 'Wątek A (+500)', fontsize=FS, ha='center', fontweight='bold')
|
||
ax.text(7.5, 6.3, 'Wątek B (−200)', fontsize=FS, ha='center', fontweight='bold')
|
||
ax.plot([2.5, 2.5], [0.8, 6.0], color=LN, lw=1)
|
||
ax.plot([7.5, 7.5], [0.8, 6.0], color=LN, lw=1)
|
||
|
||
events = [
|
||
('t1', 'czytaj → 1000', '', 5.3),
|
||
('t2', '', 'czytaj → 1000', 4.6),
|
||
('t3', '1000+500=1500', '', 3.9),
|
||
('t4', '', '1000−200=800', 3.2),
|
||
('t5', 'zapisz 1500', '', 2.5),
|
||
('t6', '', 'zapisz 800 ✗', 1.8),
|
||
]
|
||
for t, a, b, y in events:
|
||
ax.text(0.3, y + 0.15, t, fontsize=FS_SMALL, fontweight='bold', va='center')
|
||
if a:
|
||
draw_box(ax, 1.0, y, 3.0, 0.45, a, fill=GRAY4, fontsize=FS_SMALL)
|
||
if b:
|
||
fill = '#F8D7DA' if '✗' in b else GRAY1
|
||
draw_box(ax, 6.0, y, 3.0, 0.45, b, fill=fill, fontsize=FS_SMALL)
|
||
|
||
ax.text(5.0, 0.4, 'Wynik: 800 zł (powinno 1300!)',
|
||
fontsize=FS, ha='center', fontweight='bold', color='#C62828',
|
||
bbox=dict(boxstyle='round', facecolor='#F8D7DA', edgecolor='#C62828'))
|
||
|
||
fig.tight_layout(rect=[0, 0, 1, 0.9])
|
||
save_fig(fig, 'q9_race_condition.png')
|
||
|
||
|
||
# ============================================================
|
||
# 11. Deadlock scenario + cycle
|
||
# ============================================================
|
||
def gen_deadlock_scenario():
|
||
fig, axes = plt.subplots(1, 2, figsize=(11, 4.5))
|
||
fig.suptitle('Zakleszczenie (Deadlock)', fontsize=FS_TITLE, fontweight='bold')
|
||
|
||
# Panel 1: timeline
|
||
ax = axes[0]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 6)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Scenariusz z 2 mutexami', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
ax.text(2.5, 5.3, 'Wątek A', fontsize=FS_LABEL, ha='center', fontweight='bold')
|
||
ax.text(6.0, 5.3, 'Wątek B', fontsize=FS_LABEL, ha='center', fontweight='bold')
|
||
|
||
steps = [
|
||
('lock(mutex1) OK', '', 'trzyma', False, 4.5),
|
||
('', 'lock(mutex2) OK', 'trzyma', False, 3.7),
|
||
('lock(mutex2) ...WAIT', '', 'CZEKA!', True, 2.9),
|
||
('', 'lock(mutex1) ...WAIT', 'CZEKA!', True, 2.1),
|
||
]
|
||
for a_text, b_text, note, is_wait, y in steps:
|
||
if a_text:
|
||
fill = '#F8D7DA' if is_wait else GRAY4
|
||
draw_box(ax, 0.5, y, 3.3, 0.55, a_text, fill=fill, fontsize=FS_SMALL)
|
||
if b_text:
|
||
fill = '#F8D7DA' if is_wait else GRAY4
|
||
draw_box(ax, 4.3, y, 3.3, 0.55, b_text, fill=fill, fontsize=FS_SMALL)
|
||
|
||
ax.text(4.0, 1.2, 'DEADLOCK!\nŻaden nie odpuści',
|
||
fontsize=FS, ha='center', fontweight='bold', color='#C62828',
|
||
bbox=dict(boxstyle='round', facecolor='#F8D7DA', edgecolor='#C62828'))
|
||
|
||
# Panel 2: cycle diagram
|
||
ax = axes[1]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 6)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Cykl oczekiwania', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
# Thread boxes
|
||
draw_box(ax, 0.5, 3.5, 2.2, 1.2, 'Wątek A\ntrzyma Mutex 1', fill=GRAY1,
|
||
fontsize=FS, fontweight='bold')
|
||
draw_box(ax, 5.3, 3.5, 2.2, 1.2, 'Wątek B\ntrzyma Mutex 2', fill=GRAY1,
|
||
fontsize=FS, fontweight='bold')
|
||
|
||
# Mutex boxes
|
||
draw_box(ax, 0.5, 1.0, 2.2, 1.0, 'Mutex 1', fill=GRAY3, fontsize=FS,
|
||
fontweight='bold')
|
||
draw_box(ax, 5.3, 1.0, 2.2, 1.0, 'Mutex 2', fill=GRAY3, fontsize=FS,
|
||
fontweight='bold')
|
||
|
||
# holds arrows (down)
|
||
draw_arrow(ax, 1.6, 3.5, 1.6, 2.0, lw=2)
|
||
ax.text(0.9, 2.7, 'trzyma', fontsize=FS_SMALL, rotation=90, va='center')
|
||
draw_arrow(ax, 6.4, 3.5, 6.4, 2.0, lw=2)
|
||
ax.text(7.0, 2.7, 'trzyma', fontsize=FS_SMALL, rotation=90, va='center')
|
||
|
||
# waits-for arrows (across, red)
|
||
draw_arrow(ax, 2.7, 4.3, 5.3, 4.3, lw=2.5, color='#C62828')
|
||
ax.text(4.0, 4.7, 'czeka na Mutex 2', fontsize=FS_SMALL, ha='center',
|
||
fontweight='bold', color='#C62828')
|
||
draw_arrow(ax, 5.3, 3.7, 2.7, 3.7, lw=2.5, color='#C62828')
|
||
ax.text(4.0, 3.1, 'czeka na Mutex 1', fontsize=FS_SMALL, ha='center',
|
||
fontweight='bold', color='#C62828')
|
||
|
||
fig.tight_layout(rect=[0, 0, 1, 0.9])
|
||
save_fig(fig, 'q9_deadlock_scenario.png')
|
||
|
||
|
||
# ============================================================
|
||
# 12. Coffman conditions + prevention strategies
|
||
# ============================================================
|
||
def gen_coffman_strategies():
|
||
fig, ax = plt.subplots(figsize=(9, 4))
|
||
ax.set_xlim(0, 11.5)
|
||
ax.set_ylim(-3.5, 1)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Warunki Coffmana — zapobieganie deadlockowi',
|
||
fontsize=FS_TITLE, fontweight='bold', pad=10)
|
||
|
||
headers = ['Warunek', 'Opis', 'Jak złamać', 'Przykład']
|
||
col_w = [2.5, 2.5, 3.0, 3.0]
|
||
rows = [
|
||
['1. Mutual Exclusion', 'zasób wyłączny', 'współdzielony zasób', 'Read-write lock'],
|
||
['2. Hold and Wait', 'trzymaj + czekaj', 'bierz WSZYSTKIE naraz', 'lock(m1,m2) atomowo'],
|
||
['3. No Preemption', 'nie zabierzesz siłą', 'timeout / trylock', 'pthread_mutex_trylock()'],
|
||
['4. Circular Wait', 'cykliczne oczekiw.', 'porządek liniowy', 'zawsze m1 przed m2'],
|
||
]
|
||
draw_table(ax, headers, rows, x0=0.25, y0=0.5, col_widths=col_w,
|
||
row_h=0.6, fontsize=7)
|
||
|
||
ax.text(5.75, -3.1, '▸ Najczęstsza strategia: PORZĄDEK LINIOWY — '
|
||
'numeruj mutexy, zawsze blokuj rosnąco',
|
||
fontsize=FS, ha='center', fontweight='bold',
|
||
bbox=dict(boxstyle='round,pad=0.2', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
save_fig(fig, 'q9_coffman_strategies.png')
|
||
|
||
|
||
# ============================================================
|
||
# 13. Starvation + Priority Inversion (2-panel)
|
||
# ============================================================
|
||
def gen_starvation_priority():
|
||
fig, axes = plt.subplots(1, 2, figsize=(11, 4.5))
|
||
fig.suptitle('Zagłodzenie i Inwersja priorytetów', fontsize=FS_TITLE,
|
||
fontweight='bold')
|
||
|
||
# Panel 1: Starvation + aging
|
||
ax = axes[0]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 6)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Zagłodzenie (Starvation)', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
threads = [
|
||
('Wątek HIGH', 'prio=10', GRAY5, 3.0),
|
||
('Wątek HIGH', 'prio=9', GRAY3, 2.2),
|
||
('Wątek MED', 'prio=5', GRAY2, 1.4),
|
||
('Wątek LOW', 'prio=1 → głoduje!', '#F8D7DA', 0.6),
|
||
]
|
||
for name, prio, color, y in threads:
|
||
draw_box(ax, 0.5, y, 2.0, 0.6, name, fill=color, fontsize=FS_SMALL,
|
||
fontweight='bold')
|
||
ax.text(2.8, y + 0.3, prio, fontsize=FS_SMALL, va='center')
|
||
|
||
ax.text(1.5, 4.2, 'CPU zawsze\ndostaje HIGH!', fontsize=FS,
|
||
ha='center', fontweight='bold')
|
||
draw_arrow(ax, 1.5, 3.9, 1.5, 3.65, lw=1.5)
|
||
|
||
# Aging solution
|
||
draw_box(ax, 4.5, 1.5, 3.2, 2.5, '', fill=GRAY4, rounded=True)
|
||
ax.text(6.1, 3.7, 'Rozwiązanie: AGING', fontsize=FS, fontweight='bold', ha='center')
|
||
aging = ['t=0: prio=1', 't=100ms: prio=2', 't=200ms: prio=3', '...', 'w końcu → CPU!']
|
||
for i, line in enumerate(aging):
|
||
ax.text(6.1, 3.2 - i * 0.4, line, fontsize=FS_SMALL, ha='center',
|
||
family='monospace')
|
||
|
||
# Panel 2: Priority Inversion
|
||
ax = axes[1]
|
||
ax.set_xlim(0, 10)
|
||
ax.set_ylim(0, 6)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Inwersja priorytetów', fontsize=FS_LABEL, fontweight='bold')
|
||
|
||
# Timeline
|
||
labels = ['H (wysoki)', 'M (średni)', 'L (niski)']
|
||
ys = [4.2, 2.8, 1.4]
|
||
for label, y in zip(labels, ys):
|
||
ax.text(0.3, y + 0.2, label, fontsize=FS, fontweight='bold', va='center')
|
||
|
||
# L runs and locks mutex
|
||
draw_box(ax, 2.0, ys[2], 1.2, 0.5, 'lock(m)', fill=GRAY1, fontsize=FS_SMALL)
|
||
|
||
# M preempts L
|
||
draw_box(ax, 3.5, ys[1], 3.0, 0.5, 'M pracuje...', fill=GRAY3, fontsize=FS_SMALL)
|
||
|
||
# H waits for mutex
|
||
draw_box(ax, 3.5, ys[0], 3.0, 0.5, 'CZEKA na mutex!', fill='#F8D7DA',
|
||
fontsize=FS_SMALL, fontweight='bold')
|
||
|
||
# M finishes, L continues, unlocks
|
||
draw_box(ax, 6.8, ys[2], 1.5, 0.5, 'unlock(m)', fill=GRAY1, fontsize=FS_SMALL)
|
||
draw_box(ax, 8.5, ys[0], 1.2, 0.5, 'H runs', fill=GRAY4, fontsize=FS_SMALL)
|
||
|
||
# Explanation
|
||
ax.text(5.0, 0.5, 'H czeka na M (mimo H > M)!\n'
|
||
'Rozwiązanie: Priority Inheritance\n'
|
||
'L dziedziczy priorytet H → M nie wypycha L',
|
||
fontsize=FS_SMALL, ha='center', style='italic',
|
||
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
ax.text(5.0, 0.0, 'Mars Pathfinder (1997) — klasyczny bug!',
|
||
fontsize=FS_SMALL, ha='center', fontweight='bold')
|
||
|
||
fig.tight_layout(rect=[0, 0, 1, 0.9])
|
||
save_fig(fig, 'q9_starvation_priority.png')
|
||
|
||
|
||
# ============================================================
|
||
# 14. Bounded buffer + readers-writers + philosophers
|
||
# ============================================================
|
||
def gen_classic_problems():
|
||
fig, axes = plt.subplots(1, 3, figsize=(12, 5))
|
||
fig.suptitle('Klasyczne problemy synchronizacji', fontsize=FS_TITLE,
|
||
fontweight='bold')
|
||
|
||
# Panel 1: Bounded Buffer with semaphores
|
||
ax = axes[0]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 7)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Producent-Konsument\n(Bounded Buffer, N=4)', fontsize=FS,
|
||
fontweight='bold')
|
||
|
||
draw_box(ax, 0.2, 4.0, 2.0, 1.2, 'Producent\nP(empty)\nP(mutex)\nwstaw()\nV(mutex)\nV(full)',
|
||
fill=GRAY1, fontsize=5.5)
|
||
# Buffer
|
||
items = ['A', 'B', '', '']
|
||
for i, item in enumerate(items):
|
||
x = 2.8 + i * 0.9
|
||
fill = GRAY3 if item else 'white'
|
||
draw_box(ax, x, 4.3, 0.9, 0.7, item, fill=fill, fontsize=FS,
|
||
fontweight='bold', rounded=False)
|
||
ax.text(4.6, 5.2, 'Bufor (N=4)', fontsize=FS_SMALL, ha='center',
|
||
fontweight='bold')
|
||
draw_box(ax, 6.0, 4.0, 2.0, 1.2, 'Konsument\nP(full)\nP(mutex)\npobierz()\nV(mutex)\nV(empty)',
|
||
fill=GRAY4, fontsize=5.5)
|
||
draw_arrow(ax, 2.2, 4.6, 2.8, 4.65, lw=1.2)
|
||
draw_arrow(ax, 6.4, 4.65, 6.0, 4.6, lw=1.2)
|
||
|
||
# Semaphores
|
||
sems = [('mutex = 1', GRAY2), ('empty = N', GRAY1), ('full = 0', GRAY3)]
|
||
for i, (s, c) in enumerate(sems):
|
||
draw_box(ax, 2.0, 2.5 - i * 0.6, 4.0, 0.45, s, fill=c, fontsize=FS_SMALL,
|
||
fontweight='bold')
|
||
|
||
ax.text(4.0, 0.5, 'KOLEJNOŚĆ: P(empty/full)\nPRZED P(mutex)!\nOdwrotnie = DEADLOCK',
|
||
fontsize=5.5, ha='center', fontweight='bold', color='#C62828',
|
||
bbox=dict(boxstyle='round', facecolor='#F8D7DA', edgecolor='#C62828'))
|
||
|
||
# Panel 2: Readers-Writers
|
||
ax = axes[1]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 7)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Czytelnicy-Pisarze\n(Readers-Writers)', fontsize=FS,
|
||
fontweight='bold')
|
||
|
||
# Resource
|
||
draw_box(ax, 2.5, 3.5, 3.0, 1.5, 'Dane\n(współdzielone)', fill=GRAY2,
|
||
fontsize=FS, fontweight='bold')
|
||
|
||
# Readers
|
||
for i in range(3):
|
||
x = 0.3 + i * 1.0
|
||
draw_box(ax, x, 5.5, 0.8, 0.7, f'R{i+1}', fill=GRAY4, fontsize=FS,
|
||
fontweight='bold')
|
||
draw_arrow(ax, x + 0.4, 5.5, 3.0 + i * 0.5, 5.0, lw=1)
|
||
|
||
ax.text(1.5, 6.5, 'Czytelnicy (wielu naraz)', fontsize=FS_SMALL,
|
||
ha='center', fontweight='bold')
|
||
|
||
# Writer
|
||
draw_box(ax, 5.5, 5.5, 1.5, 0.7, 'Pisarz', fill=GRAY5, fontsize=FS,
|
||
fontweight='bold')
|
||
draw_arrow(ax, 6.25, 5.5, 5.0, 5.0, lw=1.5)
|
||
ax.text(6.25, 6.5, 'WYŁĄCZNY', fontsize=FS_SMALL, ha='center',
|
||
fontweight='bold', color='#C62828')
|
||
|
||
# Rules
|
||
rules = ['Wielu czytelników = OK', 'Jeden pisarz = wyłączny',
|
||
'Czytelnik + Pisarz = ✗', 'Problem: pisarze głodują']
|
||
for i, r in enumerate(rules):
|
||
ax.text(4.0, 2.5 - i * 0.45, r, fontsize=FS_SMALL, ha='center')
|
||
|
||
ax.text(4.0, 0.5, 'Rozwiązanie:\nrw_mutex + count_mutex\n+ zmienna readers',
|
||
fontsize=5.5, ha='center',
|
||
bbox=dict(boxstyle='round,pad=0.2', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
# Panel 3: Dining Philosophers
|
||
ax = axes[2]
|
||
ax.set_xlim(0, 8)
|
||
ax.set_ylim(0, 7)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Ucztujący filozofowie\n(Dining Philosophers)', fontsize=FS,
|
||
fontweight='bold')
|
||
|
||
# Draw circular table
|
||
cx, cy, r = 4.0, 3.8, 1.8
|
||
table = plt.Circle((cx, cy), 0.8, fill=True, facecolor=GRAY2,
|
||
edgecolor=LN, lw=1.5)
|
||
ax.add_patch(table)
|
||
ax.text(cx, cy, 'Stół', fontsize=FS, ha='center', fontweight='bold')
|
||
|
||
# 5 philosophers around table
|
||
for i in range(5):
|
||
angle = np.pi/2 + i * 2 * np.pi / 5
|
||
px = cx + r * np.cos(angle)
|
||
py = cy + r * np.sin(angle)
|
||
circle = plt.Circle((px, py), 0.35, fill=True, facecolor=GRAY1,
|
||
edgecolor=LN, lw=1.2)
|
||
ax.add_patch(circle)
|
||
ax.text(px, py, f'F{i}', ha='center', va='center', fontsize=FS,
|
||
fontweight='bold')
|
||
|
||
# Fork between philosophers
|
||
fork_angle = np.pi/2 + (i + 0.5) * 2 * np.pi / 5
|
||
fx = cx + (r * 0.6) * np.cos(fork_angle)
|
||
fy = cy + (r * 0.6) * np.sin(fork_angle)
|
||
ax.plot([fx - 0.1, fx + 0.1], [fy - 0.15, fy + 0.15], color=LN,
|
||
lw=2.5, solid_capstyle='round')
|
||
ax.text(fx + 0.2, fy + 0.15, f'w{i}', fontsize=5, color='#555555')
|
||
|
||
# Rules
|
||
rules = ['Jedzenie = 2 widelce', 'Naiwne → DEADLOCK',
|
||
'Fix: F4 bierze odwrotnie',
|
||
'Alt: semafor(4)']
|
||
for i, r in enumerate(rules):
|
||
ax.text(4.0, 1.2 - i * 0.35, r, fontsize=FS_SMALL, ha='center')
|
||
|
||
fig.tight_layout(rect=[0, 0, 1, 0.88])
|
||
save_fig(fig, 'q9_classic_problems.png')
|
||
|
||
|
||
# ============================================================
|
||
# 15. Sync mechanisms comparison + mutex/sem/spinlock
|
||
# ============================================================
|
||
def gen_sync_comparison():
|
||
fig, axes = plt.subplots(2, 1, figsize=(9, 7))
|
||
|
||
# Top: comparison table
|
||
ax = axes[0]
|
||
ax.set_xlim(0, 11.5)
|
||
ax.set_ylim(-5, 1)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Mechanizmy synchronizacji — porównanie', fontsize=FS_TITLE,
|
||
fontweight='bold', pad=10)
|
||
|
||
headers = ['Mechanizm', 'Opis', 'Kiedy używać']
|
||
col_w = [2.5, 4.5, 4.0]
|
||
rows = [
|
||
['Mutex', 'Zamek: 1 wątek w sekcji', 'Sekcja krytyczna'],
|
||
['Semafor(n)', 'Licznik: max n wątków', 'Ograniczone zasoby (n miejsc)'],
|
||
['Monitor', 'Obiekt z wbudowanym mutex', 'Java synchronized'],
|
||
['Cond. Variable', 'wait()/signal() na warunek', 'Producent-konsument'],
|
||
['Spinlock', 'Aktywne czekanie (busy-wait)', 'Bardzo krótkie sekcje (<1 μs)'],
|
||
['RW Lock', 'Wielu czytelników LUB 1 pisarz', 'Bazy danych, cache'],
|
||
['Barrier', 'Czekaj aż wszyscy dotrą', 'Obliczenia równoległe'],
|
||
]
|
||
draw_table(ax, headers, rows, x0=0.25, y0=0.5, col_widths=col_w,
|
||
row_h=0.5, fontsize=7)
|
||
|
||
# Bottom: mutex vs semafor vs spinlock
|
||
ax = axes[1]
|
||
ax.set_xlim(0, 12)
|
||
ax.set_ylim(0, 5)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Mutex vs Semafor vs Spinlock', fontsize=FS_TITLE,
|
||
fontweight='bold', pad=5)
|
||
|
||
# Mutex
|
||
draw_box(ax, 0.3, 2.5, 3.5, 2.0, '', fill=GRAY4)
|
||
ax.text(2.05, 4.2, 'MUTEX', fontsize=FS_LABEL, fontweight='bold', ha='center')
|
||
ax.text(2.05, 3.6, '= klucz do łazienki\n(1 osoba)', fontsize=FS, ha='center')
|
||
ax.text(2.05, 2.8, 'Wątek ZASYPIA gdy czeka\nOS go obudzi (~μs)', fontsize=FS_SMALL,
|
||
ha='center', style='italic')
|
||
|
||
# Semafor
|
||
draw_box(ax, 4.3, 2.5, 3.5, 2.0, '', fill=GRAY1)
|
||
ax.text(6.05, 4.2, 'SEMAFOR(n)', fontsize=FS_LABEL, fontweight='bold', ha='center')
|
||
ax.text(6.05, 3.6, '= parking na n miejsc\n(n wątków naraz)', fontsize=FS, ha='center')
|
||
ax.text(6.05, 2.8, 'Semafor(1) = mutex\nP() = zmniejsz, V() = zwiększ',
|
||
fontsize=FS_SMALL, ha='center', style='italic')
|
||
|
||
# Spinlock
|
||
draw_box(ax, 8.3, 2.5, 3.5, 2.0, '', fill=GRAY2)
|
||
ax.text(10.05, 4.2, 'SPINLOCK', fontsize=FS_LABEL, fontweight='bold', ha='center')
|
||
ax.text(10.05, 3.6, '= obrotowe drzwi\n(kręcisz się w kółko)', fontsize=FS, ha='center')
|
||
ax.text(10.05, 2.8, 'Wątek KRĘCI się w pętli\nLepszy gdy sekcja < 1 μs',
|
||
fontsize=FS_SMALL, ha='center', style='italic')
|
||
|
||
# Dividing rule
|
||
ax.text(6.0, 1.5, 'Reguła kciuka: sekcja > 1 μs → MUTEX | '
|
||
'sekcja < 1 μs → SPINLOCK | n jednocześnie → SEMAFOR(n)',
|
||
fontsize=FS, ha='center', fontweight='bold',
|
||
bbox=dict(boxstyle='round,pad=0.3', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
fig.tight_layout()
|
||
save_fig(fig, 'q9_sync_comparison.png')
|
||
|
||
|
||
# ============================================================
|
||
# 16. Semaphore concept diagram
|
||
# ============================================================
|
||
def gen_semaphore_concept():
|
||
fig, ax = plt.subplots(figsize=(6, 3))
|
||
ax.set_xlim(0, 10)
|
||
ax.set_ylim(0, 5)
|
||
ax.set_aspect('auto')
|
||
ax.axis('off')
|
||
ax.set_title('Semafor — koncepcja (parking na 3 miejsca)', fontsize=FS_TITLE,
|
||
fontweight='bold', pad=10)
|
||
|
||
# Parking slots
|
||
for i in range(3):
|
||
x = 2.0 + i * 2.0
|
||
occupied = i < 2 # 2 occupied, 1 free
|
||
fill = GRAY3 if occupied else 'white'
|
||
label = f'Wątek {i+1}' if occupied else '(wolne)'
|
||
draw_box(ax, x, 2.5, 1.5, 1.2, label, fill=fill, fontsize=FS,
|
||
fontweight='bold' if occupied else 'normal', rounded=False)
|
||
|
||
ax.text(5.0, 4.2, 'semafor(3): counter = 1 (jedno wolne miejsce)',
|
||
fontsize=FS, ha='center', fontweight='bold')
|
||
|
||
# Waiting thread
|
||
draw_box(ax, 0.2, 0.5, 1.5, 0.8, 'Wątek 4\nP() → czeka', fill='#F8D7DA',
|
||
fontsize=FS_SMALL)
|
||
draw_arrow(ax, 1.7, 0.9, 2.0, 2.5, lw=1.2, color='#C62828')
|
||
|
||
ax.text(5.0, 0.6, 'P() = counter−− (jeśli 0 → czekaj)\n'
|
||
'V() = counter++ (obudź czekającego)',
|
||
fontsize=FS, ha='center', family='monospace',
|
||
bbox=dict(boxstyle='round,pad=0.2', facecolor=GRAY4, edgecolor=GRAY3))
|
||
|
||
save_fig(fig, 'q9_semaphore_concept.png')
|
||
|
||
|
||
# ============================================================
|
||
# MAIN — generate all
|
||
# ============================================================
|
||
if __name__ == '__main__':
|
||
print("Generating ALL PYTANIE 9 diagrams...")
|
||
gen_process_vs_thread()
|
||
gen_memory_layout()
|
||
gen_process_states()
|
||
gen_thread_structure()
|
||
gen_pcb_structure()
|
||
gen_speed_comparison()
|
||
gen_scenario_table()
|
||
gen_ipc_details()
|
||
gen_ipc_table()
|
||
gen_race_condition()
|
||
gen_deadlock_scenario()
|
||
gen_coffman_strategies()
|
||
gen_starvation_priority()
|
||
gen_classic_problems()
|
||
gen_sync_comparison()
|
||
gen_semaphore_concept()
|
||
print("\nAll 16 PYTANIE 9 diagrams generated successfully!")
|