praca_magisterska/pytania/generate_q9_all_diagrams.py

1010 lines
40 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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!")