Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • za08gywo/sp-quiz
  • wa28ziqo/pfp-quiz
  • er04yjek/sp-quiz
  • de08hesi/sp-quiz
  • un65esoq/sp-quiz
  • wa28ziqo/sp-quiz
  • oj14ozun/sp-quiz
7 results
Show changes
Commits on Source (245)
*~
*.json
data/
out/
*.mk
This diff is collapsed.
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
QFILES != find . -name '[ws]s[[:digit:]][[:digit:]].q' -type f
JFILES := $(QFILES:%.q=%.json)
### Options
# Path to the directory with question files. The path is interpreted
# relative to this Makefile.
DATA := data
# Git branch to check out as a worktree, in case the DATA directory
# doesn't exist yet.
BRANCH := database/sp
# Output directory, that will contain the files that the site should
# host (HTML, CSS, JS, JSON).
OUT := out
# In case you don't or can't configure OUT to point directly to the
# directory hosted by a web-server, you can use "make sync" to copy
# the contents of OUT to some remote location. This uses rsync(1).
REMOTE := uni:.www/sp/quiz/
# A find(1)-compatible pattern to match all quiz files in DATA.
QPAT := *.q
# Main name of the quiz, if the intention is to have OUT be shared by
# more than just one quiz. This affects the name of the .html and the
# .json file.
NAME := quiz
### Programs
GIT := git
RSYNC := rsync -azvPLc
M4 := m4
AWK := awk
JQ := jq
RM := rm -rf
CP := cp
### Rules
.PHONY: all
all: sp.json
all: $(OUT)/$(NAME).json $(OUT)/$(NAME).html $(OUT)/quiz.js $(OUT)/quiz.css $(OUT)/katalog.html $(OUT)/conf.js
$(JFILES): %.json: %.q
./gen.pl $< > $@
$(DATA):; $(GIT) worktree add $@ $(BRANCH)
sp.json: $(JFILES)
jq -cs '[.[][]]' $^ > $@
GENMK := .$(shell echo $(BRANCH) | md5sum | cut -d" " -f 1).mk
$(GENMK): $(DATA) src/gen-mk.awk
find $< -name '$(QPAT)' -type f | \
xargs basename -s .q | \
$(AWK) -f src/gen-mk.awk > $@
include $(GENMK)
$(OUT) $(OUT)/$(NAME):; mkdir -p $@
$(OUT)/katalog.html: $(OUT)/$(NAME).json src/gen-katalog.py $(DATA)/TITLE | $(OUT)
./src/gen-katalog.py $(DATA)/TITLE < $< > $@
$(OUT)/$(NAME).html: site/quiz.html | $(OUT)
$(CP) $< $@
$(OUT)/quiz.css $(OUT)/quiz.js: $(OUT)/%: site/% | $(OUT)
$(CP) $< $@
$(OUT)/conf.js: src/conf.js.m4 $(DATA)/TITLE $(DATA)/DESC $(DATA)/GITLAB | $(DATA) $(OUT)
$(M4) -DQUIZ_DIR="$(DATA)" --prefix-builtins < $< > $@
# The dependencies for this file are listed in $(GENMK). We cannot
# give them here, since $(DATA) does not have to exist when the
# Makefile is parsed the first time.
#
# The Jq trick was taken from this comment on the the Jq Bug tracker:
# https://github.com/jqlang/jq/issues/805#issuecomment-109783967
$(OUT)/$(NAME).json:
$(JQ) -cs '[.[][]]' $^ > $@
$(OUT)/$(NAME)/%.json: $(DATA)/%.q src/gen.pl src/mark.lua | $(OUT)/$(NAME)
./src/gen.pl $< > $@
.PHONY: sync
sync: all
$(RSYNC) $(OUT)/ $(REMOTE)
.PHONY: clean
clean:
rm -f sp.json $(JFILES)
$(RM) $(OUT)
$(RM) $(GENMK)
$(GIT) worktree remove $(DATA)
-include local.mk
Fragensammlung für SP2
======================
Finde hier gesammelte und kommentierte Fragen auf
Systemprogrammierungs 2 Alt-Klausuren. Fragen liegen in `.q` Dateien,
mit einer Datei pro Semester/Klausur (`ws22.q`, `ss22.q`, `ws21.q`,
...).
Diese Fragen werden benutzt um diesen Quiz auf meiner Webseite
umzusetzen: <https://wwwcip.cs.fau.de/~oj14ozun/sp/quiz/>.
Dateiformat
-----------
Die `.q` Dateien werden von dem `gen.pl` Skript gelesen und
interpretiert. Hier die Skitze einer Gramamtik:
FILE ::= QUESTION*
QUESTION ::= HEADER ANSWER* LF "."
HEADER ::= TYPE TEXT SOURCE?
TYPE ::= "0" | "1" ;single/multiple-choice
SOURCE ::= "(" TEXT ")"
ANSWER ::= LF MODE TEXT COMMENT
MODE ::= "+" | "-" | "?" ;wahr, falsch, unsicher
COMMENT ::= ( LF TEXT )*
LF ::= <line feed>
TEXT ::= <beliebiger inhalt, bis auf LF>
Hier zwei Beispiele
0 Es gibt nur eine richtige Option (Ausgedacht)
- Die ist falsch.
Sollte offensichtlich sein.
+ Das ist richtig.
Alles andere nicht.
- Auch falsch.
? Unsicher.
Es ist egal ob man das auswählt.
.
1 Wähle alle Zahlen kleiner als 3 aus.
+ 0
+ 1
+ 2
Das ist noch kleiner.
- 3
Das ist nicht kleiner.
- 4
.
Das Skript wird `TEXT` als [Markdown] formattiert interpretieren und
entsprechend anpassen.
**Warnung:** Diese Grammatik ist nicht mehr aktuell, und dient nur als
vereinfachte Beschreibung. Das letzte Wort hat `gen.pl`.
[Markdown]: https://commonmark.org/help/
Beitragen
---------
Bei Interesse nehme ich gerne Hillfe von Stundenten an. Verschiedene Ideen:
- Fragen aus alten Klausuren in das oben beschriebene Format wandeln.
- Kommentare zu den Optionen dazuschreiben.
- Bestehende Antworten verbessern.
Ich biete an die Antworten "Probezulesen", also bekommt man auch
Feedback zurück, was motivierend sein kann.
Man kann entweder auf [GitLab] ein "Pull Request" anlegen, oder [mir]
eine Email schreiben. Ich nehme gerne Git patches, diffs oder auch
einfach so geschriebene Plain-Text Nachrichten an.
[GitLab]: https://gitlab.cs.fau.de/oj14ozun/sp-quiz
[mir]: https://wwwcip.cs.fau.de/~oj14ozun/#contact
Rechtliches
-----------
Es handelt sich hier in keinster Weise um eine offizielle Sammlung von
Klausur-Fragen, und alle Kommentare sollten als Gemeinfreie Beiträge
gewertet werden.
Lediglich der Code in diesem Repo sowie die Antworten und Erklärungen zu den Klausurfragen stehen unter CC0 (siehe `LICENSE`). Das Urheberrecht an den Klausurfragen selbst sowie die von https://sys.cs.fau.de/lehre/current/sp2/pruefung Übernommenen Antwortmöglichkeiten haben die jeweiligen Autoren (i.e., Angestellte des Lehrstuhls für Informatik 4) inne.
Quelltext für eine einfache Quiz-Seite
======================================
In diesem Repo wird der Quelltext für und um eine web-basierten Quiz
versioniert. Hierzu gehören die Benutzer-Dateien quiz.{html,css,js}
wie Skripte die das Aufsetzen erleichtern.
Um Lokal alles aufzusetzen, führe lediglich
$ make
in diesem Verzeichnis aus. Die resultierende Seite befindet sich dann
im Verzeichnis `out`.
Einstellungen können in der Makefile gefunden werden. Diese können
auch beim Aufruf von Make überschrieben werden:
$ make OUT=$HOME/.www/quiz BRANCH=database/foo NAME=foo
Zusätzliche Regeln können in der Datei `local.mk` angegeben werden:
$(OUT)/index.html: $(OUT)/$(NAME).html
ln -srf $< $@
all: $(OUT)/index.html
Datenbank der Fragen
--------------------
Fragen werden in einem separatem Branch verwaltet, und sollen über
einen "git worktree" eingeblendet werden. Damit bleibt das Repo
selbst Themenneutral. Für SP sind die Fragen auf dem "database/sp"
Branch. Wenn man an den Fragen arbeiten will, kann man gleich
$ git clone -b database/sp https://gitlab.cs.fau.de/oj14ozun/sp-quiz sp-fragen
ausführen. Weitere Branches können unabhängig erstellt werden mit
$ git checkout --orphan database/foo
$ git rm -rf
erstellt werden.
Abhängigkeiten
--------------
Man sollte auf seinem System folgende Programme installiert haben (im
CIP sind die alle verfügbar):
- GNU Make
- GNU Coreutils (`mkdir`, `ln`, `basename`, ...)
- GNU Findutils
- GNU m4
- Git
- Python 3
- AWK (beliebig)
- jq
- Perl
- Pandoc mit Lua Unterstützung \*
- rsync (optional, wenn man `make sync` benutzen will)
\* Pandoc wird benutzt um Markdown in dem Frage-Dateien zu HTML zu
übersetzen. Sollte man diese Abhängigkeit nicht benutzen wollen, kann
man in `src/gen.pl` die Variable `@markdown` anpassen.
Diese Abhängigkeiten sind nur für das automatische Aufsetzen der Seite
notwendig, was im wesentlichem die `quiz.json` Datei generiert aus
einem maßgeschneidertem Format in so-genannten `.q` Dateien. Die
`quiz.json` Datei kann auch händisch oder anderweitig generiert
werden. Die `conf.js` Datei kann die JavaScript Variablen
`quiz_title`, `quiz_description` und `gitlab_base` setzen, welche
jeweils den Titel der Seite, die Beschreibung hinter dem _i_ Knopf und
die GitLab-Integration einstellt. Per default werden diese wiederum
jeweils aus den Dateien `TITLE`, `DESC` und `GITLAB` im Datenbank
Branch gelesen, aber auch diese Datei kann selbst erstellt werden.
Ansonsten kann man `quiz.{html,css,js}` direkt übernehmen.
---
Maintainer: Philip Kaludercic \<philip.kaludercic@fau.de>
/* Copyright (C) 2024 Philip Kaludercic
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
:root {
font-family: system-ui, "Open Sans", "Noto Sans", "Lato", Arial, sans-serif;
--deafult-tint: ghostwhite;
--highlight: red;
--correct: honeydew;
--wrong: mistyrose;
--ambiguous: ivory;
--radius: 6px;
}
* {
box-sizing: border-box;
text-rendering: optimizeLegibility;
font-variant-ligatures: common-ligatures;
font-kerning: auto;
}
body {
margin: 0em auto 0 auto;
padding: 1em;
max-width: 50rem;
}
main {
border: 2px solid rgba(0,0,0,0.25);
background: var(--deafult-tint);
padding: 16px;
position: relative;
min-height: 48px;
border-radius: var(--radius);
}
main.broken, .answer.wrong main {
background: var(--wrong);
}
main.broken ~ * {
display: none !important;
}
.answer.right main {
background: var(--correct);
}
.open main {
background: var(--deafult-tint) !important;
}
/* QUESTION PANEL */
main img#media {
margin: 0.5em auto;
}
main section#text {
width: 90%;
}
main section#text > *:first-child {
margin-top: 0;
}
main section#text > *:last-child {
margin-bottom: 0;
}
/* INFO PANEL */
div#info, div#info > aside {
background: var(--default-tint);
border-radius: var(--radius);
display: inline;
}
div#info > aside > h1:first-of-type,
main > h1:first-of-type {
margin-top: 0;
}
button#info-toggle {
border: none;
background: none;
width: 32px;
height: 32px;
position: absolute;
top: 0;
right: 0;
cursor: pointer;
user-select: none;
}
:not(.open) #info > aside, .open #text, .open #metadata {
display: none;
}
.open #info > aside {
display: initial;
}
.open body > :not(.info-box),
.open main > :not(.info-box) {
display: none !important;
}
/* METADATA PANEL */
.next div#metadata::after,
div#metadata {
display: inline;
position: absolute;
bottom: 0;
right: 0;
padding: 4px 8px;
}
div#metadata {
opacity: 80%;
font-size: 70%;
}
/* ANSWERS */
#answers {
margin-bottom: 0;
}
#answers li {
padding-bottom: 1em;
}
#answers li::marker {
opacity: 80%;
}
#answers li .option {
display: flex;
align-items: flex-start;
margin: 4px;
}
#answers li > * {
display: flex;
}
#answers li > * > input {
margin-right: 0.7em;
}
#answers li .option p {
margin: 0;
display: inline;
}
#answers li .comment { display: none; }
.answer #answers li .comment { display: block; }
#answers li .comment {
border: 2px solid rgba(0,0,0,0.25);
border-radius: var(--radius);
background: var(--wrong);
padding: 8px;
margin-top: 16px;
}
#answers li .comment.correct {
background: var(--correct);
}
#answers li .comment.maybe {
background: var(--ambiguous);
}
#answers li .comment > p:first-of-type {
margin: 0; /* avoid redundant padding due to markdown */
}
/* STATISTICS */
#stats ~ * { font-size: 75%; }
#stats { font-size: 60%; display: inline; }
#stats progress, #stats meter { width: 2cm; }
#stats > #correct { background: var(--correct); }
#stats > #wrong { background: var(--wrong); }
/* ACTION BUTTON */
button {
padding: 8px;
margin-bottom: 8px;
border-width: 0;
border-radius: var(--radius);
}
#action {
float: right;
}
#action::after {
content: "Weiter?";
}
.answer #action::after {
content: "Weiter!";
}
:not(.tried) #action {
cursor: no-drop;
animation: none;
}
.answer #action,
.tried #action {
cursor: pointer;
animation: blinker 2s linear infinite;
}
/* ETC. */
mark {
background: unset;
color: unset;
}
/* We use the new non-standardised pseudo property :has to allow
reversing the direction of the standard subsequent sibling
combinator, since the checkbox appears after the options section,
where we want some words to be optionally highlighted. */
*:has(~ #display-hl:checked) .option mark,
*:has(~ #display-hl:checked) #text mark {
color: revert;
background: revert;
}
/* HACK: Firefox just started supporting the pseudo-selector :has (as
of 2023-12), we use this fact to hide the "Hebe Warnwörter hervor"
option from being displayed in the first place, avoiding
confusion. */
.display-hl {
display: none;
}
.display-hl:has(~ #display-hl-hack) {
display: initial;
}
/* This is the initial value, that indicates no link has been set and
therefore the "report problems" and "permalink" links is not of interest. */
a.shy[href="#"] {
display: none;
}
/* Some browser engines reduce the size of fixed-with elements by
default, and this appears to be a hack to circumvent this issue:
https://github.com/necolas/normalize.css/issues/519#issuecomment-197131966
*/
code, tt, kbd {
font-family: monospace, monospace;
}
pre {
overflow-x: auto;
}
/* based on https://stackoverflow.com/questions/16344354/ */
@keyframes blinker {
50% {
opacity: 50%;
}
}
/* basic "dark theme" support */
@media screen and (prefers-color-scheme: dark) {
:root {
--deafult-tint: darkslategray;
--highlight: cyan;
--correct: seagreen;
--wrong: maroon;
--ambiguous: sienna;
}
html {
background: black;
color: white;
}
a, button, input {
filter: invert(90%);
}
}
<!-- Copyright (C) 2024 Philip Kaludercic -->
<!-- This program is free software: you can redistribute it and/or modify -->
<!-- it under the terms of the GNU General Public License as published by -->
<!-- the Free Software Foundation, either version 3 of the License, or -->
<!-- (at your option) any later version. -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program. If not, see <https://www.gnu.org/licenses/>. -->
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="referrer" content="none" />
<meta name="viewport" content="width=device-width" />
<title>Quiz</title>
<link rel="stylesheet" href="./quiz.css" />
<script src="./conf.js"></script>
<script src="./quiz.js"></script>
</head>
<body>
<main class="info-box">
<div id="metadata">
Quelle: <span id="metadata-data">N/A</span>
</div>
<div id="info" class="info-box">
<aside >
<h1 id="title">Quiz</h1>
<div id="desc"></div>
<p>
Diese Seite benutzt <a href="https://en.wikipedia.org/wiki/Web_storage">Web storage</a>
um zu lokal zu verfolgen welche Fragen wie oft richtig oder falsch
beantwortet wurden. Man kann diese Daten mittels des Browsers
zurücksetzen, siehe bspw. für
<a href="https://support.mozilla.org/en-US/kb/storage">Firefox</a>.
</p>
<hr />
<p>
<strong>Tipp zum Benutzen dieser Seite:</strong>
Man kann alles mit der Tastatur eingeben! Die
Fragen <em>1</em> bis <em>10</em> können auch über die
Tasten <kbd>1</kbd> bis <kbd>0</kbd> eingegeben werden,
und mit <kbd>Enter</kbd> (oder <kbd>Space</kbd>) bestätigt werden. Ein
weiteres <kbd>Enter</kbd> (oder <kbd>Space</kbd>) öffnet die nächste Frage.
</p>
</aside>
<button id="info-toggle">𝒊</button>
</div>
<noscript>
<p>
Diese Seite benötigt leider Javascript um beabsichtigt
funktionieren!
</p>
<p>
Die Fragen können
auch <a href="./quiz.json">hier</a> direkt heruntergeladen und
selbst verwertet werden.
</p>
</noscript>
<img id="media" />
<section id="text">
<!-- questions shall be inserted here -->
</section>
</main>
<section id="options">
<ol id="answers"></ol>
</section>
<button id="action"></button>
<table id="stats">
<tr>
<td><label for="seen">Gesehen:</label></td>
<td><progress id="seen"></progress></td>
</tr>
<tr>
<td><label for="correct">
<abbr title="Wie viele Fragen wurden insgesamt schon mehrheitlich richtig beantwortet?">#Richtig</abbr>:
</label></td>
<td><meter id="correct"></meter></td>
</tr>
<tr>
<td><label for="success">
<abbr title="Wie häufig wurde diese Frage richtig beantwortet?">@Richtig</abbr>:
</label></td>
<td><meter id="success"></meter></td>
</tr>
</table>
<input type="checkbox" id="display-hl" class="display-hl" />
<label for="display-hl" class="display-hl">
Hebe <abbr title="Wörter wie &quot;immer&quot;, &quot;nie&quot;, ...">Warnwörter</abbr> hervor
</label>
<a class="shy" id="report" href="#" target="_blank" rel="noopener noreferrer">Fehler melden</a>
<a class="shy" id="perma" href="#">Dauerlink</a>
<span id="display-hl-hack"></span>
</body>
</html>
// Copyright (C) 2024 Philip Kaludercic
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
"use strict";
// request query parameters once (this can be done before the page is
// finished loading everything).
const params = new URLSearchParams(window.location.search);
// a string to prefix local storage keys with, so as to avoid
// conflicts with other quizzes.
const ls_prefix = window.location.toString().split("").reduce(
function (acc, val) { // Fowler–Noll–Vo Hash (FNV-1a)
return ((acc ^ val) * 0x01000193);
}, 0x811c9dc5).toString(36) + "_";
function shuf(arr) { // shuffle ARR using Fisher–Yates
for (let i = 0; i < arr.length - 1; i++) {
const j = Math.floor(Math.random() * arr.length);
const tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
return arr;
}
function create_gitlab_issue(title, desc) {
if (!gitlab_base) {
return "#"; // empty link
}
return `${gitlab_base}/issues/new?issue[title]=${encodeURIComponent(title)}&issue[description]=${encodeURIComponent(desc)}`;
}
function init(questions) {
console.table(questions);
// the part of the state of the website is controlled by CSS
// classes applied to the document root. We fetch this class list
// here to make manipulating it easier.
const state = document.documentElement.classList;
// configure the info button
const info_toggle = document.getElementById("info-toggle");
info_toggle.addEventListener("click", function (event) {
state.toggle("open");
});
info_toggle.addEventListener("keydown", function (event) {
// prevent CR from toggling the info panel, by dismissing
// every key event as invalid
return false;
});
// shared state variables
let current; // current question
let opts = []; // list of options as currently displayed
let progress = JSON.parse(
window.localStorage.getItem(ls_prefix + "progress")
) || {};
// seconds during which a question remains "recent", and we wish
// to avoid presenting it again.
const cooldown = 1000 * 60 * 5;
function get_data(q) {
const data = progress[q.id] || {};
if (!data["right"]) data["right"] = 0;
if (!data["wrong"]) data["wrong"] = 0;
if (!data["timestamp"]) data["timestamp"] = Date.now() - 10 * cooldown;
return data;
}
function evaluate(q) {
const data = get_data(q);
return (1 - Math.random() ** 3) * (data["right"] ** 2 - data["wrong"])
+ cooldown / (1 + Date.now() - data["timestamp"])
+ 6 * Math.random() - 3;
}
function update_stats(q) {
const seen = document.getElementById("seen");
seen.max = Object.keys(questions).length;
seen.value =
(Object.keys(progress).length || 0) +
(progress.hasOwnProperty(q.id) ? 0 : 1);
seen.title = seen.innerText =
`${seen.value} von ${seen.max}`;
function adjust(meter) {
meter.optimum = 0.95 * meter.max;
meter.high = 0.8 * meter.max;
meter.low = 0.5 * meter.max;
}
const correct = document.getElementById("correct");
let max = 0, value = 0;
for (const key in progress) {
if (progress[key]["right"] > progress[key]["wrong"])
value++;
max++;
}
if (0 == max) {
correct.max = 1; correct.value = 0;
correct.title = correct.innerText =
`Es wurden noch Fragen beantwortet`;
} else {
correct.max = max;
correct.value = value;
adjust(correct);
correct.title = correct.innerText =
`${correct.value} von ${correct.max}`;
}
const data = get_data(q);
const success = document.getElementById("success");
if (0 == data.right + data.wrong) {
success.max = 1; success.value = 0;
success.title = success.innerText =
`Diese Frage wurde noch nie beantwortet`;
} else {
success.max = data.right + data.wrong || 1;
success.value = data.right;
adjust(success);
success.title = success.innerText =
`${success.value} von ${success.max}`;
}
}
function remember(q, ok) {
const data = get_data(q);
if (ok) {
data["right"]++;
} else {
data["wrong"]++;
}
data["timestamp"] = Date.now();
// store in local storage
progress[q.id] = data;
window.localStorage.setItem(
ls_prefix + "progress",
JSON.stringify(progress));
}
function pick() {
const qid = params.get("show");
if (qid) {
if (params.has("solution")) {
state.add("answer");
}
const q = questions.find(q => q.id == qid);
if (q) {
return q;
}
console.error("Invalid ID: " + qid);
}
let choice = questions[0];
let worst = evaluate(choice);
// FIXME: Replace this with a proper priority queue
for (let i = 0; i < questions.length; i++) {
const q = questions[i];
let val = evaluate(q);
if (val <= worst) {
choice = q;
worst = val;
}
}
return choice;
}
// display the next next question
function display(q) {
const answers = document.getElementById("answers");
const text = document.getElementById("text");
const media = document.getElementById("media");
const metadata = document.getElementById("metadata-data");
const action = document.getElementById("action");
// insert question
text.innerHTML = q.question;
// insert metadata
metadata.innerText = q.source;
// load image, if specified
if (q.media) {
media.style.display = (media.src = q.media) ? "block" : "none";
} else {
media.style.display = "none";
}
// try to ensure that the options appear in a different order,
// unless a specific question was requested
if (!params.has("show"))
shuf(q.options);
// remove the previous options, if any, and add the new ones
while (answers.firstChild)
answers.removeChild(answers.firstChild);
opts = [];
for (let i = 0; i < q.options.length; i++) {
const oid = q.id + "_" + i;
const o = q.options[i];
const li = document.createElement("li");
const input = document.createElement("input");
const option = document.createElement("label");
const answ = document.createElement("div");
const comm = document.createElement("div");
// prepare the option container
option.classList.add("option");
option.setAttribute("for", oid);
// prepare checkbox
input.type = q.multiple ? "checkbox" : "radio";
input.setAttribute("id", oid);
input.setAttribute("name", "answer");
input.addEventListener("click", function (event) {
state.add("tried");
});
input.disabled = params.has("solution");
opts[i] = input;
// insert answer
answ.innerHTML = o.option;
// insert (and hide) comment
let fallback = "Falsch";
switch (o.correct) {
case true:
comm.classList.add("correct");
fallback = "Wahr";
break;
case null:
comm.classList.add("maybe");
fallback = "Unsicher";
break;
}
comm.innerHTML = o.comment || fallback;
comm.classList.add("comment");
// assemble everything to be added to the <li>
option.appendChild(input);
option.appendChild(answ);
li.appendChild(option);
li.appendChild(comm);
answers.appendChild(li);
}
// remember current question
current = q;
if (!params.has("solution")) {
// reset the answer state
state.remove("answer");
state.remove("wrong");
state.remove("right");
}
// display question metadata
update_stats(q);
// update report button
let url = new URL(window.location);
let usp = new URLSearchParams(url.search);
usp.set("show", q.id);
url.search = usp.toString();
document.getElementById("report").href = create_gitlab_issue(
`Problem mit der Frage: "${text.innerText}" (${q.source})`,
`(Ersetze diese Klammern mit einer Erklärung des Problem.)
Siehe <${url.toString()}&solution=>.`);
// update permalink
document.getElementById("perma").href = "?" + usp.toString();
}
function submit() {
let ok = true;
// check all answers
for (let i = 0; i < current.options.length; i++) {
const checked = opts[i].checked;
const correct = current.options[i].correct;
if (correct != null && checked != correct) { // wrong answer
ok = false;
}
opts[i].disabled = true;
}
// respond to result
if (ok) {
state.add("right");
} else {
state.add("wrong");
}
remember(current, ok);
// update question metadata
update_stats(current);
// set answer mode
state.add("answer");
state.remove("tried");
action.disabled = false;
}
// listen to keys
document.addEventListener("keyup", function (event) {
const action = document.getElementById("action");
switch (event.key) {
// shortcuts for first 10 options
case "1": case "2": case "3": case "4": case "5":
case "6": case "7": case "8": case "9": case "0": {
const n = Number(event.key)-1;
if (n in opts)
opts[n].click();
break;
}
case "Enter": // shortcut for action button
action.click();
break;
case "Escape": // close info section
document.documentElement.classList.remove("open");
break;
}
});
// setup action button
action.addEventListener("click", function (event) {
if (state.contains("answer")) {
// remove any permalink
if (params.get("show")) {
window.location.search = "";
} else {
display(pick());
}
} else if (state.contains("tried")) {
submit();
}
});
display(pick());
}
window.addEventListener("load", function (event) {
let quiz_url = params.get("quiz");
if (!quiz_url) {
const match = window.location.href.match(/\/(\w+)\.html$/);
let quiz_name;
if (match) {
quiz_name = match[1];
} else {
quiz_name = "quiz";
}
quiz_url = `./${quiz_name}.json`;
}
// query json specification of quiz
fetch(quiz_url).then(function (response) {
const main = document.querySelector("main");
function display_error(msg) {
main.innerHTML = msg + `
<a class="shy" href="${create_gitlab_issue("Fehler während der Initialisierung", "(Ersetze diese Klammern mit einer Erklärung des Problem.)")}">Problem via GitLab melden</a>`;
main.classList.add("broken");
}
if (response.status !== 200) {
console.table(response);
console.table(response.statusText);
display_error(`<h1>Fehler beim Abrufanforderung der Quiz Daten</h1>
<pre>URL: <a href="${quiz_url}">${quiz_url}</a>
Status: <abbr title="${response.statusText}">${response.status}</abbr>
</pre>`);
} else {
response.json().then(function (quiz_data) {
if (!(quiz_data instanceof Array)) {
display_error(`<h1>Ungültige Antwort</h1>
<p>
Es war erwartet, dass <code>${quiz_url}</code> eine Liste von Fragen aufgelöst wird, ergab aber stattdessen:
</p>
<pre>${quiz_data}</pre>`);
return;
}
if (quiz_data.length == 0) {
display_error(`<h1>Fragendatenbank Leer</h1>
<p>
Die Datenbank an Fragen konnte erfolgreich abgerufen werden, hat aber
keine Einträge enthalten.
</p>`);
return;
}
init(shuf(quiz_data));
}).catch(function (error) {
console.log(error);
display_error(`<h1>Laufzeitfehler</h1>
<pre>${error}</pre>`);
});
}
});
document.getElementById("title").innerHTML = document.title = quiz_title;
document.getElementById("desc").innerHTML = quiz_description;
});
m4_ifdef(`QUIZ_DIR',,
`m4_errprint(`QUIZ_DIR not defined')m4_m4exit(1)')m4_dnl
m4_changecom(`')m4_dnl
m4_changequote(`', `')m4_dnl
const quiz_title = `m4_include(QUIZ_DIR/TITLE)`;
const quiz_description = `m4_include(QUIZ_DIR/DESC)`;
const gitlab_base = `m4_include(QUIZ_DIR/GITLAB)`;
#!/usr/bin/env python3
# Generate a HTML index of questions
import json
import sys
import time
import locale
import urllib.parse as urlparse
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <title>", file=sys.stderr)
exit(1)
with open(sys.argv[1]) as tf:
title = tf.read()
# Try to ensure a German timestamp is generated
try:
locale.setlocale(locale.LC_TIME, "de_DE")
except locale.Error:
pass
quest = json.load(sys.stdin)
exams = {
s: [q for q in quest if q["source"] == s]
for s in {q["source"] for q in quest}
}
print(f"""<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<title>Ein Fragenkatalog zu {title}</title>
<link rel="stylesheet" href="./quiz.css" />
<style>
li > a > p {{ display: inline; }}
li.mc::marker {{ color: red; }}
</style>
</head>
<body>
<h1>Ein Fragenkatalog zu <a href=".">{title}</a></h1>
<p>
Punkte welche <span style="color: red;">rot</span> markiert sind, sind <q>multiple choice</q> Fragen,
ansonsten ist alles <q>single choice</q>.
</p>
""")
for exam in sorted(list(exams), reverse=True):
print(f"<h2>Aus der Klausur <q>{exam}</q></h2><ul>")
for q in sorted(exams[exam], key=lambda q: q["question"]):
qid = urlparse.quote_plus(q['id'])
link = f"quiz.html?show={qid}"
print("<li class=\"mc\">" if q['multiple'] else "<li>")
print(f"<a href='{link}'>{q['question']}</a> <sup>(<a href='{link}&solution='>Lösung</a>)</sup></li>")
print("</ul>")
print(f"""<hr/>
<footer>
Generiert am {time.strftime("%c")}.
</footer>
</body>
</html>""")
BEGIN { print "# Auto-generated file, do not edit manually" }
{ deps = deps " " "$(OUT)/$(NAME)/" $1 ".json" }
END { print "$(OUT)/$(NAME).json:" deps }
......@@ -7,10 +7,10 @@ use warnings;
use JSON;
use IPC::Open2;
use Digest::MD5 qw(md5_base64);
use FindBin;
my @hlwords = qw/immer nie jede jedem jedes muss keine alle nicht/;
my @markdown = qw/pandoc -f markdown+smart+fenced_code_blocks+backtick_code_blocks --html-q-tags --ascii/;
# my @markdown = qw/cat/;
my @markdown = qw/pandoc --html-q-tags --ascii --mathml/;
push @markdown, "--lua-filter=$FindBin::Bin/mark.lua";
my $question;
my $single_choice;
......@@ -24,14 +24,11 @@ sub md { # pipe through $markdown
my ($input) = @_;
my $pid = open2(\*OUT, \*IN, @markdown)
or die "open2() failed $!";
print IN $input;
print IN "$input\n";
close(IN);
waitpid($pid, 0);
my $html = do { local $/; <OUT> };
my $match = join("|", @hlwords);
$html =~ s!\b($match)\b!<span class="hl">$1</span>!;
return $html;
return do { local $/; <OUT> };
}
sub add {
......@@ -40,17 +37,17 @@ sub add {
if ($last_option{"comment"}) {
if (defined $last_option{"correct"}) {
if (($last_option{"correct"} eq JSON::true) and
($last_option{"comment"} =~ /^\s*Nein,/)) {
warn "$ARGV:$.: Correct answer starting with \"Nein\"\n";
($last_option{"comment"} =~ /^\s*Nein\b/)) {
warn "$ARGV:$.: Correct answer starting with \"Nein\"\n";
}
if (($last_option{"correct"} eq JSON::false) and
($last_option{"comment"} =~ /^\s*Ja,/)) {
warn "$ARGV:$.: Wrong answer starting with \"Ja\"\n";
($last_option{"comment"} =~ /^\s*Ja\b/)) {
warn "$ARGV:$.: Wrong answer starting with \"Ja\"\n";
}
}
$last_option{"comment"} = md($last_option{"comment"});
}
push(@options, {%last_option});
}
......@@ -66,15 +63,18 @@ while (<>) {
my $any = 0;
foreach my $opt (@options) {
$opt->{"option"} = md($opt->{"option"})
if $opt->{"option"};
if $opt->{"option"};
$any = 1 if (not (defined $opt->{"correct"})) or ($opt->{"correct"} eq JSON::true);
}
die "no correct option at \"$question\" ($ARGV)\n"
if not($any) and $single_choice;
my $data = uc ($ARGV . $question);
$data =~ tr{\n}{ };
$data =~ s/\s+/ /g;
my %ent =
(
"id" => md5_base64($ARGV . $question),
"id" => md5_base64($data),
"question" => md($question),
"options" => [@options],
"multiple" => ($single_choice eq "0" ? \0 : \1)
......@@ -102,9 +102,9 @@ while (<>) {
}
} elsif (/^[|](.*)/) {
if ($last_option{"option"}) {
$last_option{"option"} .= "$1\n";
$last_option{"option"} .= "\n$1";
} else {
$question .= "$1\n";
$question .= "\n$1";
}
} elsif (/^@(.*)/) {
$media = $1;
......@@ -116,7 +116,8 @@ while (<>) {
"option" => $2,
"correct" => $1 eq "+" ? JSON::true : $1 eq "-" ? JSON::false : JSON::null
);
} elsif (/^.*$/) { # non-empty line
} elsif (/^.*$/) { # non-empty line
s/^\s*\\//; # remove escape character
$last_option{"comment"} .= $_
if %last_option;
}
......
-- Pandoc Lua filter to insert special emphasis for certain words
-- ensure that we only kick in if we are generating HTML
if FORMAT ~= 'html' then return {} end
-- table words to highlight
local hl = {
["immer"] = true,
["nie"] = true,
["jede"] = true,
["jedem"] = true,
["jedes"] = true,
["muss"] = true,
["keine"] = true,
["alle"] = true,
["nicht"] = true,
["kann"] = true,
}
-- function to wrap words from hl with <mark>
local function mark (elem)
if hl[elem.text] then
return {
pandoc.RawInline("html", "<mark>"),
elem.text,
pandoc.RawInline("html", "</mark>"),
}
else
return elem
end
end
-- the filter itself
return {{Str = mark}}
# https://sys.cs.fau.de/extern/lehre/ws23/sp2/pruefung/klausuren/2016s-SP-Klausur-www.pdf
0 Welche Aussage zum Thema Speicherzuteilung ist richtig? (2016-06)
- Beim Halbierungsverfahren (buddy-Verfahren) kann keine interne Fragmentierung auftreten.
Doch, weil der Speicher intern in Zweier-Potenzen aufgeteilt wird, und wenn man weniger braucht, hat man Speicher-Verschließ.
- Speicherbereiche, die vor Beendigung eines Prozesses nicht mit free freigegeben wurden, sind bis zum Neustart des Systems unwiederbringlich verloren.
Nein, weil diese bereits dann "unwiederbringlich" verloren gehen, sobald der Prozess sich beendet.
+ Beim Halbierungsverfahren (buddy-Verfahren) kann es vorkommen, dass zwei nebeneinander liegende freie Speicherbereiche nicht miteinander verschmolzen werden können.
Nur "echte nachbaren", d.h. Speicherbereiche welche intern nicht weiter getrennt sind, und aus einem Speicherbereich entstanden sind, können verschmolzen werden. Es ist möglich, dass zwei Speicherbereiche zwar neben einander liegen, aber nicht gleich groß sind.
- best-t ist in jedem Fall das beste Verfahren.
Nein, das versucht nur den Verschnitt zu minimieren, und opfert dafür Laufzeit, weshalb es nicht "in jedem Fall" das beste sein kann.
.
0 Welche Aussage zum Thema Adressräume ist richtig? (2016-06)
- Im realen Adressraum sind alle theoretisch möglichen Adressen auch gültig.
Nein, weil der reale Addressraum lücken haben kann, bedingt durch den Speicher.
+ Der Zugriff auf eine virtuelle Adresse, die zum Zeitpunkt des Zugriffs nicht im Hauptspeicher abgebildet ist, führt zu einem Trap.
Ja, das Betriebsystem kann dann versuchen die Seite einzulagern, oder einen Speicherfehler an den Prozess weiterzuleiten.
- Die Größe eines virtuellen Adressraums darf die Größe des vorhandenen Hauptspeichers nicht überschreiten.
Nein, der virtuelle Adressraum abstrahiert über dem realem Addressraum, und kann vortäuschen (/virtualisieren/) die gesammte Wortbreite addressierbar zu machen.
- Bei Seitennummerierung besitzt jede Seite eine unterschiedliche Größe.
Nein, alle Seiten haben die gleiche Größe (ausnahme Huge-Pages, welche ein Vielfaches größer sind), damit diese leichter ein- und ausgelagert werden können.
.
0 Ein laufender Prozess wird in den Zustand blockiert überführt. Welche Aussage passt zu diesem Vorgang? (2016-06)
- Der Prozess terminiert.
Nein, weil ein blokierter Prozess kann später weiter laufen, sobald die Ursache, d.h. eine fehlende Ressource, behoben wurde.
- Es ist kein direkter Übergang von laufend nach blockiert möglich.
Nein, ein laufender Prozess kann direkt blockiert werden (ohne bspw. über "bereit" zu gehen), wenn ein blokierender Systemaufruf (`read(2)`, `accept(2)`, `wait(2)`, ...) getätigt wird.
+ Der Prozess wartet auf Daten von der Standardeingabe.
Ja, weil das den Prozess so lange blokiert, bis diese Daten verfügbar sind.
- Der bisher laufende Prozess wurde vom Betriebssystem verdrängt und ein anderer Prozess auf der CPU eingelastet.
Nein, der Prozess muss nicht (sofort) auf einer anderen CPU eingelastet werden.
.
0 Welche Aussage zum Aufbau einer Kommunikationsverbindung zwischen einem Client und Server über eine Socket-Schnittstelle ist richtig? (2016-06)
- Der Server signalisiert durch einen Aufruf von `connect()`, dass er zur Annahme von Verbindungen bereit ist; ein Client kann dies durch `accept()` annehmen.
Nein, dazu benutzt der Server den Systemaufruf `listen()`,
`connect()` wird auf der Client-Seite benutzt um sich mit einem
Server zu verbinden.
- Der Server erzeugt einen Socket und ruft anschließend `bind()` auf -- der Client muss durch einen Aufruf von `listen()` warten, bis der Server bereit zur Annahme von Verbindungen ist.
Nein, wenn der *Server* den Socket nicht für Verbindungen
vorbereitet hat mittels `listen()`, wird der *Client* mit
`connect()` einen Fehler erfahren. Es ist nicht in diesem Sinne mit
`listen()` für den Client möglich auf zukünftige Dienstleistungen zu
warten.
- Nach der Erzeugung eines Sockets mittels `socket()` kann ohne weitere System- oder Funktionsaufrufe sofort eine Verbindung von einem Client durch einen Aufruf von `connect()` angenommen werden.
Falsch, man kann mit `connect()` keine Verbindung *annehmen*, dazu
benutzt man `accept()`. In dem Fall muss aber zuvor `bind()` und
`listen()` verwenden.
+ Der Server richtet an einem Socket mittels `listen()` eine Warteschlange für ankommende Verbindungen ein und kann danach mit `accept()` eine konkrete Verbindung annehmen. `accept()` blockiert so lange die Warteschlange leer ist.
.
0 Wozu dient der Maschinenbefehl _cas_ (compare-and-swap)? (2016-06)
- Um bei Monoprozessorsystemen Interrupts zu sperren.
Nein, das wäre auf x86 ein Befehl wie `cli`.
+ Um auf einem Multiprozessorsystem einfache Modifikationen an Variablen ohne Sperren implementieren zu können.
Ja, es ersetzt einen Wert atomar und transaktional, wenn der Wert sich nicht verändert hat.
- Um bei der Implementierung von Schlossvariablen (Locks) aktives Warten zu vermeiden
Nein, passives Warten braucht einen Scheduler, was man nicht direkt mit cas bekommt.
- Um in einem System mit Seitennummerierung (Paging) Speicherseiten in die Auslagerungspartition (swap area) schreiben zu können.
Nein, das wird nicht mit einem Maschinenbefehl umgesetzt, sondern braucht eine MMU.
.
0 Welche Aussage zu Seitenersetzungsstrategien ist richtig? (2016-06)
- Bei der Seitenersetzungsstrategie FIFO wird immer die zuletzt eingelagerte Seite ersetzt.
Nein, _First In, First Out_ wird die älteste Seite ausgelagert.
- Beim Auslagern einer Speicherseite muss der zugehörige Seitendeskriptor angepasst werden, beim Einlagern einer Seite ist das jedoch nicht nötig.
Nein, der Deskriptor muss dahingehend angepasst werden, um anzudeuten dass die Seite wieder präsent ist.
+ Bei der Seitenersetzungsstrategie LRU wird die Seite ersetzt, welche am längsten nicht mehr referenziert wurde.
Ja, _Least Recently Used_ wird die älteste unbenutzte Seite ausgelagert.
- Beim Einsatz der Seitenersetzungsstrategie FIFO kann es nicht zu Seitenflattern kommen.
Doch, wenn die neuste Seite (_First In_) immer ausgelagert wird (_First Out_), dann werden bei hoher Last immer die gleichen Seiten ein- und ausgelagert, was man unter "Seitenflattern" versteht.
.
\ No newline at end of file
0 Welche Aussage über Funktionen der `exec()`-Familie ist richtig? (2018-07)
- Dem Vater-Prozess wird die Prozess-ID des Kind-Prozesses zurückgeliefert.
Nein, bei `exec` wird ein Programm innerhalb von einem Prozess ersetzt. Die Funktion kehrt nur im Fehlerfall zurück, und kommuniziert nichts direkt an den Vater-Prozess.
- Der an `exec()` übergebene Funktionszeiger wird durch einen neuen Thread im aktuellen Prozess ausgeführt.
Nein, weil `exec` Funktionen keine Funktionszeiger nehmen, sondern ein Verweis auf eine Datei, in welchem das auszuführende Programm drin steht.
+ Falls kein Fehler auftritt, kehrt der Aufruf von `exec()` nicht zurück
Ja, weil dann das alte Programm im Prozess ersetzt wurde, und nicht mehr weiter laufen könnte.
- `exec()` erzeugt einen neuen Kind-Prozess und startet darin das angegebene Programm.
Nein, weil `exec` keine neuen Prozesse erstellt.
.
0 Welche Aussage über den Rückgabewert von fork() ist richtig? (2018-07)
+ Dem Vater-Prozess wird die Prozess-ID des Kind-Prozesses zurückgeliefert.
- Der Kind-Prozess bekommt die Prozess-ID des Vater-Prozesses.
Nein, es bekommt den festen Wert 0; der Eltern-Prozess kann mit `getppid` (eindeutig) bestimmt werden.
- Im Fehlerfall wird im Kind-Prozess -1 zurückgeliefert.
Nein, im Fehlerfall (d.h. es konnte kein Kind erstellt werden) bekommt der Eltern-Prozess eine Fehler mit.
- Der Rückgabewert ist in jedem Prozess (Kind und Vater) jeweils die eigene Prozess-ID.
Nein, Kind bekommt 0, damit es weiß es ist das Kind und Vater bekommt die PID vom Kind.
.
0 Welche Aussage über das aktuelle Arbeitsverzeichnis (Current Working Directory) trifft zu? (2018-07)
- Jedem UNIX-Benutzer ist zu jeder Zeit ein aktuelles Verzeichnis zugeordnet.
Nein, weil ein aktuelles Verzeichnis wird einem Prozess zugeordnet.
+ Pfadnamen, die nicht mit dem Zeichen `/` beginnen, werden relativ zu dem aktuellen Arbeitsverzeichnis interpretiert.
- Mit dem Systemaufruf `chdir()` kann das aktuelle Arbeitsverzeichnis eines Prozesses durch seinen Vaterprozess verändert werden.
Nein, das ändert den CWD von dem aufrufendem Prozess.
- Besitzt ein UNIX-Prozess kein Current Working Directory, so beendet sich der Prozess mit einem Segmentation Fault.
.
0 Welche der folgenden Aussagen zum Thema Threads ist richtig? (2018-07)
+ Bei User-Threads ist die Scheduling-Strategie nicht durch das Betriebssystem vorgegeben.
Ja, weil das Scheduling innerhalb vom Prozess stattfindet, und damit unabhängig vom Betriebsystem ist.
- Kernel-Threads können Multiprozessoren nicht ausnutzen.
Doch, weil das Betriebsystem diese kennt und in der Lage ist diese auf verschiedene Kerne einzulagern.
- Die Umschaltung von User-Threads ist eine privilegierte Operation und muss deshalb im Systemkern erfolgen.
Nein, weil dieses Umschalten keine privilegierte Operation ist.
- Zu jedem Kernel-Thread gehört ein eigener, geschützter Adressraum.
Nein, ein Kernel-Thread kann auch den gleichen Speicherraum mit anderen Threads teilen (bspw. im kontext von echtem Multi-Threading, siehe Pthread).
.
0 Gegeben sei folgendes Szenario: zwei Fäden werden auf einem Monoprozessorsystem mit der Strategie "First Come First Served" verwaltet. In jedem Faden wird die Anweisung `i++;` auf der gemeinsamen, globalen volatile Variablen i ausgeführt. Welche der folgenden Aussagen ist richtig? (2019-06)
+ In einem Monoprozessorsystem ohne Verdrängung ist keinerlei Synchronisation erforderlich.
Ja, weil der Befehl immer ganz ausgeführt wird, und weil es keine unvorhergesehenen Unterbrechungen gibt, muss man diese auch nicht verhindern oder dagen vorbeugen.
- Während der Inkrementoperation müssen Interrupts vorübergehend unterbunden werden.
Nein, weil ein Interrupt keinen Einfluss haben muss auf diese Operation.
- Die Inkrementoperation muss mit einer CAS-Anweisung synchronisiert werden.
Nein, wenn notwenig, wäre es auch erlaubt es mit anderen mitteln zu sychronisieren.
- Die Operation i++ ist auf einem Monoprozessorsystem immer atomar.
Nein, das hängt von der Recherarchitektur ab, und kann aber muss nicht atomar übersetzt werden.
.
1 Der Speicher eines UNIX-Prozesses ist in Text-, Daten- und Stack-Segment untergliedert. Welche Aussagen bezogen auf C-Programme sind richtig? (2019-06)
+ Die Sichtbarkeit globaler `static`-Variablen ist auf das jeweilige Modul beschränkt.
Ja, damit verindert man auch Namenskonflikte zwischen Modulen.
+ Funktionspointer werden nicht immer im Text-Segment gespeichert.
Ja, es kann auch direkt auf dem Stack gespeichert werden.
? Globale schreibbare Variablen liegen im Daten-Segment.
Ja-ish; diese können nicht im Text-Segment liegen, weil die dann nicht schreibbar währen; diese können nicht auf der Halde liegen, weil das zur laufzeit dynamisch verwaltet wird; diese können nicht auf dem Stack liegen, weil die gültigkeit unabhängig vom Kontrollfluss und dem Aufrufbaum ist.
- Dynamisch allozierte Zeichenketten liegen im Text-Segment.
Nein, weil das Text Segment üblicherweise nicht zur laufzeit verändert wird.
- Bei einem Aufruf von `malloc(3)` wird das Stack-Segment dynamisch erweitert.
Nein, dynamische Speicherverwaltung, würde wenn überhaupt die Halde (Heap) erweitern.
- Der Code von Funktionen wird zusammen mit den Variablen der Funktion im Stack-Segment abgelegt.
Nein, der Code von Funktionen liegt im Text Segment.
? Variablen der Speicherklasse `static` liegen im Daten-Segment.
Eigentlich ja, aber man kann dann diskutieren ob eine nicht-initialsierte `static` Variable, welche im BSS Segment liegt auch teil vom Daten-Segment ist oder nicht.
- Lokale `static`-Variablen werden bei jedem Betreten der zugehörigen Funktion neu initialisiert.
Nein, diese behalten den gleichen Wert unabhängig vom Funktionsaufruf. Ihre Sichtbarkeit ist nur auf eine Funktion beschränkt.
.
0 Was passiert, wenn Sie in einem C-Programm versuchen über einen Zeiger auf ungültigen Speicher zuzugreifen? (2019-06)
- Das Betriebssystem erkennt die ungültige Adresse bei der Weitergabe des Befehls an die CPU (partielle Interpretation) und leitet eine Ausnahmebehandlung ein.
Das Betriebsystem interpretiert die Befehle des Programms nicht selbst. Diese werden direkt von der CPU interpretiert, welches sich in einem unpriviligiertem Modus befindet. Für priviligierte Befehle, muss man mit einem Systemaufruf sich an das Betriebsystem wenden.
- Der Compiler erkennt die problematische Code-Stelle und generiert Code, der zur Laufzeit bei dem Zugriff einen entsprechenden Fehler auslöst.
Zwar möglich, aber das ist nicht der Fall in C, wo Fehlerbehandlung händisch gemacht wird.
- Der Speicher schickt an die CPU einen Interrupt. Hierdurch wird das Betriebssystem angesprungen, das dem gerade laufenden Prozess das Signal `SIGSEGV` (Segmentation Violation) zustellt.
Der "Speicher" würde in diesem Fall keinen Interrupt schicken, weil der Zugriff auf den ungültigen Speicher bewusst und deterministisch aufgetreten ist, und daher als ein "Trap" angesehen werden sollte.
+ Beim Zugriff muss die MMU, soweit vorhanden, die erforderliche Adressumsetzung vornehmen, erkennt die ungültige Adresse und löst einen Trap aus.
Ja; die CPU kann dann je nach Betriebsystem sich entscheiden diesen Trap weiter zu leiten an einen Prozess in Form eines Signals o.Ä.
.
0 Welche der folgenden Aussagen zum Thema Synchronisation ist richtig? (2019-06)
- Der Einsatz von nicht-blockierenden Synchronisationsmechanismen kann zu Verklemmungen (deadlocks) führen.
Nein, bei einer Verklemmung müssen mehere Fäden passiv aufeinander warten, was bei nicht-blockierendeer Syncrhonisation nicht passieren kann.
- Ein Anwendungsprozess muss bei der Verwendung von Semaphoren Interrupts sperren, um Probleme durch Nebenläufigkeit zu verhindern.
Nein, wenn nur weil ein Anwendungsprozess keine Interrupts selbst sperren kann.
+ Gibt ein Faden einen Mutex frei, den er selbst zuvor nicht angefordert hatte, stellt dies einen Programmierfehler dar; eine Fehlerbehandlung sollte eingeleitet werden.
Ja, weil ein Mutex immer von dem gleichem Faden freigegeben werden sollte, wer es auch beansprucht hat (im Gegensatz zur binären Semaphore, wo diese Bedingung nicht gilt).
- Zur Synchronisation eines kritischen Abschnitts ist passives Warten immer besser geeignet als aktives Warten.
Je nach Situation ist es nicht möglich (bspw. im Kern selbst, wo die mechanismen Fehlen um "passiv").
.
0 In einem UNIX-UFS-Dateisystem gibt es symbolische Namen/Verweise (Symbolic Links) und feste Links (Hard Links) auf Dateien. Welche Aussage ist richtig? (2022-07)
+ Für jede reguläre Datei existiert mindestens ein Hard-Link im selben Dateisystem.
Wenn kein "Hardlink" auf eine Datei existiert, dann löscht das
Dateisystem die Datei. Daher muss auf jede existierende Datei
mindestens ein Verweis existieren.
- Wird der letzte Symbolic Link auf eine Datei gelöscht, so wird auch die Datei selbst gelöscht.
Symbolic Links sind nur verweise auf Dateipfade, wobei die Datei
hinter dem Dateipfad nichts von diesem Verweis wissen muss. Es ist
ja auch möglich auf eine nicht-existierende Datei zu verweisen.
- Ein Symbolic Link kann nicht auf Dateien anderer Dateisysteme verweisen.
Nein, weil Symbolic Links nur Pfade sind, welche das Betriebsystem
transparent auflöst, wenn man versucht auf die Datei zuzugreifen,
aber diese nichts über das tatsächliche System wissen. Bei
Hard-Links ist das hingengen nicht der fall, weil diese die gleiche
Inode-Nummer teilen müssen, was von Datei-System abhängig ist.
- Ein Hard Link kann nur auf Verzeichnisse verweisen, nicht jedoch auf Dateien.
Hard-Links können auf beides verweisen, nur ist es mit dem `link`
(bzw. dem `ln` Befehl) Systemaufruf dem Benutzer nicht erlaubt
selbst Verweise auf Verzeichnisse zu erstellen. Damit wird
sichergestellt, dass der Datei-Baum, der durch Hard-Link aufgespannt
wird, nicht zu einem Graphen degeneriert, was Probleme bereiten
würde für Werkzeuge wie `find`.
.
0 Ausnahmesituationen bei einer Programmausführung werden in die beiden Kategorien Trap und Interrupt unterteilt. Welche der folgenden Aussagen ist zutreffend? (2022-07)
- Ein Trap signalisiert einen schwerwiegenden Fehler und führt deshalb immer zur Beendigung des unterbrochenen Programms.
Nein, ein Trap kann auch durch eine nicht eingelagerte Seite (wo
dann mit der MMU versucht wird diese einzulagern, und dann das
Programm weiter läuft) oder durch einen Systemaufruf ausgelöst
werden.
- Ein durch einen Interrupt unterbrochenes Programm darf je nach der Interruptursache entweder abgebrochen oder fortgesetzt werden.
Nein, weil ein Interrupt nichts mit dem Programm zu tun hat, und
daher dieses nicht (direkt) abbrechen sollte.
+ Obwohl Traps immer synchron auftreten, kann es im Rahmen ihrer Behandlung zu Wettlaufsituationen mit dem unterbrochenen Programm kommen.
Ja. Dazu muss man sich an die Definition vom Programm erinnern:
> Festlegung einer Folge von Anweisungen für einen Prozessor, nach der die zur Bearbeitung einer (durch einen Algorithmus wohldefinierten) Handlungsvorschrift erforderlichen Aktionen stattfinden sollen.
darunter kann auch das "Hauptprogramm" vom Betriebsystem verstanden
werden, welches von einem Trap-Handler (ein weiteres Programm) unterbrochen
wird. Sollten beide (möglicherweise auf verschiedenen Kernen) versuchen
synchronisierte Operationen auszuführen, können diese sich gleichzeitig
verklemmen.
- Ein Systemaufruf im Anwendungsprogramm ist der Kategorie Interrupt zuzuordnen.
Nein, ein Systemaufruf ist ein Trap, weil diese deterministisch bei
der Ausführung auftreten, bspw. wenn je nach Rechenarchitektur ein
[`syscall`](https://www.felixcloutier.com/x86/syscall) (x86_64)
Befehl ausgeführt wird.
.
0 Welche Aussage zum Thema Systemaufrufe ist richtig?
- Durch die Bereitstellung von Systemaufrufen, kann ein Benutzerprogramm das Betriebssystem um eigene Funktionen erweitern.
Nein, Systemaufrufe sind die Schnittstelle, die das Betriebssystem Benutzerprozessen zur Verfügung stellt. Benutzerprozesse können (u. a. aus Sicherheitsgründen) das Betriebssystem nicht in dieser Hinsicht verändern.
+ Mit Hilfe von Systemaufrufen kann ein Benutzerprogramm privilegierte Operationen durch das Betriebssystem ausführen lassen, die es im normalen Ablauf nicht selbst ausführen dürfte.
Ja, möchte ein Programm z. B. eine Datei lesen, so sendet es nicht selbst z. B. NVMe-Befehle an den Speicher (privilegierte Operation, könnte zum Verlust aller Daten führen), sondern beauftragt das Betriebssystem (im Fall von POSIX mit `read(2)`), den Dateiinhalt zu lesen.
- Die Bearbeitung eines Systemaufrufs findet immer im selben Adressraum statt, aus dem heraus der Systemaufruf abgesetzt wurde.
Nein, bei der Bearbeitung eines Systemaufrufs im Systemkern (*kernel*) ist der *kernel address space*, der nicht für Benutzerprozesse sichtbar ist, verfügbar.
- Benutzerprogramme dürfen keine Systemaufrufe absetzen, diese sind dem Betriebssystem vorbehalten.
Nein, Benutzerprozesse verwenden Systemaufrufe, um Operationen auszuführen, für die sie selbst nicht die Privilegien besitzen.
.
0 Bei der Behandlung von Ausnahmen (Traps oder Interrupts) unterscheidet man zwei Bearbeitungsmodelle. Welche Aussage hierzu ist richtig?
- Nach dem Beendigungsmodell werden Interrupts bearbeitet. Gibt man z. B. CTRL-C unter UNIX über die Tastatur ein, wird ein Interrupt-Signal an den gerade laufenden Prozess gesendet und dieser dadurch beendet.
Nein, der Interrupt wird vom Betriebssystem abgefangen. Dieses kann dann dem Prozess, der gerade das aktuelle Terminal belegt, ein Signal zustellen. Dieser muss jedoch nicht unbedingt der gerade laufende Prozess sein (Auch Hintergrundprozesse (*daemons*) können sich im Zustand "laufend" befinden).
- Das Beendigungsmodell sieht das Herunterfahren des Betriebssystems im Falle eines schwerwiegenden Fehlers vor.
Nein. Es reicht aus, nur den problematischen Prozess zu beenden. Das hier beschriebene Herunterfahren würde die Robustheit eines Betriebssystems senken.
+ Das Wiederaufnahmemodell ist für Interrupts und Traps gleichermaßen geeignet.
Ja. Auch bei Traps wie z. B. einem Seitenfehler, bei dem ein Prozess auf eine Seite zugreift, die gerade ausgelagert ist (*swapping*), ist das Weiterlaufen des Prozesses, nachdem die Seite wieder eingelagert wurde, sinnvoll.
- Interrupts sollten nach dem Beendigungsmodell behandelt werden, weil ein Zusammenhang zwischen dem unterbrochenen Prozess und dem Grund des Interrupts bestehen kann.
Nein, Interrupts sind unvorhersagbare Ereignisse wie z. B. das Eingehen eines Netzwerkpaketes oder das Drücken einer Taste auf der Tastatur. Der aktuell laufende Prozess sollte i. A. nicht wegen einem derartigen Ereignis beendet werden, da dieser nicht unbedingt einen Bezug zu einer derartigen Eingabe hat.
.
0 Welche Aussage zum Thema Programme und Prozesse ist richtig?
+ Ein Programm kann durch mehrere Prozesse gleichzeitig ausgeführt werden
Ja, teste z. B. `sleep 5 & sleep 5`. Hier wird das Programm `sleep` gleichzeitig ausgeführt, weswegen dieser Befehl nur 5 Sekunden (statt 10) benötigt.
- In einem Prozess kann immer nur ein Programm ausgeführt werden.
Nein, auf POSIX kann mit `exec(3)` ein Prozess zu einem anderen Programm wechseln.
- Ein Prozess kann gleichzeitig mehrere verschiedene Programme ausführen.
Nein, ein Prozess ist *ein* Programm in Ausführung. Es kann zwar zu einem anderen Programm gewechselt werden, möchte man jedoch nebenläufig ein anderes Programm ausführen, muss man einen neuen Prozess erzeugen (`fork(3) + exec(3)` oder `posix_spawn(3)`)
- Der Compiler erzeugt aus mehreren Programmteilen (Module) einen Prozess.
Nein. Einerseits erzeugt der Compiler Objektdateien, die der Linker zu einem Programm bindet, andererseits wird ein Prozess weder vom Compiler noch vom Linker, sondern vom Betriebssystem zur Laufzeit erzeugt.
.
0 Ein laufender Prozess wird in den Zustand bereit überführt. Welche Aussage passt zu diesem Vorgang?
- Es ist kein direkter Übergang von laufend nach bereit möglich.
Nein.
+ Der Prozess wird durch einen anderen Prozess verdrängt, oder gibt die CPU freiwillig ab.
Ja, der Prozess könnte noch weiterrechnen, ist also *bereit*.
- Der Prozess wartet auf Daten von der Festplatte.
Nein, der Prozess ist dann *blockiert*, bis die Daten gelesen sind.
- Der Prozess wartet mit dem Systemaufruf waitpid(3) auf die Beendigung eines anderen Prozesses.
Nein, der Prozess ist dann *blockiert*, bis der andere Prozess beendet ist. Eine Ausnahme könnte hier der Aufruf mit dem Parameter `WNOHANG` darstellen, mit welchem der Systemaufruf sofort zurückgibt.
.
0 Welche Seitennummer und welcher Offset gehören bei einstufiger Seitennummerierung und einer Seitengröße von 1024 (= 2¹⁰
) Bytes zu folgender logischer Adresse: 0x0802?
+ Seitennummer 0x2, Offset 0x2
Ja, `0x0802 = 0b0000 10|00 0000 0010` also ist die Seitennummer `0b0000 10 = 0x2` und der Offset `0b00 0000 0010 = 0x2`
- Seitennummer 0x8, Offset 0x8
Nein. Hier sind beide falsch.
- Seitennummer 0x2, Offset 0x8
Nein. Hier ist der Offset offensichtlich falsch.
- Seitennummer 0x8, Offset 0x2
Nein. Da hier der Offset 10 Bit und kein Vielfaches von 4 hat, darf man nicht einfach die Hexadezimaldarstellung an der Grenze eines *nibbles* (= Halbbyte, 4 Bit, ein Zeichen in Hexadezimaldarsetllung) zerlegen. Deswegen ist hier die Seitennummer falsch.
.
0 Welche Aussage zu UNIX/Linux-Dateideskriptoren ist korrekt?
+ Nach dem Aufruf von fork(2) teilen sich Eltern und Kindprozess die den gemeinsamen Dateideskriptoren zu Grunde liegenden Kernel-Datenstrukturen.
Ja, vergleiche zum Beispiel `sister`, bei der ein Kindprozess für die Bearbeitung einer Anfrage zuständig war und den Dateideskriptor verwendet, der im Elternprozess von `accept(3)` zurückgegeben wurde.
- Da Dateideskriptoren Zeiger auf Betriebssystem-Interne Strukturen sind, können diese zwischen Prozessen geteilt werden.
Der Dateideskriptor ist lediglich ein Index in die Dateideskriptortabelle pro Prozess im *kernel space*. Da diese nicht geteilt ist, ist auch das Teilen von Dateideskriptoren im Allgemeinen nicht zielführend.
- Der Dateideskriptor enthält die nötigen Metadaten einer Datei und ist auf der Festplatte gespeichert.
Nein, das wäre die `inode`. Der Dateideskriptor ist eine Referenz (jedoch kein Pointer) auf eine Datenstruktur im Betriebssystem, die u. a. auch die Position des Prozesses in der Datei enthält.
- Das Flag `FD_CLOFORK` eines Dateideskriptors sorgt dafür, dass der Dateideskriptor bei einem Aufruf von `fork(2)` automatisch geschlossen wird.
Nein, dieses Flag existiert nicht. Angespielt wird hier auf `FD_CLOEXEC`, was die beschriebene Wirkung bei einem Aufruf von `exec(3)` zeigt.
.
0 Welche Aussage über die Koordinierung von kritischen Abschnitten unter Unix ist richtig?
+ Für die Synchronisation zwischen dem Hauptprogramm und einer Signalbehandlungsfunktion sind Schlossvariablen (Locks) ungeeignet.
Ja. Signalbehandlung stellt asymmetrische Nebenläufigkeit dar. Der aktuelle Ausführungsstrang wird also unterbrochen, um zum *signal handler* zu springen. Wenn dieser nun versucht, ein *lock* zu sperren, das schon vom Hauptprogramm gesperrt wurde, so wartet dieser auf das Hauptprogramm, welches jedoch durch die Signalbehandlung unterbrochen ist. Es kommt zu einer Verklemmung (*deadlock*).
- Ein Unix-Prozess kann durch das Sperren von Unterbrechungen (Interrupts) den Speicherzugriff in einem kritische Abschnitte [sic!] synchronisieren.
Nein. Einerseits ist das Sperren von Interrupts, die ja nicht notwendigerweise im Zusammenhang mit dem Prozess stehen müssen, eine privilegierte Operation, die dem Betriebssystem vorenthalten ist. Andererseits würde dies bei manchen Formen von Nebenläufigkeit nicht den gewünschten Effekt erzielen, da z. B. auf einem Multiprozessorsystem mehrere leichtgewichtige Prozesse (*kernel-level threads*) echt nebenläufig Speicherzugriffe durchführen können, ohne dass einer der Prozesse durch einen Interrupt verdrängt wird.
- In einem Unix-Prozess kann es keinen kritischen Abschnitt geben, da immer nur ein Aktivitätsträger pro Prozess aktiv ist.
Nein. Die Ressourcen eines schwergewichtigen Prozesses können zwischen verschiedenen leichtgewichtigen Prozessen geteilt sein, die nebenläufig einen Aktivitätsstrang ausführen.
- Kritische Abschnitte können unter Unix nur mit Semaphoren synchronisiert werden.
Nein. es gibt vielerlei Synchronisationsmechanismen für verschiedene Arten von Nebenläufigkeit, für kritische Abschnitte kann insbesondere auch `pthread_mutex` verwendet werden.
.
1 Welche der Aussagen zum folgenden Programmfragment sind richtig?
|
|
|~~~~~
|static int a = 2022;
|void f1 (const int *y) {
| static int b;
| int c;
| char *d = malloc(0x802);
| void (*e)(const int *) = f1;
| y++;
| //...
|}
|~~~~~
|
+ `e` liegt im Stacksegment und zeigt in das Textsegment.
Ja. `e` ist ein Zeiger auf die Funktion `f1`. Funktionen liegen im Textsegment. Zudem ist `e` eine lokale Variable, liegt also im Stacksegment.
- `c` ist mit dem Wert 0 initialisiert.
Nein, denn nur globale Variablen werden mit 0 initialisiert, wenn sie nicht anderweitig initialisiert werden. `c` ist uninitialisiert.
- Die Anweisung `y++` führt zu einem Laufzeitfehler, da `y` konstant ist.
Ja. Das Ziel von `y` ist hier `const`, `y` selbst jedoch nicht. Pointerarithmetik auf `y` ist also erlaubt. Eine Merkregel hierfür lautet "`const` bezieht sich auf das Schlüsselwort links davon, außer es steht ganz links. Dann bezieht sich `const` auf das Schlüsselwort rechts davon." Hier bezöge sich const also auf `int`, nicht auf `*`.
+ `d` ist ein Zeiger, der in den Heap zeigt.
Ja. Mit `malloc(3)` wird Heapspeicher allokiert.
+ `a` liegt im Datensegment.
Ja. `a` ist eine globale Variable. Globale Variablen liegen im Datensegment. `a` liegt im Datensegment.
? `y` liegt im Stacksegment.
Gemäß der x86_64-POSIX-ABI liegen Funktionsparameter wie `y` in einem Register, das ist jedoch nicht plattformunabhängig garantiert. Ich vermute trotzdem, die erwartete Antwort hier ist "ja".
- `b` liegt im Stacksegment.
Durch die Verwendung von `static` innerhalb einer Funktion erzeugt man eine Variable, die zwar nur in diesem Gültigkeitsbereich sichtbar ist, jedoch die Semantik einer globalen Variable besitzt. Demnach liegt `b` im Datensegment.
- Die Speicherstelle, auf die `d` zeigt, verliert beim Rücksprung aus der Funktion `f1()` ihre Gültigkeit.
Nein, da diese Speicherstelle im Heap liegt und - im Gegensatz zu Stackspeicher - erst durch den Aufruf von `free(3)` ungültig wird.
.
# https://sys.cs.fau.de/extern/lehre/ws23/sp2/pruefung/klausuren/2023s-SP-Klausur_www.pdf
0 Man unterscheidet zwischen privilegierten und nicht-privilegierten Maschinenbefehlen. Welche Aussage ist richtig? (2023-07)
? Privilegierte Maschinenbefehle dürfen in Anwendungsprogrammen grundsätzlich nicht verwendet werden.
Abhängig davon, was man unter "grundsätzlich" versteht, sollte es eigentlich heißen, dass man Privilegierte Maschinenbefehle nicht verwenden darf.
- Die Benutzung eines privilegierten Maschinenbefehls in einem Anwendungsprogramm führt zu einer asynchronen Programmunterbrechung.
Nein, es würde zu einer _syncrhonen_ Programmunterbrechung führen, weil diese direkt durch den Versuch den Befehl zu interpretieren ausgelöst werden würde.
? Mit nicht-privilegierten Befehlen ist der Zugriff auf Geräteregister grundsätzlich nicht möglich.
Abhängig davon, was man unter "grundsätzlich" versteht; auf Intel CPUs werden hierzu gesonderte Befehle benutzt, aber _grundsätzlich_ könnte eine Rechnerarchitektur auch durch Speicherschutz einen nicht-privilegierten Prozess daran hindern mittels nicht-priviligierten Befehlen auf Geräteregister zuzugreifen.
- Privilegierte Maschinenbefehle können durch Betriebssystemprogramme implementiert werden.
Nein, diese werden nicht durch das Betriebsystem(programme) implementiert, sondern werden von der CPU ausführt, angenommen es befindet sich im richtigen Privilegienmodus.
.
0 Was passiert, wenn Sie in einem C-Programm über einen ungültigen Zeiger versuchen auf Speicher zuzugreifen? (2023-07)
- Das Betriebssystem erkennt die ungültige Adresse bei der Weitergabe des Befehls an die CPU (partielle Interpretation) und leitet eine Ausnahmebehandlung ein.
Nein, das Betriebssystem gibt den Befehl selbst nicht an die CPU weiter, und überprüft daher auch nicht ob die Addressen gültig seien.
- Der Compiler erkennt die problematische Code-Stelle und generiert Code, der zur Laufzeit bei dem Zugriff einen entsprechenden Fehler auslöst.
Nein, der Übersetzer kann sich nicht immer sicher sein wo die problematische Code-Stellen sind, und generiert (für C) auch nicht den Code um den Fehler auszulösen.
+ Beim Zugriff über den Zeiger muss die MMU die erforderliche Adressumsetzung vornehmen, erkennt die ungültige Adresse und löst einen Trap aus.
Ja, wenn die MMU die Seite nicht auflösen kann, dann wird es der CPU ein Trap zustellen und löst damit eine syncrhone Unterbrechung im Programm aus.
- Der Speicher schickt an die CPU einen Interrupt. Hierdurch wird das Betriebssystem angesprungen, das den gerade laufenden Prozess mit einem "Segmentation fault"-Signal unterbricht.
Da der Fehler direkt bedingt ist durch das Verhalten des Programms, würde es sich hier um ein "Trap" und kein "Interrupt" (unvorhersebar) handeln.
.
0 Welche der folgenden Aussagen zum Thema Dateispeicherung sind richtig? (2023-07)
- Bei indizierter Speicherung von Dateien ensteht externer Verschnitt auf der Platte.
Nein, weil für Dateien ganze Blöcke vergeben und herumgeschoben werden können, womit externer Verschnitt vermieden wird, weil es nicht dazu kommen kann, dass es lücken gibt die "zu klein" wären für eine Datei.
+ Festplatten eignen sich besser für sequentielle als für wahlfreie Zugriffsmuster.
Ja, weil der Lese-Schreib-Kopf der Festplatte schneller auf lokal-nahe Daten in der gleichen Leserichtung zugreifen kann, ist ist der Zugriff auch dementsprechend schneller.
- Eine Datei in einem Winows-NT-Dateisystem kann nur genau einen Dateinamen haben, da dieser in ihrem Master-File-Table-Eintrag gespeichert ist.
Nein, obwohl der Datei Name für ein "fileID" im MFT gespeichert ist, wird es auch in Verzeichnissen gespeichert und erlaubt es ähnlich zu einem UFS-Inspiriertem Datei-System, mehere Namen auf ein "Volume" zu definieren.
- Da SSDs ohne mechanische Komponenten auskommen, gibt es auch durch häufige Schreiboperationen keinen Verschleis.
Nein, Verschleis hat nichts mit der technischen Umsetzung des Speichermediums zu tun.
.
0 Welches Signal wird bei einer Speicherschutzverletzung versendet? (2023-07)
- SIGKILL
`signal.h(7p)` sagt "Kill (cannot be caught or ignored)".
+ SIGSEGV
`signal.h(7p)` sagt "Invalid memory reference".
- SIGTERM
`signal.h(7p)` sagt "Termination signal".
- SIGABORT
`signal.h(7p)` sagt "Process abort signal".
.
\ No newline at end of file
# https://sys.cs.fau.de/extern/lehre/ws23/sp2/pruefung/klausuren/2015w-SP-Klausur-www.pdf
0 (2016-02) Wie funktioniert Adressraumschutz durch Eingrenzung?
- Der Lader positioniert Programme immer so im Arbeitsspeicher, dass
| unerlaubte Adressen mit nicht-existierenden physikalischen
| Speicherbereichen zusammenfallen.
Nein, das ist nicht was mit "Eingrenzung" gemeint ist. Ansonsten
wäre es recht umständlich, die Lücken im physikalischem Speicher
welche für Memory-Mapped I/O, den BIOS, PCI, etc. für eine dynamische
Anzahl an Prozessen auszunutzen.
- Begrenzungsregister legen einen Adressbereich im logischen
| Adressraum fest, auf den alle Speicherzugriffe beschränkt werden.
Nein, bei Eingrenzung ist der Addressbereich im physikalischen
Adressraum.
+ Begrenzungsregister legen einen Adressbereich im physikalischen
| Adressraum fest, auf den alle Speicherzugriffe beschränkt werden.
Ja, Bei Eingrenzung, bzw. "Einfriedung" wird ein Teil des
physikalischen Speichers mittels Grenzregistern überprüft, noch
bevor der Addressbus angeprochen wird.
- Jedes Programm bekommt zur Ladezeit mehrere Wertepaare aus Basis-
| und Längenregistern zugeordnet, die die Größe aller Segmente des
| darin laufenden Prozesses festlegen.
Nein, das umschreibt Segmentierung.
.
0 (2016-02) Ein Prozess wird vom Zustand blockiert in den Zustand
| bereit überführt. Welche Aussage passt zu diesem Vorgang?
+ Der Prozess hat auf das Einlesen von Daten von der Festplatte
| gewartet, die nun verfügbar sind.
Ja, die Ressource (Daten von der Festplatte) waren nicht verfügbar,
und waren daher Grund weshalb der Prozess im Blokiertem Zustand war.
Man geht nicht direkt von "Blokiert" nach "Laufend" über, sondern
macht den Umweg über "Bereit", d.h. der Prozess ist in der
Bereitliste vom Scheduler eingeordnet.
? Ein Prozess, der zu einem früheren Zeitpunkt aufgrund von
| Speichermangel auf den Hintergrundspeicher ausgelagert wurde, ist nun
| wieder eingelagert und kann weiterlaufen.
Ich glaube das sollte falsch sein, weil ein Prozess _als solches_
nicht _aufgrund_ von Speichermangel in den Hintergrundspeicher
ausgelagert wird.
- Ein anderer Prozess wurde vom Betriebssystem verdrängt und der
| erstgenannte Prozess wird nun auf der CPU eingelastet.
Nein, dieser erstgennante Prozess wäre "Bereit" gewesen, und würde
nun im Zustand "Laufend" sein. Der andere Prozess wäre den
Umgekehrten weg, von "Laufend" nach "Bereit" übergegangen (diesem
Fehlt nichts außer die CPU).
- Es ist kein direkter Übergang von blockiert nach bereit möglich.
Nein, es ist kein Überganz von "Blokiert" nach "Laufend" möglich,
aber ein blokierter Prozess kann in "Bereit" überführt werden, sobal
der Grund für das Blokiertsein aufgelöst wurde.
.
0 (2016-02) Wodurch kann es zu Seitenflattern kommen?
- Wenn die Zahl der residenten Seiten die Größe des physikalischen
| Speichers überschreitet.
Nein, in diesem Fall würde es nur zu einer Auslagerung von Seiten
kommen, was aber nicht zwingend "Seitenflattern" bedingen würde.
- Durch Programme, die eine Defragmentierung auf der Platte
| durchführen.
Nein, Seitenflattern bezeichnet das pathologische Phänomen, wenn die
Seitenumlagerungsstrategie eine Menge von Seiten zu häufig Umlagern
muss, und damit den Zugriff auf den Speicher verlangsamt. Es an
sich hat nichts mit dem Dateisystem zu tun.
+ Wenn ein Prozess zum Weiterarbeiten immer gerade die Seiten
| benötigt, die durch das Betriebssystem im Rahmen einer globalen
| Ersetzungsstrategie gerade erst ausgelagert wurden.
Ja, das ist die Definition vom Begriff. Siehe im [Wosch
Glossar](https://www4.cs.fau.de/~wosch/glossar.pdf), den Eintrag
"Flattern".
- Wenn zu viele Prozesse im Rahmen der mittelfristigen Einplanung auf
| den Hintergrundspeicher ausgelagert wurden (swap-out).
Nein, der Begriff ist unabhängig von der Planungsstrategie zu
verstehen.
.
0 (2016-02) Man unterscheidet bei Programmunterbrechungen
| zwischen Traps und Interrupts. Welche Aussage dazu ist richtig?
- Die Behandlung eines Traps führt immer zur Beendigung des
| unterbrochenen Programms, da Traps nur durch schwerwiegende Fehler
| ausgelöst werden.
Nein, Traps können auch durch Systemaufrufe (Quasi-Nachrichten an
das Betriebsystem) oder durch fehlende aber einlagerbare
Speicherseiten ausgelöst werden.
- Da das Betriebssystem nicht vorhersagen kann, wann ein
| Benutzerprogramm einen Systemaufruf absetzt, sind Systemaufrufe als
| Interrupts zu klassifizieren.
Nein, Systemaufrufe treten immer dann deterministisch auf, wenn der
entsprechende Maschinenbefehl (`syscall` auf , `int` auf x86,
`ecall` of RISC-V).
+ Bei der mehrfachen Ausführung eines unveränderten Programms mit
| gleicher Eingabe treten Traps immer an den gleichen Stellen auf.
Ja, das folgt aus dem Determinismus von Traps.
- Da Interrupts in keinem Zusammenhang mit dem unterbrochenen Programm
| stehen, muss der Prozessorstatus des unterbrochenen Programms
| während der Behandlung nicht speziell gesichert werden.
Doch, weil die Behandlung des Interrupts den Prozessorstatus
durchaus beeinflusst, und damit es aber das unterbrochenen Programm
nicht direkt verändert (es braucht ja nur für die Behandlung die CPU
für eine kurze Zeit), wird vor der Behandlung der Zustand des
Prozessors gespeichert -- bspw. in dem dieser auf dem Stack kopiert
wird, dann die Unterbrechungsroutine eingeleitet wird, und danach
wiederhergestellt wird.
.
0 (2016-02) Namensräume dienen u. a. der Organisation von
| Dateisystemen. Welche Aussage ist richtig?
- Das Arbeitsverzeichnis eines Prozesses definiert die Wurzel des
| hierarchisch organisierten Namensraums in einem Dateisystem.
Nein, die Wurzel des Namensraums ist (zunächst, siehe `chroot(2)`)
fest und unabhängig von Prozessen und ihren Arbeitsverzeichnissen.
+ In einem hierarchisch organisierten Namensraum dürfen gleiche Namen
| in unterschiedlichen Kontexten enthalten sein.
Ja, so kann es sowohl `/home/alice/shopping` und
`/home/bob/shopping` geben. Nur in einem Verzeichnis darf _ein_
Name _einmal_ vergeben werden.
? Flache Namensräume erlauben pro Benutzer nur einen Kontext.
Ein Benutzer hat immer nur einen Kontext, unabhängig davon ob der
Namensraum flach ist oder nicht.
- Hierarchische Namensräume werden erzeugt, indem man in einem Kontext
| symbolische Verweise auf Dateien einträgt.
Nein, hierarchische Namensräume können auch nur von Hardlinks
aufgespannt werden (wie es der Fall war bevor BSD diese für UNIX
implementiert hat).
.
0 (2016-02) Welche Aussage zu Programmbibliotheken ist richtig?
- Statische Bibliotheken können in C nicht implementiert werden.
Doch, wir können eiene Menge von Obejktdateien mit `ar` zu einem
Archiv zusammenbinden. Das war historisch auch der standard.
- Eine Änderung am Code einer statischen Bibliothek (z. B. Bugfixes)
| erfordert kein erneutes Binden der Programme, die diese Bibliothek
| benutzen.
Nein, weil die Bibliotheken beim Binden des Programms eingebunden
werden, und gleich bleiben wenn das Archiv geändert wird.
+ Eine statische Bibliothek, mit der ein Programm gebunden wurde, muss
| zum Ladezeitpunkt des Programms nicht mehr als eigenständige Datei
| im Dateisystem vorhanden sein.
Ja, weil die Inhalte des Archivs wurden beim Binden des Archivs
in die ausführbare Datei "kopiert", und werden zur Ausführung aus
der ausführbaren Datei geladen und nicht aus dem ursprünglichem
Archiv, was daher auch nicht mehr existieren muss.
- Beim Binden mit einer statischen Bibliothek werden in einem Programm
| nur Verweise auf verwendete Symbole der Bibliothek angelegt.
Nein, das wäre dynamisches Binden. Beim statischem Binden wird der
gesammte Programmtext der Bibilothek im Programm kopiert.
.
0 (2016-02) Welche Seitennummer und welcher Versatz gehören bei
| einstufiger Seitennummerierung und einer Seitengröße von 2048 Bytes zu
| folgender logischer Adresse: `0xba1d`
- Seitennummer `0xb`, Versatz `0xa1d`.
Nein, weil `(0xb << 11) | 0xa1d = 0x5a1d`.
+ Seitennummer `0x17`, Versatz `0x21d`.
Ja, weil `(0x17 << 11) | 0x21d = 0xba1d`, oder alternativ:
(0xba1d & (2048-1)) = 0x21d
(0xba1d & ~(2048-1)) >> 11 = 0x17
Die Idee hier ist, dass man mit einer Seitengröße von 2048 Byte mit
11 Bit addressieren kann. Die ersten 11 Byte von
0b1011101000011101 = 0xba1d
sind
0b01000011101 = 0x21d
und damit bleiben für die Page nummer nur noch
0b10111 = 0x17
- Seitennummer `0xba`, Versatz `0x1d`.
Nein, weil `(0xba << 11) | 0x1d = 0x5d01d`.
- Seitennummer `0x2e`, Versatz `0x21d`.
Nein, weil `(0x2e << 11) | 0x21d = 0x1721d`.
.
0 (2016-02) Man unterscheidet die Begriffe Programm und
| Prozess. Welche der folgenden Aussagen zu diesem Themengebiet ist
| richtig?
- Ein Programm kann immer nur von einem Prozess gleichzeitig
| ausgeführt werden.
Nein, mehere Prozessinstanzen können das gleiche Programm ausführen,
zumindest auf einem UNIX System, welches keine solchen
Einschänkungen vorsieht.
- Das Programm ist der statische Teil (Rechte, Speicher, etc.), der
| Prozess der aktive Teil (Programmzähler, Register, Stack).
Nein, der Speicher ist auch "aktiv" an der Ausführung beteiligt,
aber ansonsten ist auch die Begriffsunterscheidung hier (für mich)
nicht klar.
- Wenn ein Programm nur einen aktiven Ablauf enthält, nennt man diesen
| Prozess, enthält das Programm mehrere Abläufe, nennt man diese
| Threads.
Nein, der Begriff "Threads" (Fäden) verstanden als
"Leichtgewichtiger Prozess", beschreibt einen Ausführungsstrang,
welches mit anderen Threads _in einem Prozess_ den gleichen
Speicherraum teil. Jedenfalls nennt man ein Programm weder Prozess
oder Threads (Kategorien-Fehler), sondern ein Programm wird in
bzw. von einem Prozess oder in einem Thread ausgeführt.
+ Ein Prozess ist ein Programm in Ausführung - ein Prozess kann aber
| während seiner Lebenszeit auch mehrere verschiedene Programme
| ausführen.
Ja, das ist die Definition. Ein Prozess kann sein Programm mitttels
eines `exec` Systemaufrufs auswechseln.
.
1 (2016-02) Welche der folgenden Aussagen zum Thema
| Seiteneinlagerungs- und Seitenersetzungsstrategien ist richtig?
+ Die Ersetzungsstrategie MIN ist in der Praxis nur schwer
| realisierbar, weil Wissen über das zukünftige Verhalten des
| Gesamtsystems notwendig ist.
Ja, MIN (oder "B0", "OPT") müsste die Referenzfolge zuvor wissen;
der Ansatz ist "meist nur zum Vergleich von Strategien brauchbar".
- Bei der Verwendung von globalen Seitenersetzungsstrategien sind
| Seitenfehler vorhersagbar bzw. reproduzierbar.
Nein, weil bei einer globalen Ersetzungsstrategie verhällt sich ein
Seitenfehler wie ein _Interrupt_, weil es nicht direkt vorhersehbar
ist anhand vom Verhalten des eigenen Programms.
- Mit dem Systemaufruf `free()` kann eine Speicherseite in den
| Freiseitenpuffer eingefügt werden.
Nein, mit `free()` wird im _Freispeicher_ ein reservierte
Speicherbereich zurück gegeben. Es ist darüber hinaus kein
Systemaufruf, sondern verwaltet Speicher im Userspace.
+ Die Ersetzungsstrategie LRU ersetzt die am längsten nicht mehr
| referenzierte Seite.
Ja, da Akronym LRU -- _Least Recently Used_ -- deutet darauf hin.
+ Bei der Verwendung von lokalen Seitenersetzungsstrategien sind
| Seitenfehler vorhersagbar bzw. reproduzierbar.
Ja, weil bei einer lokalen Ersetzungsstrategie ist ein Seitenfehler
ein _Trap_.
- Die Ersetzungsstrategie LRU benötigt im Vergleich zu FIFO immer
| weniger Versuche, bis eine zu ersetzende Seite gefunden werden kann.
Nein, FIFO (_First In, First Out_) kann immer gleich bestimmen
welche Seite zu ersetzen ist, während man bei LRU (_Least Recently
Used_) erst berechnen müsste welche Seite am längsten nicht benutzt
wurde.
- Lokale Seitenersetzungsstrategien wählen die zu ersetzende Seite
| immer aus der Menge aller im System verfügbaren Seitenrahmen aus.
Nein, bei einer lokalen Ersetzungsstrategien, ist ein Prozess selbst
dafür verantwortlich seine eigenen Seiten zu ersetzen.
+ Bei der Ersetzungsstrategie LFU wird die am seltensten referenzierte
| Seite aus dem Speicher verdrängt.
Ja, das Akronym -- _Least Frequently Used_ -- deutet darauf hin.
.
1 (2016-02) Welche der folgenden Aussagen zum Thema Synchronisation
| sind richtig?
- Ein Mutex kann ausschließlich für einseitige Synchronisation
| verwendet werden.
Nein, ein Mutex kann auch zur mehrseitigen synchronisation bei
assymetrischer Nebenläufigkeit dienen (die _Mutual Exclusion_,
welche mit einem "Mut Ex" umgesetzt wird, wird eben oft dazu benutzt
um andere Handlungsstränge davon abzuhalten, einen kritischen
Abschnitt zu betreten, wo man unter der Annahme arbeiten will, dass
keine Wettlaufsituationen auftreten können).
- Der Einsatz von nicht-blockierenden Synchronisationsmechanismen kann
| zu Verklemmungen (dead-locks) führen.
Nein, ein Dead-Lock setzt voraus, dass ein Faden blockiert werden
kann, bspw. durch ein Mutex oder eine Semaphore, was aber nicht der
Fall ist bei nicht-blockierender Syncrhonisation, dessen Ansatz
darauf basiert atomare Befehle zu verwenden, um transaktional
kritische Operationen auszuführen. Bemerke aber, dass es dennoch zu
einem Live-Lock, oder einem effektivem Live-Lock kommen kann, weil
Nicht-Blockierende synchronisation, nicht bedeutet, dass der Ansatz
Wartefrei/Sperrfrei/Behinderunfsfrei sein muss.
+ Die V-Operation kann auf einem Semaphor auch von einem Faden
| aufgerufen werden, der zuvor keine P-Operation auf dem selben
| Semaphor ausgeführt hat.
Ja, im Gegensatz zu einem Mutex ist der Benutzer nicht dazu
gezwungen erst die Semaphore zu "sperren" (P) und dannach auf dem
gleichen Faden wieder "freizugeben" (V).
- Ein Anwendungsprozess muss bei der Verwendung von Semaphoren
| Interrupts sperren, um Probleme durch Nebenläugkeit zu verhindern.
Nein, ein Anwendungsprozess kann selbst keine interrupts Sperren,
und unabhängig davon ist das nicht notwendig, da Semaphoren selbst
eine Syncrhonisationsmittel sind.
+ Für nichtblockierende Synchronisation werden spezielle Befehle der
| Hardware genutzt, die wechselseitigen Ausschluss garantieren.
Ja, Operationen wie CAS (Compare and Swap), TAS (Test and Set), FFA
(Fetch and Add) werden bei verschienden Architekturen auf
verschiedene Befehle abgebildet, welche -- ohne
Betriebsystemunterstützung -- in der Lage sind die meist komplexen
Operationen atomar auszuführen. Siehe Beispilesweise auf x86
[`CMPXCHG`](https://c9x.me/x86/html/file_module_x86_id_41.html),
[`BTS`](https://c9x.me/x86/html/file_module_x86_id_25.html) oder
[`XADD`](https://c9x.me/x86/html/file_module_x86_id_327.html) mit
dem `LOCK` prefix. Auf anderen Architekturen (häufig RISC-artig)
können diese nachgebildet werden mittels
[Load-link/store-conditional](https://en.wikipedia.org/wiki/Load-link/store-conditional)
befehlen (siehe Vorlesungsfolien von der Vorlesung [Concurrent
Systems](https://www4.cs.fau.de/Lehre/WS19/V_CS/Vorlesung/folien/handout/5-elops.pdf)
für mehr zu dem Thema).
+ Semaphore können sowohl für einseitige als auch für mehrseitige
| Synchronisation verwendet werden.
Ja. Bei einseitige Synchronisation kann man zwei signalisierende
Semaphoren benutzen, bei mehrseitige Synchronisation ein
ausschließende Semaphore.
- Zur Synchronisation eines kritischen Abschnitts ist passives Warten
| immer besser geeignet als aktives Warten.
Nein, allgemein kann man das nicht sagen, weil es auch davon abhängt
ob man ein Betriebsystem hat, oder wie lange man erwartet zu warten,
wo die Kosten von passivem Warten ggf. nicht zu vernachlässigen
wären.
+ Gibt ein Faden einen Mutex frei, den er selbst zuvor nicht
| angefordert hatte, stellt dies einen Programmierfehler dar; der
| fehlerhafte Prozess sollte dann abgebrochen werden.
Ja, weil das die beabsichtige Verwendung einer Semaphore verletzen
würde, welches eben diese Benutzung vorraussetzt.
.
# https://sys.cs.fau.de/extern/lehre/ws23/sp2/pruefung/klausuren/2019w-SP-Klausur_www.pdf
0 Wozu dient die CAS (Compare-And-Swap) Instruktion? (2020-02)
- Zur Realisierung einer effizienten Verdrängungssteuerung bei einseitiger Synchronisation.
Nein. Dies wird je nach Art der einseitigen Synchronisation (z. B. Blockieren der Signalbehandlung) unterschiedlich, jedoch nicht mit CAS, gelöst.
- Um in einem System mit Seitennummerierung (Paging) Speicherseiten in die Auslagerungspartition (swap area) schreiben zu können.
Nein, für die Implementation dessen ist das Betriebssystem zuständig.
+ Um auf einem Multiprozessorsystem einfache Modifikationen an Variablen ohne Sperren implementieren zu können.
Ja, die CAS-Instruktion erlaubt, eine Variable transaktional nur dann auf einen neuen Wert zu setzen,
wenn sie nicht anderweitig modifiziert wurde.
Mittels einer CAS-Schleife kann somit der Wert einer Variable atomar ohne gegenseitigen Ausschluss geändert werden.
- Um bei der Implementierung von Schlossvariablen (Locks) aktives Warten zu vermeiden.
Nein, allein mit einer atomaren Variable *locks* zu implementieren, setzt aktives Warten auf die Änderung des Wertes voraus.
Stattdessen müsste für passives Warten der wartende Prozess in den Zustand blockiert übergehen, bis er von einem anderen Prozess "geweckt" wird. Hierfür ist Betriebssystemunterstützung in Form eines Schedulers nötig.
.
0 Was versteht man unter RAID 0? (2020-02)
- Ein auf Flash-Speicher basierendes, extrem schnelles Speicherverfahren.
Nein, RAID 0 und andere RAID *level* bezeichnen, wie Daten auf mehrere Speichermedien (ein *redundant array of independent disks*) aufgeteilt werden
und nicht, wie der Speicher auf der physikalischen Ebene implementiert ist.
- Auf Platte 0 wird Parity-Information der Datenblöcke der Platten 1 - 4 gespeichert.
Nein, RAID 0 sorgt nicht für Redundanz, sondern dient nur zur Beschleunigung von Speicherzugriffen. Es werden keine Paritätsdaten angelegt.
+ Datenblöcke eines Dateisystems werden über mehrere Platten verteilt gespeichert.
Ja, ein Begriff hierfür lautet *striping*
- Datenblöcke werden über mehrere Platten verteilt und repliziert gespeichert.
Nein, RAID 0 sorgt nicht für Redundanz, sondern dient nur zur Beschleunigung von Speicherzugriffen. Es werden keine Replikate von Datenblöcken angelegt.
.
0 Welche der folgenden Aussagen zum Thema Adressräume ist richtig? (2020-02)
- Virtuelle Adressräume sind Voraussetzung für die Realisierung logischer Adressräume.
Nein, andersherum. Ein logischer Adressraum ist ein zusammenhängender Adressbereich. Der virtuelle Adressbereich setzt auf diesen auf, indem Adressen, die nicht auf den Speicher abgebildet werden können, beim Zugriff einen Adressübersetzungsfehler auslösen. Die Zuordnung vom logischen zum realen Adressraum kann somit als totale Funktion betrachtet werden, im Gegensatz dazu kann die Zuordnung vom virtuellen zum realen Adressraum als partielle Funktion auf den logischen Adressen betrachtet werden (vgl. SP1 B Vl. 2 S. 12 ff., 27).
- Der virtuelle Adressraum kann nie größer sein als der im Rechner vorhandene Hauptspeicher.
Nein, durch Speichervirtualisierung können Seiten auf den Hintergrundspeicher ausgelagert werden (*swapping*).
- Die maximale Größe des virtuellen Adressraums kann unabhängig von der verwendeten Hardware frei gewählt werden.
Nein, z. B. ist diese auf einem 32-Bit-System in der Regel auf höchstens 2³² Datenwörter beschränkt (In der Praxis also nur 4 GiB).
+ Der physikalische Adressraum ist durch die gegebene Hardwarekonfiguration definiert.
Ja, jede physikalische Adresse korrespondiert zu einer tatsächlichen Stelle im Hauptspeicher, von dem eine bestimmte Menge in einem System verbaut ist.
.
0 Welche Aussage zu Zeigern in C-Programmen ist richtig? (2020-02)
+ Zeiger können verwendet werden, um in C eine call-by-reference Übergabesemantik nachzubilden.
Ja. C ist *call-by-value*, da Funktionsparameter als Kopie und nicht als Referenz weitergegeben werden.
Zeiger sind jedoch Referenzen auf Speicherstellen im Adressraum. Erhält eine Funktion also einen Zeiger als Wert,
so kann sie mit dem Ziel des Zeigers so verfahren, als hätte sie das Ziel selbst per Referenz erhalten.
- Zeiger vom Typ `void*` existieren in C nicht, da solche Zeiger auf "Nichts" keinen sinnvollen Einsatzzweck hätten.
Nein, *void pointer* zeigen auf Daten mit unspezifiziertem Typ. Ein `void*` muss jedoch zuerst zu einem Zeiger auf einen nicht-`void`-Wert gecastet werden,
bevor er dereferenziert werden kann, oder Pointerarithmetik durgeführt werden kann.
- Ein Zeiger kann zur Manipulation von Daten in schreibgeschützten Speicherbereichen verwendet werden.
Nein. Wird versucht, schreibend auf einen schreibgeschützten Bereich zuzugreifen, so verhindert dies die MMU,
unabhängig davon, mit welchem Programmierkonzept der Zugriff versucht wurde.
- Die Übergabesemantik für Zeiger als Funktionsparameter ist call-by-reference.
Nein, ein Zeiger wird immer noch als Wert an eine Funktion übergeben, d. h. es wird eine Kopie des Zeigers in das entsprechende Register
oder auf den Stack abgelegt. Das ein Zeiger eine Referenz auf eine Speicherstelle *ist*, spielt hierbei keine Rolle.
Beispiel: Würde die Aussage stimmen, so würde das folgende Programm "10" ausgeben. Es gibt jedoch "5" aus.
~~~
#include <stdio.h>
static int b = 10;
static void pointers_are_values_too(int* arg) {
arg = &b; // arg zeigt hier auf b, ptr in main bleibt aber unverändert.
}
int main(void) {
int a = 5;
int* ptr = &a; // ptr zeigt auf a.
pointers_are_values_too(ptr);
printf("%d\n", *ptr); // ptr zeigt immer noch auf a.
}
~~~
.
0 Welche Aussage zu Programmbibliotheken ist richtig? (2020-02)
- Eine statische Bibliothek, die in ein Programm eingebunden wurde, muss zum Ladezeitpunkt dieses Programms im Dateisystem vorhanden sein.
Nein, das wäre bei einer dynamischen Bibliothek der Fall.
Bei statischen Binden wird bereits zur *link time* der Objektcode der Bibliothek in das Programm kopiert.
- Beim Binden mit einer statischen Bibliothek werden in einem Programm nur Verweise auf verwendete Symbole der Bibliothek angelegt.
Nein, das wäre bei einer dynamischen Bibliothek der Fall. Bei einer statischen Bibliothek wird zunächst die gesamte Bibliothek in die ausführbare Datei eingebunden.
+ Programm-Module, die von mehreren Anwendungen gemeinsam genutzt werden, können in Form einer dynamischen Bibliothek zentral installiert werden, um Speicherplatz zu sparen.
Ja, eine dynamische Bibliothek kann von mehreren Programmen referenziert werden. Somit ist nur eine Kopie dieser auf dem System nötig. So kann Speicherplatz gespart werden.
- Eine Änderung am Code einer statischen Bibliothek (z. B. Bugfixes) erfordert kein erneutes Binden der Programme, die diese Bibliothek benutzen.
Nein, bei statischen Bibliotheken wird der Objektcode zum Bindezeitpunkt in das Zielprogramm kopiert. Soll dieser geändert werden, so muss das Programm neu gebunden werden. Es reicht nicht aus,
nur die statische Bibliothek zu ändern, da das Programm immer noch den ursprünglichen Objektcode enthält.
.
0 Welche der folgenden Aussagen über Einplanungsverfahren ist richtig? (2020-02)
- Beim Einsatz präemptiver Einplanungsverfahren kann laufenden Prozessen die CPU nicht entzogen werden.
Nein, das wäre kooperative Einplanung. Bei präemptiver Einplanung kann einem Prozess durch einen Timer-Interrupt die CPU entzogen werden (Präemption).
- Probabilistische Einplanungsverfahren müssen die exakten CPU-Stoßlängen aller im System vorhandenen Prozesse kennen.
Nein, diese betrachten nur die ungefähren Stoßlängen.
+ Bei kooperativer Einplanung kann es zur Monopolisierung der CPU kommen.
Ja, bei kooperativer Einplanung kann der laufende Prozess erst gewechselt werden, wenn das Programm kooperativ ist und die CPU abgibt.
Ein unkooperatives Programm kann dies unterlassen, und die CPU für eine unbegrenzte Zeit in Anspruch nehmen.
- Asymmetrische Einplanungsverfahren können ausschließlich auf asymmetrischen Multiprozessor-Systemen zum Einsatz kommen.
Nein, asymmetrische Planung (eine Bereitliste pro Prozessor) muss zwar auf asymmetrischen Multiprozessorsystemen (z. B. CPU und GPU) eingesetzt werden, kann jedoch auch auf symmetrischen Multiprozessorsystemen, bei welchen jeder Prozessor gleichermaßen dieselben Programme ausführen kann, eingesetzt werden. (vgl. SP1 C IX.2 S. 11 ff.)
.