praca_magisterska/pytania/generate_q9_all_diagrams.py

1010 lines
40 KiB
Python
Raw Normal View History

2026-02-17 22:40:15 +01:00
#!/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', '~110 ms', '~10100 μs (100× szybciej)'],
['Przełączanie', '~15 μs (TLB flush)', '~0.10.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', '', '1000200=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!")