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 (301)
*~
sp.q
sp.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
### Options
sp.json: sp.q
./gen.pl $< > $@
# Path to the directory with question files. The path is interpreted
# relative to this Makefile.
DATA := data
sp.q: $(QFILES)
cat $^ > $@
# 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: $(OUT)/$(NAME).json $(OUT)/$(NAME).html $(OUT)/quiz.js $(OUT)/quiz.css $(OUT)/katalog.html $(OUT)/conf.js
$(DATA):; $(GIT) worktree add $@ $(BRANCH)
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) $(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.
[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.
\ No newline at end of file
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 }
......@@ -6,8 +6,11 @@ use warnings;
use JSON;
use IPC::Open2;
use Digest::MD5 qw(md5_base64);
use FindBin;
my @markdown = qw/cmark --smart --safe/;
my @markdown = qw/pandoc --html-q-tags --ascii --mathml/;
push @markdown, "--lua-filter=$FindBin::Bin/mark.lua";
my $question;
my $single_choice;
......@@ -21,7 +24,7 @@ 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);
......@@ -31,24 +34,47 @@ sub md { # pipe through $markdown
sub add {
return unless %last_option;
if (defined $last_option{"comment"}) {
if ($last_option{"comment"}) {
if (defined $last_option{"correct"}) {
if (($last_option{"correct"} eq JSON::true) and
($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\b/)) {
warn "$ARGV:$.: Wrong answer starting with \"Ja\"\n";
}
}
$last_option{"comment"} = md($last_option{"comment"});
}
push(@options, {%last_option});
}
while (<>) {
if (/^\s*#/) {
# ignore comments
} elsif (/^.$/) {
} elsif (/^\.$/) {
add();
die "end of block without a question at line $.\n"
die "end of block without a question at line $ARGV:$.\n"
unless defined $question;
my $any = 0;
foreach my $opt (@options) {
$opt->{"option"} = md($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($data),
"question" => md($question),
"options" => [@options],
"multiple" => ($single_choice eq "0" ? \0 : \1)
......@@ -63,14 +89,23 @@ while (<>) {
undef $media;
undef %last_option;
undef @options;
} elsif (/^([01])(.*?)(?:\(([^()]*)\))?$/) {
} elsif (/^([01])(.*?)$/) {
$single_choice = $1;
$question = $2;
chomp $question;
$source = $3;
chomp $source if $source;
if ($question =~ /\(([[0-9]{4}-[0-9]{2})\)/) {
$source = $1;
$question =~ s/\Q($source)\E//;
chomp $source;
}
} elsif (/^[|](.*)/) {
if ($last_option{"option"}) {
$last_option{"option"} .= "\n$1";
} else {
$question .= "\n$1";
}
} elsif (/^@(.*)/) {
$media = $1;
chomp $media;
......@@ -78,11 +113,13 @@ while (<>) {
add();
%last_option =
(
"option" => md($2),
"option" => $2,
"correct" => $1 eq "+" ? JSON::true : $1 eq "-" ? JSON::false : JSON::null
);
} elsif (/^.+$/) { # non-empty line
$last_option{"comment"} .= $_;
} 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? (Juli 2016)
- 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? (Juli 2016)
- 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? (Juli 2016)
- 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? (Juli 2016)
- 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. (Juli 2016)
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)? (Juli 2016)
- Um bei Monoprozessorsystemen Interrupts zu sperren.
Nein, das wäre auf x86 ein Befehl wie `cli`.
+ Um auf einem Multiprozessorsystem einfache Modikationen 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? (Juli 2016)
- 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 Seitenattern 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? (Juli 2018)
- 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? (Juli 2018)
+ 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? (Juli 2018)
- 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? (Juli 2018)
+ 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? (Juli 2019)
+ 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? (Juli 2019)
+ 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? (Juli 2019)
- 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? (Juli 2019)
- 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äugkeit 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? (Juli 2022)
+ 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? (Juli 2022)
- 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.
.
# 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? (Juli 2023)
? 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? (Juli 2023)
- 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? (Juli 2023)
- 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? (Juli 2023)
- 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/2022w-SP-Klausur_www.pdf
0 Welche der folgenden Aussagen zu statischem bzw. dynamischem Binden ist richtig? (Februar 2022)
- Statisch gebundene Programmdateien sind kleiner als dynamisch gebundene, da mehrfach genutzte Funktionen in einer shared library abgelegt werden und nicht in die ausführbare Datei kopiert werden
Die Beschreibung trifft auf dynamisch gebundene Programmdateien,
weil Bibliotheksfunktionen zur Laufzeit auf `.so` Dateien laden, im
gegensatz zu statisch gebundenen Programmen, wo diese jedes mal zum
Bindezeitpunkt in die ausführbare Datei kopiert werden.
- Bei dynamischem Binden können Fehlerkorrekturen in Bibliotheken leichter übernommen werden, da nur die Bibliothek selbst neu erzeugt werden muss. Programme, die die Bibliothek verwenden, müssen nicht neu kompiliert und gebunden werden.
Ja. Im Gegensatz dazu, müsste man bei einem statischen Bibliothek
alle Programme nochmal neu, gegen die aktualisierte Bibliothek
bauen, damit diese die neuste Version benutzen.
- Beim statischen Binden werden alle Adressen zum Ladezeitpunkt aufgelöst
Nein, Adressen werden alle beim Binden aufgelöst, und sind somit
unabhängig von der Ausführung -- *statisch* -- bekannt.
- Bei dynamischem Binden müssen zum Übersetzungszeitpunkt alle Adressbezüge vollständig aufgelöst werden
Nein, Bibliotheksfunktionen welche aus *Shared Object* Dateien
geladen werden können zur Laufzeit, müssen nicht beim Übersetzen
bereits aufgelöst geworden sein.
.
0 Welche Antwort trifft für die Eigenschaften eines UNIX/Linux-Dateideskriptor zu? (Februar 2022)
- Ein Dateideskriptor ist eine Integerzahl, die über gemeinsamen Speicher an einen anderen Prozess übergeben werden kann, und von letzterem zum Zugriff auf eine geöffnete Datei verwendet werden kann.
Nein, es ist zwar eine Ganzzahl, aber diese kann nicht beliebig hin und her gereicht werden, zumindest ohne dass das Betriebsystem entsprechend informiert wird (siehe `sendmsg(2)`).
- Dateideskriptoren sind Zeiger auf Betriebssystemstrukturen, die von den Systemaufrufen ausgewertet werden, um auf Dateien zuzugreifen.
Nein, es handelt sich nicht um einen Zeiger im gewöhnlichen Sinne (eine Adresse auf eine Speicherstelle), auch wenn es üblicherweise dazu benutzt wird um eine Prozess-Lokale Datei Tabelle zu Adressieren. Es stimmt aber, dass es dazu benutzt wird, einem Systemaufruf eine Datei anzudeuten, auf dem dieses arbeiten soll.
+ Ein Dateideskriptor ist eine prozesslokale Integerzahl, die der Prozess zum Zugriff auf eine Datei, ein Gerät, einen Socket oder eine Pipe benutzen kann.
Ja, ein Dateideskriptor muss nicht nur auf "gewöhnlichen" Dateien arbeiten, sondern kann verschieden "Datei-Ähnlichen" Objekten (d.h. Datenströmen) abstrahieren. Wichtig ist auch der Punkt "prozesslokal", weil die Zahl nur eine Bedeutung hat, wenn das Betriebsystem dem Prozess diese Zahl zuvor explizit vergeben hat.
- Beim Öffnen ein und derselben Datei erhält ein Prozess jeweils die gleiche Integerzahl als Dateideskriptor zum Zugriff zurück.
Nein, es ist möglich eine Datei Mehrfach zu öffnen, und dabei mehrere Dateideskriptoren zu bekommen. Versuche
~~~
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int fd1 = open("/etc/passwd", O_RDONLY);
int fd2 = open("/etc/passwd", O_RDONLY);
printf("fd1: %d, fd2: %d\n", fd1, fd2);
}
~~~
.
1 Man unterschiedet zwei Kategorien von Ausnahmesituationen bei einer Programmausführung: Traps und Interrupts. Welche der folgenden Aussagen sind zutreffend? (Februar 2022)
+ Ein Programm darf im Rahmen einer Trapbehandlung abgebrochen werden.
Ja, Beispielsweise bei einem ungültigem Speicherzugriff.
+ Ein durch einen Interrupt unterbrochenes Programm darf je nach der Interruptursache entweder abgebrochen oder fortgesetzt werden.
Ja, so ist ein _Timer Interrupt_ nicht fatal, und aktiviert nur den
Scheduler, während ein _Tastatur Interrupt_ so konfiguriert werden
kann, dass ein Prozess unterbrochen wird (denke an `Ctrl-C`).
- Bei einem Trap wird der gerade in Bearbeitung bendliche Maschinenbefehl immer noch vollständig zu Ende bearbeitet, bevor mit der Trapbehandlung begonnen wird.
Nein, bspw. wenn man versucht auf nicht lesbaren Speicher versucht
zuzugreifen, muss es abgebrochen werden, bevor auf den Speicher
zuggegriffen wurde.
+ Die CPU sichert bei einem Interrupt einen Teil des Prozessorzustands.
Ja, genau wie bei einer Signal-Behandlung unterbricht ein Interrupt
das "Hauptprogramm" vom Betriebsystem. Der Prozessorzustand muss
dafür vermerkt worden sein, damit es danach wieder restauriert
werden kann.
+ Die Ausführung einer Ganzzahl-Rechenoperation (z. B. Addition, Division) kann zu einem Trap führen.
Ja, eine Rechnerarchitektur kann beim Teilen durch 0 ein Trap auslösen.
- Da Traps immer synchron auftreten, kann es im Rahmen ihrer Behandlung nicht zu Wettlaufsituationen mit dem unterbrochenen Programm kommen.
Nein, weil Traps eben immer syncrhron auftreten, gibt es keine
Nebenläufigkeit welche Probleme bereitet.
? Wenn ein Interrupt ein schwerwiegendes Ereignis signalisiert, wird das unterbrochene Programm im Rahmen der Interruptbearbeitung immer abgebrochen.
- Ein Systemaufruf im Anwendungsprogramm ist der Kategorie Interrupt zuzuordnen.
Nein, weil diese deterministisch immer dann auftreten, wenn ein Programm ein Systemaufruf absetzen will.
.
0 In welcher der folgenden Situationen wird ein Prozess vom Zustand laufend in den Zustand bereit übeführt? (Februar 2022)
- Der Prozess ruft die Bibliotheksfunktion `exit(3)` auf.
Nein, weil danach der Prozess nicht mehr "bereit" ist (d.h. wieder eingelagert werden kann).
+ Der Scheduler bewirkt, dass der Prozess durch einen anderen Prozess verdrängt wird.
Ja, weil die einzige Ressource welche dem Prozess fehlt ist die CPU, welche der Scheduler später wieder "vergeben" kann, und damit der Prozess auch die ganze Zeit bereit ist wieder weiter zu laufen.
- Der Prozess greift lesend auf eine Datei zu und der entsprechende Datenblock ist noch nicht im Hauptspeicher vorhanden.
Nein, weil dann idr. der Prozess beendet wird mit einem Sigal wie `SIGSEVG`, und nicht mehr laufen darf.
- Der Prozess ruft eine P-Operation auf einen Semaphor auf, welcher den Wert 0 hat.
Nein, weil dann der Prozess blokiert wird, und nicht mehr laufen kann, bis an einer anderen Stelle eine entsprechende V-Operation ausgeführt wird.
.
0 Sie kennen den Translation-Lookaside-Buffer (TLB). Welche Aussage ist richtig? (Februar 2023)
+ Verändert sich die Speicherabbildung von logischen auf physikalische Adressen aufgrund einer Adressraumumschaltung, so werden auch die Daten im TLB ungültig.
Ja, weil die zwischengespeicherten Seitenaddressen nicht mehr mit dem neuem logischem Addressraum zusammenhängen würden.
- Der TLB verkürzt die Zugriffszeit auf den physikalischen Speicher da ein Teil des möglichen Speichers in einem schnellen Pufferspeicher vorgehalten wird.
Nein, im TLB werden nur die Addressen der Seiten gespeichert, um das Nachschlagen in der MMU zu vermeiden, und nicht den Speicher selbst.
- Der TLB puffert Daten bei der Ein-/Ausgabebehandlung und beschleunigt diese damit.
Nein, der TLB hat nichts mit den Daten per se zu tun, sondern beschleundigt nur den Zugriff auf häufig benutzte Seiten.
- Wird eine Speicherabbildung im TLB nicht gefunden, wird der auf den Speicher zugreifende Prozess mit einer Schutzraumverletzung (Segmentation Fault) abgebrochen.
Nein, ist kein Eintrag im TLB zu finden, wird die Addresse von der MMU aufgelöst.
.
0 Welche Aussage zu Prozessen und Threads ist richtig? (Februar 2023)
- Mittels `fork()` erzeugte Kindprozesse können in einem Multiprozessor-System nur auf dem Prozessor ausgeführt werden, auf dem auch der Elternprozess ausgeführt wird.
Nein, es ist dem Scheduler ganz überlassen zu entscheiden, auf welchen Prozessoren ein Prozess laufen soll. Ein Prozess muss auch nicht ganz auf einem Kern oder nur auf einem Kern laufen
+ Der Aufruf von `fork()` gibt im Elternprozess die Prozess-ID des Kindprozesses zurück, im Kindprozess hingegen den Wert 0.
Ja, das steht auch so in der Man Page. Die Annahme ist hier, dass kein Fehler aufgetreten ist.
- Threads, die mittels `pthread_create()` erzeugt wurden, besitzen jeweils einen eigenen Adressraum.
Nein, Kind-Threads (im Kontext von der Pthread Bibliothek.) teilen den Addressraum mit dem Eltern-Thread. Prozesse haben eigene Addressräume.
- Die Veränderung von Variablen und Datenstrukturen in einem mittels `fork()` erzeugten Kindprozess beeinflusst auch die Datenstrukturen im Elternprozess.
Nein, allgemein nicht, außer der Benutzer richtet dieses spezifisch ein (Siehe `shm_open(3)`), was aber ohne weiteres nicht der Fall ist.
.
0 Was ist ein Stack-Frame? (Februar 2023)
- Der Speicherbereich, in dem der Programmcode einer Funktion abgelegt ist.
Nein, ein Stack-Frame liegt im Stack Segment, und der Programmcode liegt im Text-Segment.
- Ein Fehler, der bei unberechtigten Zugriffen auf den Stack-Speicher entsteht.
Nein, es gibt keinen gesonderten Namen für Fehlerhaften Zugriff auf den Stack-Namen.
+ Ein Bereich des Speichers, in dem u.a. lokale automatic-Variablen einer Funktion abgelegt sind.
Ja, ein Stack-Frame wird beim betreten einer Funktion angelegt für alle Metadaten und Daten welche zur ausführung benötigt werden und aufgeräumt sobald die Funktion verlassen wird.
- Ein spezieller Registersatz des Prozessors zur Bearbeitung von Funktionen.
Nein, es ist kein Registersatz.
.
0 Welche der folgenden Informationen wird typischerweise in dem Seitendeskriptor einer Seite eines virtuellen Adressraums gehalten?
- Die Zugriffsrechte auf die jeweilige Seite (z. B. lesen, schreiben, ausführen).
Ja, so setzt das Betriebsystem bspw. um, dass ein Text Segment ausführbar aber nicht schreibbar ist.
- Die Identifikation des Prozesses, dem die Seite zugeordnet ist.
Nein, das Seiten existieren unabhängig von Prozessen und müssten per se nicht dafür benutzt werden, um logische Speicherräume auf Betriebsystemen mit unabhängigen Prozessen umzusetzen.
- Die Zuordnung zu einem Segment (Text, Daten, ...).
Nein, dieses wird muss man sich nicht in dem Seitendeskriptor merken, weil es nur wichtig ist, dass die Seite sich gemäß dem Verständniss von diesen Segmenten verhällt (lesbar, ausfübar, ...).
- Die Position der Seite im virtuellen Adressraum.
Nein, diese Zuordnung findet nicht im Seitendeskriptor statt, sondern im Betriebsystem.
.