Compare commits

..

38 Commits

Author SHA1 Message Date
8b3246ebd9 complement 2025-12-04 11:11:48 +09:00
575d48efa6 complement 9.md 10.md 11.md 2025-11-30 21:19:04 +09:00
1518950242 some complement in 11.27 2025-11-27 14:10:51 +09:00
0a652a3214 update 8.md 9.md 2025-11-26 03:47:12 +09:00
17257c2d9f add L10.pdf L11.pdf L12.pdf 2025-11-26 03:39:07 +09:00
e1da8a7292 fic minor lab03 2025-11-25 17:27:46 +09:00
1c50fbd315 complement lab03 2025-11-24 03:03:35 +09:00
3e87f2c37d add P3.pdf 2025-11-22 19:56:06 +09:00
4f21b2e69f Merge commit 'e2a5e6750a362e0fd8c89f3d1d40a623ba6d1fd6' 2025-11-21 16:23:27 +09:00
Woong Sul
e2a5e6750a Lab03 assigned 2025-11-12 02:58:26 +00:00
646ecc0931 fix pdf ordering and add L8.pdf L9.pdf and a missing one 2025-11-06 14:36:06 +09:00
99381bb361 build notes 2025-10-22 03:19:36 +09:00
d5c9dcf735 update notes style 2025-10-22 03:19:07 +09:00
bf60eeff7e construct notes build system 2025-10-22 03:18:51 +09:00
6f41ae9730 update 3.md and 4.md 2025-10-22 03:04:38 +09:00
a86855a11c add L6.pdf 2025-10-22 01:50:32 +09:00
ab6ccd8cf6 lab02/defuse the bomb 2025-10-21 21:36:20 +09:00
375aac730e lab02/defuse the bomb 2025-10-21 21:36:20 +09:00
1122a9c541 initializing Makefile 2025-10-20 22:19:26 +09:00
5b96f9961d initializing Makefile 2025-10-20 22:19:26 +09:00
42efa4341b fix 3.md 2025-10-20 21:58:00 +09:00
99628ab532 fix 3.md and update Makefile 2025-10-20 21:54:07 +09:00
b04b68a339 delete illegal files 2025-10-15 20:22:23 +09:00
05332b0146 Merge commit 'c50fc23a19060f984c8219b98e196a662df888ee' 2025-10-15 20:20:33 +09:00
Woong Sul
2dfb064afb Lab02 assigned 2025-10-15 20:19:19 +09:00
Woong Sul
c50fc23a19 Lab02 assigned 2025-10-14 03:48:15 +00:00
4c2c0363b3 complement many 2025-10-11 08:39:17 +09:00
3d651c4a8a add pdf L4 L5 2025-10-11 08:38:50 +09:00
1c72fe6940 some minor changes for datalab01/bits.c 2025-10-01 13:31:09 +09:00
85db177ba3 some minor changes for datalab01/bits.c 2025-10-01 13:31:09 +09:00
8813f46c6c update datalab01/bits.c 2025-09-29 15:45:56 +09:00
b189afd37a update datalab01/bits.c 2025-09-29 15:45:56 +09:00
57a619ac7b complement in 9.25 2025-09-26 01:18:55 +09:00
29734eb1e2 some change in lab1 2025-09-26 01:18:42 +09:00
791672d18d some change in lab1 2025-09-26 01:18:42 +09:00
901757b211 Add 'labs/' from commit '58cd0413b229aea4bad859401adb7f4cb8f4474d'
git-subtree-dir: labs
git-subtree-mainline: 4d409d3106
git-subtree-split: 58cd0413b2
2025-09-18 15:31:05 +09:00
Woong Sul
58cd0413b2 datalab assigned 2025-09-16 12:10:33 +00:00
Administrator
42cf54ff74 Initial commit 2025-09-15 05:25:49 +00:00
78 changed files with 12871 additions and 2 deletions

15
.crossnote/config.js Normal file
View File

@@ -0,0 +1,15 @@
({
katexConfig: {
"macros": {}
},
mathjaxConfig: {
"tex": {},
"options": {},
"loader": {}
},
mermaidConfig: {
"startOnLoad": false
},
})

6
.crossnote/head.html Normal file
View File

@@ -0,0 +1,6 @@
<!-- The content below will be included at the end of the <head> element. -->
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
// your code here
});
</script>

12
.crossnote/parser.js Normal file
View File

@@ -0,0 +1,12 @@
({
// Please visit the URL below for more information:
// https://shd101wyy.github.io/markdown-preview-enhanced/#/extend-parser
onWillParseMarkdown: async function(markdown) {
return markdown;
},
onDidParseMarkdown: async function(html) {
return html;
},
})

25
.crossnote/style.less Normal file
View File

@@ -0,0 +1,25 @@
/* Please visit the URL below for more information: */
/* https://shd101wyy.github.io/markdown-preview-enhanced/#/customize-css */
.markdown-preview.markdown-preview {
// modify your style here
// eg: background-color: blue;
font-size: 10pt;
.mermaid {
background-color: white;
}
p {
margin-bottom: 0.0pt;
}
h1, h2, h3, h4, h5, h6 {
margin-bottom: 1pt;
margin-top: 2pt;
}
div[data-cmd="sh"] {
font-size: 8pt;
}
font-family: NanumGothic;
}

2
.gitattributes vendored
View File

@@ -1,3 +1,5 @@
* text=auto eol=lf
[attr]lfs-file filter=lfs diff=lfs merge=lfs -text
"*.pdf" lfs-file

6
.gitignore vendored
View File

@@ -1,2 +1,8 @@
.vscode
.venv
*.out
notes/*.o
notes/*.s
node_modules/

9
Makefile Normal file
View File

@@ -0,0 +1,9 @@
all: clean build
clean:
rm -f ./notes/*.out
rm -f ./notes/*.o
rm -f ./notes/*.s
build:
node build.js

BIN
assets/3_1stackframe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

43
build.js Normal file
View File

@@ -0,0 +1,43 @@
const { Notebook } = require("crossnote")
const path = require('path')
const fs = require('fs')
async function main() {
const notebook = await Notebook.init(
{
notebookPath: path.resolve(''),
config: {
previewTheme: 'github-light.css',
mathRenderingOption: 'KaTeX',
codeBlockTheme: 'github.css',
printBackground: true,
enableScriptExecution: true,
chromePath: '/usr/bin/google-chrome-stable',
},
}
);
const files = fs.readdirSync(path.resolve('notes')).filter(file => {
return path.extname(file).toLowerCase() == '.md';
});
files.forEach(async (file) => {
const fileBase = "notes/" +path.basename(file);
const fileName = path.basename(file, ".md")
console.log("found " + fileBase);
const engine = notebook.getNoteMarkdownEngine(fileBase);
await engine.chromeExport({ runAllCodeChunks: true });
const old = path.resolve('notes', fileName + ".pdf");
const dest = path.resolve('out', 'notes', fileName + ".pdf");
fs.rename(old, dest, (err) => {
if (err) throw err;
console.log(fileName + ".pdf" + " moved to out completed");
});;
});
}
main();

26
labs/01_datalab/Makefile Normal file
View File

@@ -0,0 +1,26 @@
#
# Makefile that builds btest and other helper programs for CSE4009 data lab
#
CC = gcc
CFLAGS = -O -Wall -m32
LIBS = -lm
all: btest fshow ishow
btest: btest.c bits.c decl.c tests.c btest.h bits.h
$(CC) $(CFLAGS) $(LIBS) -o btest bits.c btest.c decl.c tests.c
fshow: fshow.c
$(CC) $(CFLAGS) -o fshow fshow.c
ishow: ishow.c
$(CC) $(CFLAGS) -o ishow ishow.c
# Forces a recompile. Used by the driver program.
btestexplicit:
$(CC) $(CFLAGS) $(LIBS) -o btest bits.c btest.c decl.c tests.c
clean:
rm -f *.o btest fshow ishow *~

139
labs/01_datalab/README Normal file
View File

@@ -0,0 +1,139 @@
**********************************
* CS4009 Assignemnt 1 - Data Lab *
**********************************
Your goal is to modify your copy of bits.c so that it passes all the
tests in btest without violating any of the coding guidelines.
*********
0. Files:
*********
Makefile - Makes btest, fshow, and ishow
README - This file
bits.c - The file you will be modifying and handing in
bits.h - Header file
btest.c - The main btest program
btest.h - Used to build btest
decl.c - Used to build btest
tests.c - Used to build btest
tests-header.c- Used to build btest
dlc* - Rule checking compiler binary (data lab compiler)
driver.pl* - Driver program that uses btest and dlc to autograde bits.c
Driverhdrs.pm - Header file for optional "Beat the Prof" contest
fshow.c - Utility for examining floating-point representations
ishow.c - Utility for examining integer representations
***********************************************************
1. Modifying bits.c and checking it for compliance with dlc
***********************************************************
IMPORTANT: Carefully read the instructions in the bits.c file before
you start. These give the coding rules that you will need to follow if
you want full credit.
Use the dlc compiler (./dlc) to automatically check your version of
bits.c for compliance with the coding guidelines:
unix> ./dlc bits.c
dlc returns silently if there are no problems with your code.
Otherwise it prints messages that flag any problems. Running dlc with
the -e switch:
unix> ./dlc -e bits.c
causes dlc to print counts of the number of operators used by each function.
Once you have a legal solution, you can test it for correctness using
the ./btest program.
*********************
2. Testing with btest
*********************
The Makefile in this directory compiles your version of bits.c with
additional code to create a program (or test harness) named btest.
To compile and run the btest program, type:
unix> make btest
unix> ./btest [optional cmd line args]
You will need to recompile btest each time you change your bits.c
program. When moving from one platform to another, you will want to
get rid of the old version of btest and generate a new one. Use the
commands:
unix> make clean
unix> make btest
Btest tests your code for correctness by running millions of test
cases on each function. It tests wide swaths around well known corner
cases such as Tmin and zero for integer puzzles, and zero, inf, and
the boundary between denormalized and normalized numbers for floating
point puzzles. When btest detects an error in one of your functions,
it prints out the test that failed, the incorrect result, and the
expected result, and then terminates the testing for that function.
Here are the command line options for btest:
unix> ./btest -h
Usage: ./btest [-hg] [-r <n>] [-f <name> [-1|-2|-3 <val>]*] [-T <time limit>]
-1 <val> Specify first function argument
-2 <val> Specify second function argument
-3 <val> Specify third function argument
-f <name> Test only the named function
-g Format output for autograding with no error messages
-h Print this message
-r <n> Give uniform weight of n for all problems
-T <lim> Set timeout limit to lim
Examples:
Test all functions for correctness and print out error messages:
unix> ./btest
Test all functions in a compact form with no error messages:
unix> ./btest -g
Test function foo for correctness:
unix> ./btest -f foo
Test function foo for correctness with specific arguments:
unix> ./btest -f foo -1 27 -2 0xf
Btest does not check your code for compliance with the coding
guidelines. Use dlc to do that.
*******************
3. Helper Programs
*******************
We have included the ishow and fshow programs to help you decipher
integer and floating point representations respectively. Each takes a
single decimal or hex number as an argument. To build them type:
unix> make
Example usages:
unix> ./ishow 0x27
Hex = 0x00000027, Signed = 39, Unsigned = 39
unix> ./ishow 27
Hex = 0x0000001b, Signed = 27, Unsigned = 27
unix> ./fshow 0x15213243
Floating point value 3.255334057e-26
Bit Representation 0x15213243, sign = 0, exponent = 0x2a, fraction = 0x213243
Normalized. +1.2593463659 X 2^(-85)
linux> ./fshow 15213243
Floating point value 2.131829405e-38
Bit Representation 0x00e822bb, sign = 0, exponent = 0x01, fraction = 0x6822bb
Normalized. +1.8135598898 X 2^(-126)

310
labs/01_datalab/bits.c Normal file
View File

@@ -0,0 +1,310 @@
/*
* CSE4009 Assignment 1 - Data Lab
*
* name: Hajin Ju, id: 2024062806
*
* bits.c - Source file with your solutions to the Lab.
* This is the file you will hand in to your instructor.
*
* WARNING: Do not include the <stdio.h> header; it confuses the dlc
* compiler. You can still use printf for debugging without including
* <stdio.h>, although you might get a compiler warning. In general,
* it's not good practice to ignore compiler warnings, but in this
* case it's OK.
*/
#if 0
/*
* Instructions to Students:
*
* STEP 1: Read the following instructions carefully.
*/
You will provide your solution to the Data Lab by
editing the collection of functions in this source file.
INTEGER CODING RULES:
Replace the "return" statement in each function with one
or more lines of C code that implements the function. Your code
must conform to the following style:
int Funct(arg1, arg2, ...) {
/* brief description of how your implementation works */
int var1 = Expr1;
...
int varM = ExprM;
varJ = ExprJ;
...
varN = ExprN;
return ExprR;
}
Each "Expr" is an expression using ONLY the following:
1. Integer constants 0 through 255 (0xFF), inclusive. You are
not allowed to use big constants such as 0xffffffff.
2. Function arguments and local variables (no global variables).
3. Unary integer operations ! ~
4. Binary integer operations & ^ | + << >>
Some of the problems restrict the set of allowed operators even further.
Each "Expr" may consist of multiple operators. You are not restricted to
one operator per line.
You are expressly forbidden to:
1. Use any control constructs such as if, do, while, for, switch, etc.
2. Define or use any macros.
3. Define any additional functions in this file.
4. Call any functions.
5. Use any other operations, such as &&, ||, -, or ?:
6. Use any form of casting.
7. Use any data type other than int. This implies that you
cannot use arrays, structs, or unions.
You may assume that your machine:
1. Uses 2s complement, 32-bit representations of integers.
2. Performs right shifts arithmetically.
3. Has unpredictable behavior when shifting if the shift amount
is less than 0 or greater than 31.
EXAMPLES OF ACCEPTABLE CODING STYLE:
/*
* pow2plus1 - returns 2^x + 1, where 0 <= x <= 31
*/
int pow2plus1(int x) {
/* exploit ability of shifts to compute powers of 2 */
return (1 << x) + 1;
}
/*
* pow2plus4 - returns 2^x + 4, where 0 <= x <= 31
*/
int pow2plus4(int x) {
/* exploit ability of shifts to compute powers of 2 */
int result = (1 << x);
result += 4;
return result;
}
FLOATING POINT CODING RULES
For the problems that require you to implement floating-point operations,
the coding rules are less strict. You are allowed to use looping and
conditional control. You are allowed to use both ints and unsigneds.
You can use arbitrary integer and unsigned constants. You can use any arithmetic,
logical, or comparison operations on int or unsigned data.
You are expressly forbidden to:
1. Define or use any macros.
2. Define any additional functions in this file.
3. Call any functions.
4. Use any form of casting.
5. Use any data type other than int or unsigned. This means that you
cannot use arrays, structs, or unions.
6. Use any floating point data types, operations, or constants.
NOTES:
1. Use the dlc (data lab checker) compiler (described in the handout) to
check the legality of your solutions.
2. Each function has a maximum number of operations (integer, logical,
or comparison) that you are allowed to use for your implementation
of the function. The max operator count is checked by dlc.
Note that assignment ('=') is not counted; you may use as many of
these as you want without penalty.
3. Use the btest test harness to check your functions for correctness.
4. Use the BDD checker to formally verify your functions
5. The maximum number of ops for each function is given in the
header comment for each function. If there are any inconsistencies
between the maximum ops in the writeup and in this file, consider
this file the authoritative source.
/*
* STEP 2: Modify the following functions according the coding rules.
*
* IMPORTANT. TO AVOID GRADING SURPRISES:
* 1. Use the dlc compiler to check that your solutions conform
* to the coding rules.
* 2. Use the BDD checker to formally verify that your solutions produce
* the correct answers.
*/
#endif
/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This header is separate from features.h so that the compiler can
include it implicitly at the start of every compilation. It must
not itself include <features.h> or any other header that includes
<features.h> because the implicit include comes before any feature
test macros that may be defined in a source file before it first
explicitly includes a system header. GCC knows the name of this
header in order to preinclude it. */
/* glibc's intent is to support the IEC 559 math functionality, real
and complex. If the GCC (4.9 and later) predefined macros
specifying compiler intent are available, use them to determine
whether the overall intent is to support these features; otherwise,
presume an older compiler has intent to support these features and
define these macros by default. */
/* wchar_t uses Unicode 10.0.0. Version 10.0 of the Unicode Standard is
synchronized with ISO/IEC 10646:2017, fifth edition, plus
the following additions from Amendment 1 to the fifth edition:
- 56 emoji characters
- 285 hentaigana
- 3 additional Zanabazar Square characters */
//1
/*
* bitAnd - x&y using only ~ and |
* Example: bitAnd(6, 5) = 4
* Legal ops: ~ |
* Max ops: 8
* Rating: 1
*/
int bitAnd(int x, int y) {
return ~(~x | ~y);
}
/*
* bitXor - x^y using only ~ and &
* Example: bitXor(4, 5) = 1
* Legal ops: ~ &
* Max ops: 14
* Rating: 1
*/
int bitXor(int x, int y) {
return ~((~(x & ~y)) & (~(~x & y)));
}
/*
* isTmin - returns 1 if x is the minimum, two's complement number,
* and 0 otherwise
* Legal ops: ! ~ & ^ | +
* Max ops: 10
* Rating: 1
*/
volatile int isTmin(int x) {
return !!x & (!(((~(x) + 1) ^ x)));
}
//2
/*
* isEqual - return 1 if x == y, and 0 otherwise
* Examples: isEqual(5,5) = 1, isEqual(4,5) = 0
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int isEqual(int x, int y) {
return !(x ^ y);
}
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x + 1;
}
/*
* getByte - Extract byte n from word x
* Bytes numbered from 0 (least significant) to 3 (most significant)
* Examples: getByte(0x12345678,1) = 0x56
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 6
* Rating: 2
*/
int getByte(int x, int n) {
return (x >> (n << 3)) & 0xFF;
}
//3
/*
* isLess - if x < y then return 1, else return 0
* Example: isLess(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLess(int x, int y) {
return ((x >> 31) & 1 & ~((y >> 31) & 1)) | (~(((x >> 31) & 1) ^ ((y >> 31) & 1)) & (((x + (~y + 1)) >> 31) & 1));
}
/*
* conditional - same as x ? y : z
* Example: conditional(2,4,5) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 16
* Rating: 3
*/
int conditional(int x, int y, int z) {
return (((x | (~x + 1)) >> 31) & y) | (~((x | (~x + 1)) >> 31) & z);
}
//4
/*
* floatScale2 - Return bit-level equivalent of expression 2*f for
* floating point argument f.
* Both the argument and result are passed as unsigned int's, but
* they are to be interpreted as the bit-level representation of
* single-precision floating point values.
* When argument is NaN, return argument
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatScale2(unsigned uf) {
return (((uf >> 23) & 0xFF) == 0xFF) ? uf : ((((uf >> 23) & 0xFF) == 0) ? ((uf << 1) | (uf & (1 << 31))) : (uf + (1 << 23)));
}
/*
* floatFloat2Int - Return bit-level equivalent of expression (int) f
* for floating point argument f.
* Argument is passed as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point value.
* Anything out of range (including NaN and infinity) should return
* 0x80000000u.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
int floatFloat2Int(unsigned uf) {
unsigned exp, sign, frac, mantissa;
int value, shift;
exp = (uf >> 23) & ((1 << 8) - 1);
sign = (uf >> 31) & 1;
frac = uf & ((1 << 23) - 1);
if (exp >= 158) {
return 1 << 31;
}
if (exp < 127) {
return 0;
}
shift = exp - 150;
mantissa = ((1 << 23) | frac);
if (shift > 0) {
value = mantissa << shift;
} else {
value = mantissa >> -shift;
}
if (sign) {
return -value;
} else {
return value;
}
}

59
labs/01_datalab/bits.h Normal file
View File

@@ -0,0 +1,59 @@
/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This header is separate from features.h so that the compiler can
include it implicitly at the start of every compilation. It must
not itself include <features.h> or any other header that includes
<features.h> because the implicit include comes before any feature
test macros that may be defined in a source file before it first
explicitly includes a system header. GCC knows the name of this
header in order to preinclude it. */
/* glibc's intent is to support the IEC 559 math functionality, real
and complex. If the GCC (4.9 and later) predefined macros
specifying compiler intent are available, use them to determine
whether the overall intent is to support these features; otherwise,
presume an older compiler has intent to support these features and
define these macros by default. */
/* wchar_t uses Unicode 10.0.0. Version 10.0 of the Unicode Standard is
synchronized with ISO/IEC 10646:2017, fifth edition, plus
the following additions from Amendment 1 to the fifth edition:
- 56 emoji characters
- 285 hentaigana
- 3 additional Zanabazar Square characters */
//1
int bitAnd(int, int);
int test_bitAnd(int, int);
int bitXor(int, int);
int test_bitXor(int, int);
int isTmin(int);
int test_isTmin(int);
//2
int isEqual(int, int);
int test_isEqual(int, int);
int negate(int);
int test_negate(int);
int getByte(int, int);
int test_getByte(int, int);
//3
int isLess(int, int);
int test_isLess(int, int);
int conditional(int, int, int);
int test_conditional(int, int, int);
//4
unsigned floatScale2(unsigned);
unsigned test_floatScale2(unsigned);
int floatFloat2Int(unsigned);
int test_floatFloat2Int(unsigned);

583
labs/01_datalab/btest.c Normal file
View File

@@ -0,0 +1,583 @@
/*
* CSE4009 Assignment 1 - Data Lab
*
* btest.c - A test harness that checks a student's solution in bits.c
* for correctness.
*
* Copyright (c) 2001-2011, R. Bryant and D. O'Hallaron, All rights
* reserved. May not be used, modified, or copied without permission.
*
* This is an improved version of btest that tests large windows
* around zero and tmin and tmax for integer puzzles, and zero, norm,
* and denorm boundaries for floating point puzzles.
*
* Note: not 64-bit safe. Always compile with gcc -m32 option.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <signal.h>
#include <setjmp.h>
#include <math.h>
#include "btest.h"
/* Not declared in some stdlib.h files, so define here */
float strtof(const char *nptr, char **endptr);
/*************************
* Configuration Constants
*************************/
/* Handle infinite loops by setting upper limit on execution time, in
seconds */
#define TIMEOUT_LIMIT 300
/* For functions with a single argument, generate TEST_RANGE values
above and below the min and max test values, and above and below
zero. Functions with two or three args will use square and cube
roots of this value, respectively, to avoid combinatorial
explosion */
#define TEST_RANGE 500000
/* This defines the maximum size of any test value array. The
gen_vals() routine creates k test values for each value of
TEST_RANGE, thus MAX_TEST_VALS must be at least k*TEST_RANGE */
#define MAX_TEST_VALS 13*TEST_RANGE
/**********************************
* Globals defined in other modules
**********************************/
/* This characterizes the set of puzzles to test.
Defined in decl.c and generated from templates in ./puzzles dir */
extern test_rec test_set[];
/************************************************
* Write-once globals defined by command line args
************************************************/
/* Emit results in a format for autograding, without showing
and counter-examples */
static int grade = 0;
/* Time out after this number of seconds */
static int timeout_limit = TIMEOUT_LIMIT; /* -T */
/* If non-NULL, test only one function (-f) */
static char* test_fname = NULL;
/* Special case when only use fixed argument(s) (-1, -2, or -3) */
static int has_arg[3] = {0,0,0};
static unsigned argval[3] = {0,0,0};
/* Use fixed weight for rating, and if so, what should it be? (-r) */
static int global_rating = 0;
/******************
* Helper functions
******************/
/*
* Signal - installs a signal handler
*/
typedef void handler_t(int);
handler_t *Signal(int signum, handler_t *handler)
{
struct sigaction action, old_action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask); /* block sigs of type being handled */
action.sa_flags = SA_RESTART; /* restart syscalls if possible */
if (sigaction(signum, &action, &old_action) < 0)
perror("Signal error");
return (old_action.sa_handler);
}
/*
* timeout_handler - SIGALARM hander
*/
sigjmp_buf envbuf;
void timeout_handler(int sig) {
siglongjmp(envbuf, 1);
}
/*
* random_val - Return random integer value between min and max
*/
static int random_val(int min, int max)
{
double weight = rand()/(double) RAND_MAX;
int result = min * (1-weight) + max * weight;
return result;
}
/*
* gen_vals - Generate the integer values we'll use to test a function
*/
static int gen_vals(int test_vals[], int min, int max, int test_range, int arg)
{
int i;
int test_count = 0;
/* Special case: If the user has specified a specific function
argument using the -1, -2, or -3 flags, then simply use this
argument and return */
if (has_arg[arg]) {
test_vals[0] = argval[arg];
return 1;
}
/*
* Special case: Generate test vals for floating point functions
* where the input argument is an unsigned bit-level
* representation of a float. For this case we want to test the
* regions around zero, the smallest normalized and largest
* denormalized numbers, one, and the largest normalized number,
* as well as inf and nan.
*/
if ((min == 1 && max == 1)) {
unsigned smallest_norm = 0x00800000;
unsigned one = 0x3f800000;
unsigned largest_norm = 0x7f000000;
unsigned inf = 0x7f800000;
unsigned nan = 0x7fc00000;
unsigned sign = 0x80000000;
/* Test range should be at most 1/2 the range of one exponent
value */
if (test_range > (1 << 23)) {
test_range = 1 << 23;
}
/* Functions where the input argument is an unsigned bit-level
representation of a float. The number of tests generated
inside this loop body is the value k referenced in the
comment for the global variable MAX_TEST_VALS. */
for (i = 0; i < test_range; i++) {
/* Denorms around zero */
test_vals[test_count++] = i;
test_vals[test_count++] = sign | i;
/* Region around norm to denorm transition */
test_vals[test_count++] = smallest_norm + i;
test_vals[test_count++] = smallest_norm - i;
test_vals[test_count++] = sign | (smallest_norm + i);
test_vals[test_count++] = sign | (smallest_norm - i);
/* Region around one */
test_vals[test_count++] = one + i;
test_vals[test_count++] = one - i;
test_vals[test_count++] = sign | (one + i);
test_vals[test_count++] = sign | (one - i);
/* Region below largest norm */
test_vals[test_count++] = largest_norm - i;
test_vals[test_count++] = sign | (largest_norm - i);
}
/* special vals */
test_vals[test_count++] = inf; /* inf */
test_vals[test_count++] = sign | inf; /* -inf */
test_vals[test_count++] = nan; /* nan */
test_vals[test_count++] = sign | nan; /* -nan */
return test_count;
}
/*
* Normal case: Generate test vals for integer functions
*/
/* If the range is small enough, then do exhaustively */
if (max - MAX_TEST_VALS <= min) {
for (i = min; i <= max; i++)
test_vals[test_count++] = i;
return test_count;
}
/* Otherwise, need to sample. Do so near the boundaries, around
zero, and for some random cases. */
for (i = 0; i < test_range; i++) {
/* Test around the boundaries */
test_vals[test_count++] = min + i;
test_vals[test_count++] = max - i;
/* If zero falls between min and max, then also test around zero */
if (i >= min && i <= max)
test_vals[test_count++] = i;
if (-i >= min && -i <= max)
test_vals[test_count++] = -i;
/* Random case between min and max */
test_vals[test_count++] = random_val(min, max);
}
return test_count;
}
/*
* test_0_arg - Test a function with zero arguments
*/
static int test_0_arg(funct_t f, funct_t ft, char *name)
{
int r = f();
int rt = ft();
int error = (r != rt);
if (error && !grade)
printf("ERROR: Test %s() failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, r, r, rt, rt);
return error;
}
/*
* test_1_arg - Test a function with one argument
*/
static int test_1_arg(funct_t f, funct_t ft, int arg1, char *name)
{
funct1_t f1 = (funct1_t) f;
funct1_t f1t = (funct1_t) ft;
int r, rt, error;
r = f1(arg1);
rt = f1t(arg1);
error = (r != rt);
if (error && !grade)
printf("ERROR: Test %s(%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, r, r, rt, rt);
return error;
}
/*
* test_2_arg - Test a function with two arguments
*/
static int test_2_arg(funct_t f, funct_t ft, int arg1, int arg2, char *name)
{
funct2_t f2 = (funct2_t) f;
funct2_t f2t = (funct2_t) ft;
int r = f2(arg1, arg2);
int rt = f2t(arg1, arg2);
int error = (r != rt);
if (error && !grade)
printf("ERROR: Test %s(%d[0x%x],%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, arg2, arg2, r, r, rt, rt);
return error;
}
/*
* test_3_arg - Test a function with three arguments
*/
static int test_3_arg(funct_t f, funct_t ft,
int arg1, int arg2, int arg3, char *name)
{
funct3_t f3 = (funct3_t) f;
funct3_t f3t = (funct3_t) ft;
int r = f3(arg1, arg2, arg3);
int rt = f3t(arg1, arg2, arg3);
int error = (r != rt);
if (error && !grade)
printf("ERROR: Test %s(%d[0x%x],%d[0x%x],%d[0x%x]) failed...\n...Gives %d[0x%x]. Should be %d[0x%x]\n", name, arg1, arg1, arg2, arg2, arg3, arg3, r, r, rt, rt);
return error;
}
/*
* test_function - Test a function. Return number of errors
*/
static int test_function(test_ptr t) {
int test_counts[3]; /* number of test values for each arg */
int args = t->args; /* number of function arguments */
int arg_test_range[3] = {0, }; /* test range for each argument */
int i, a1, a2, a3;
int errors = 0;
/* These are the test values for each arg. Declared with the
static attribute so that the array will be allocated in bss
rather than the stack */
static int arg_test_vals[3][MAX_TEST_VALS];
/* Sanity check on the number of args */
if (args < 0 || args > 3) {
printf("Configuration error: invalid number of args (%d) for function %s\n", args, t->name);
exit(1);
}
/* Assign range of argument test vals so as to conserve the total
number of tests, independent of the number of arguments */
if (args == 1) {
arg_test_range[0] = TEST_RANGE;
}
else if (args == 2) {
arg_test_range[0] = pow((double)TEST_RANGE, 0.5); /* sqrt */
arg_test_range[1] = arg_test_range[0];
}
else {
arg_test_range[0] = pow((double)TEST_RANGE, 0.333); /* cbrt */
arg_test_range[1] = arg_test_range[0];
arg_test_range[2] = arg_test_range[0];
}
/* Sanity check on the ranges */
if (arg_test_range[0] < 1)
arg_test_range[0] = 1;
if (arg_test_range[1] < 1)
arg_test_range[1] = 1;
if (arg_test_range[2] < 1)
arg_test_range[2] = 1;
/* Create a test set for each argument */
for (i = 0; i < args; i++) {
test_counts[i] = gen_vals(arg_test_vals[i],
t->arg_ranges[i][0], /* min */
t->arg_ranges[i][1], /* max */
arg_test_range[i],
i);
}
/* Handle timeouts in the test code */
if (timeout_limit > 0) {
int rc;
rc = sigsetjmp(envbuf, 1);
if (rc) {
/* control will reach here if there is a timeout */
errors = 1;
printf("ERROR: Test %s failed.\n Timed out after %d secs (probably infinite loop)\n", t->name, timeout_limit);
return errors;
}
alarm(timeout_limit);
}
/* Test function has no arguments */
if (args == 0) {
errors += test_0_arg(t->solution_funct, t->test_funct, t->name);
return errors;
}
/*
* Test function has at least one argument
*/
/* Iterate over the values for first argument */
for (a1 = 0; a1 < test_counts[0]; a1++) {
if (args == 1) {
errors += test_1_arg(t->solution_funct,
t->test_funct,
arg_test_vals[0][a1],
t->name);
/* Stop testing if there is an error */
if (errors)
return errors;
}
else {
/* if necessary, iterate over values for second argument */
for (a2 = 0; a2 < test_counts[1]; a2++) {
if (args == 2) {
errors += test_2_arg(t->solution_funct,
t->test_funct,
arg_test_vals[0][a1],
arg_test_vals[1][a2],
t->name);
/* Stop testing if there is an error */
if (errors)
return errors;
}
else {
/* if necessary, iterate over vals for third arg */
for (a3 = 0; a3 < test_counts[2]; a3++) {
errors += test_3_arg(t->solution_funct,
t->test_funct,
arg_test_vals[0][a1],
arg_test_vals[1][a2],
arg_test_vals[2][a3],
t->name);
/* Stop testing if there is an error */
if (errors)
return errors;
} /* a3 */
}
} /* a2 */
}
} /* a1 */
return errors;
}
/*
* run_tests - Run series of tests. Return number of errors
*/
static int run_tests()
{
int i;
int errors = 0;
double points = 0.0;
double max_points = 0.0;
printf("Score\tRating\tErrors\tFunction\n");
for (i = 0; test_set[i].solution_funct; i++) {
int terrors;
double tscore;
double tpoints;
if (!test_fname || strcmp(test_set[i].name,test_fname) == 0) {
int rating = global_rating ? global_rating : test_set[i].rating;
terrors = test_function(&test_set[i]);
errors += terrors;
tscore = terrors == 0 ? 1.0 : 0.0;
tpoints = tscore;
points += tpoints;
max_points ++;
if (grade || terrors < 1)
printf(" %.0f\t%d\t%d\t%s\n",
tpoints, rating, terrors, test_set[i].name);
}
}
printf("Total points: %.0f/%.0f\n", points, max_points);
return errors;
}
/*
* get_num_val - Extract hex/decimal/or float value from string
*/
static int get_num_val(char *sval, unsigned *valp) {
char *endp;
/* See if it's an integer or floating point */
int ishex = 0;
int isfloat = 0;
int i;
for (i = 0; sval[i]; i++) {
switch (sval[i]) {
case 'x':
case 'X':
ishex = 1;
break;
case 'e':
case 'E':
if (!ishex)
isfloat = 1;
break;
case '.':
isfloat = 1;
break;
default:
break;
}
}
if (isfloat) {
float fval = strtof(sval, &endp);
if (!*endp) {
*valp = *(unsigned *) &fval;
return 1;
}
return 0;
} else {
long long int llval = strtoll(sval, &endp, 0);
long long int upperbits = llval >> 31;
/* will give -1 for negative, 0 or 1 for positive */
if (!*valp && (upperbits == 0 || upperbits == -1 || upperbits == 1)) {
*valp = (unsigned) llval;
return 1;
}
return 0;
}
}
/*
* usage - Display usage info
*/
static void usage(char *cmd) {
printf("Usage: %s [-hg] [-r <n>] [-f <name> [-1|-2|-3 <val>]*] [-T <time limit>]\n", cmd);
printf(" -1 <val> Specify first function argument\n");
printf(" -2 <val> Specify second function argument\n");
printf(" -3 <val> Specify third function argument\n");
printf(" -f <name> Test only the named function\n");
printf(" -g Compact output for grading (with no error msgs)\n");
printf(" -h Print this message\n");
printf(" -r <n> Give uniform weight of n for all problems\n");
printf(" -T <lim> Set timeout limit to lim\n");
exit(1);
}
/**************
* Main routine
**************/
int main(int argc, char *argv[])
{
char c;
/* parse command line args */
while ((c = getopt(argc, argv, "hgf:r:T:1:2:3:")) != -1)
switch (c) {
case 'h': /* help */
usage(argv[0]);
break;
case 'g': /* grading option for autograder */
grade = 1;
break;
case 'f': /* test only one function */
test_fname = strdup(optarg);
break;
case 'r': /* set global rating for each problem */
global_rating = atoi(optarg);
if (global_rating < 0)
usage(argv[0]);
break;
case '1': /* Get first argument */
has_arg[0] = get_num_val(optarg, &argval[0]);
if (!has_arg[0]) {
printf("Bad argument '%s'\n", optarg);
exit(0);
}
break;
case '2': /* Get first argument */
has_arg[1] = get_num_val(optarg, &argval[1]);
if (!has_arg[1]) {
printf("Bad argument '%s'\n", optarg);
exit(0);
}
break;
case '3': /* Get first argument */
has_arg[2] = get_num_val(optarg, &argval[2]);
if (!has_arg[2]) {
printf("Bad argument '%s'\n", optarg);
exit(0);
}
break;
case 'T': /* Set timeout limit */
timeout_limit = atoi(optarg);
break;
default:
usage(argv[0]);
}
if (timeout_limit > 0) {
Signal(SIGALRM, timeout_handler);
}
/* test each function */
run_tests();
return 0;
}

32
labs/01_datalab/btest.h Normal file
View File

@@ -0,0 +1,32 @@
/*
* CSE4009 Assignment 1 - Data Lab
*/
/* Declare different function types */
typedef int (*funct_t) (void);
typedef int (*funct1_t)(int);
typedef int (*funct2_t)(int, int);
typedef int (*funct3_t)(int, int, int);
/* Combine all the information about a function and its tests as structure */
typedef struct {
char *name; /* String name */
funct_t solution_funct; /* Function */
funct_t test_funct; /* Test function */
int args; /* Number of function arguments */
char *ops; /* List of legal operators. Special case: "$" for floating point */
int op_limit; /* Max number of ops allowed in solution */
int rating; /* Problem rating (1 -- 4) */
int arg_ranges[3][2]; /* Argument ranges. Always defined for 3 args, even if */
/* the function takes fewer. Special case: First arg */
/* must be set to {1,1} for f.p. puzzles */
} test_rec, *test_ptr;
extern test_rec test_set[];

79
labs/01_datalab/decl.c Normal file
View File

@@ -0,0 +1,79 @@
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define TMin INT_MIN
#define TMax INT_MAX
#include "btest.h"
#include "bits.h"
test_rec test_set[] = {
/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This header is separate from features.h so that the compiler can
include it implicitly at the start of every compilation. It must
not itself include <features.h> or any other header that includes
<features.h> because the implicit include comes before any feature
test macros that may be defined in a source file before it first
explicitly includes a system header. GCC knows the name of this
header in order to preinclude it. */
/* glibc's intent is to support the IEC 559 math functionality, real
and complex. If the GCC (4.9 and later) predefined macros
specifying compiler intent are available, use them to determine
whether the overall intent is to support these features; otherwise,
presume an older compiler has intent to support these features and
define these macros by default. */
/* wchar_t uses Unicode 10.0.0. Version 10.0 of the Unicode Standard is
synchronized with ISO/IEC 10646:2017, fifth edition, plus
the following additions from Amendment 1 to the fifth edition:
- 56 emoji characters
- 285 hentaigana
- 3 additional Zanabazar Square characters */
//1
{"bitAnd", (funct_t) bitAnd, (funct_t) test_bitAnd, 2, "| ~", 8, 1,
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}},
{"bitXor", (funct_t) bitXor, (funct_t) test_bitXor, 2, "& ~", 14, 1,
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}},
{"isTmin", (funct_t) isTmin, (funct_t) test_isTmin, 1, "! ~ & ^ | +", 10, 1,
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}},
//2
{"isEqual", (funct_t) isEqual, (funct_t) test_isEqual, 2,
"! ~ & ^ | + << >>", 5, 2,
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}},
{"negate", (funct_t) negate, (funct_t) test_negate, 1,
"! ~ & ^ | + << >>", 5, 2,
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}},
{"getByte", (funct_t) getByte, (funct_t) test_getByte, 2,
"! ~ & ^ | + << >>", 6, 2,
{{TMin, TMax},{0,3},{TMin,TMax}}},
//3
{"isLess", (funct_t) isLess, (funct_t) test_isLess, 2,
"! ~ & ^ | + << >>", 24, 3,
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}},
{"conditional", (funct_t) conditional, (funct_t) test_conditional, 3, "! ~ & ^ | << >>", 16, 3,
{{TMin, TMax},{TMin,TMax},{TMin,TMax}}},
//4
{"floatScale2", (funct_t) floatScale2, (funct_t) test_floatScale2, 1,
"$", 30, 4,
{{1, 1},{1,1},{1,1}}},
{"floatFloat2Int", (funct_t) floatFloat2Int, (funct_t) test_floatFloat2Int, 1,
"$", 30, 4,
{{1, 1},{1,1},{1,1}}},
{"", NULL, NULL, 0, "", 0, 0,
{{0, 0},{0,0},{0,0}}}
};

BIN
labs/01_datalab/dlc Executable file

Binary file not shown.

440
labs/01_datalab/driver.pl Executable file
View File

@@ -0,0 +1,440 @@
#!/usr/bin/perl
#######################################################################
# driver.pl - CSE4009 Data Lab driver
#
# Copyright (c) 2004-2011, R. Bryant and D. O'Hallaron, All rights
# reserved. May not be used, modified, or copied without permission.
#
# Note: The driver can use either btest or the BDD checker to check
# puzzles for correctness. This version of the lab uses btest, which
# has been extended to do better testing of both integer and
# floating-point puzzles.
#
#######################################################################
use strict 'vars';
use Getopt::Std;
use lib ".";
#use Driverlib;
# Set to 1 to use btest, 0 to use the BDD checker.
my $USE_BTEST = 1;
# Generic settings
$| = 1; # Flush stdout each time
umask(0077); # Files created by the user in tmp readable only by that user
$ENV{PATH} = "/usr/local/bin:/usr/bin:/bin";
#
# usage - print help message and terminate
#
sub usage {
printf STDERR "$_[0]\n";
printf STDERR "Usage: $0 [-h] [-u \"nickname\"]\n";
printf STDERR "Options:\n";
printf STDERR " -h Print this message.\n";
printf STDERR " -u \"nickname\" Send autoresult to server, using nickname on scoreboard)\n";
die "\n";
}
##############
# Main routine
##############
my $login = getlogin() || (getpwuid($<))[0] || "unknown";
my $tmpdir = "/var/tmp/datalab.$login.$$";
my $diemsg = "The files are in $tmpdir.";
my $driverfiles;
my $infile;
my $autograded;
my $status;
my $inpuzzles;
my $puzzlecnt;
my $line;
my $blank;
my $name;
my $c_points;
my $c_rating;
my $c_errors;
my $p_points;
my $p_rating;
my $p_errors;
my $total_c_points;
my $total_c_rating;
my $total_p_points;
my $total_p_rating;
my $tops;
my $tpoints;
my $trating;
my $foo;
my $name;
my $msg;
my $nickname;
my $autoresult;
my %puzzle_c_points;
my %puzzle_c_rating;
my %puzzle_c_errors;
my %puzzle_p_points;
my %puzzle_p_ops;
my %puzzle_p_maxops;
my %puzzle_number;
# Parse the command line arguments
no strict;
getopts('hu:f:A');
if ($opt_h) {
usage();
}
# The default input file is bits.c (change with -f)
$infile = "bits.c";
$nickname = "";
#####
# These are command line args that every driver must support
#
# Causes the driver to send an autoresult to the server on behalf of user
if ($opt_u) {
$nickname = $opt_u;
check_nickname($nickname);
}
# Hidden flag that indicates that the driver was invoked by an autograder
if ($opt_A) {
$autograded = $opt_A;
}
#####
# Drivers can also define an arbitary number of other command line args
#
# Optional hidden flag used by the autograder
if ($opt_f) {
$infile = $opt_f;
}
use strict 'vars';
################################################
# Compute the correctness and performance scores
################################################
# Make sure that an executable dlc (data lab compiler) exists
(-e "./dlc" and -x "./dlc")
or die "$0: ERROR: No executable dlc binary.\n";
# If using the bdd checker, then make sure it exists
if (!$USE_BTEST) {
(-e "./bddcheck/cbit/cbit" and -x "./bddcheck/cbit/cbit")
or die "$0: ERROR: No executable cbit binary.\n";
}
#
# Set up the contents of the scratch directory
#
system("mkdir $tmpdir") == 0
or die "$0: Could not make scratch directory $tmpdir.\n";
# Copy the student's work to the scratch directory
unless (system("cp $infile $tmpdir/bits.c") == 0) {
clean($tmpdir);
die "$0: Could not copy file $infile to scratch directory $tmpdir.\n";
}
# Copy the various autograding files to the scratch directory
if ($USE_BTEST) {
$driverfiles = "Makefile dlc btest.c decl.c tests.c btest.h bits.h";
unless (system("cp -r $driverfiles $tmpdir") == 0) {
clean($tmpdir);
die "$0: Could not copy autogradingfiles to $tmpdir.\n";
}
}
else {
$driverfiles = "dlc tests.c bddcheck";
unless (system("cp -r $driverfiles $tmpdir") == 0) {
clean($tmpdir);
die "$0: Could not copy support files to $tmpdir.\n";
}
}
# Change the current working directory to the scratch directory
unless (chdir($tmpdir)) {
clean($tmpdir);
die "$0: Could not change directory to $tmpdir.\n";
}
#
# Generate a zapped (for coding rules) version of bits.c. In this
# zapped version of bits.c, any functions with illegal operators are
# transformed to have empty function bodies.
#
print "1. Running './dlc -z' to identify coding rules violations.\n";
system("cp bits.c save-bits.c") == 0
or die "$0: ERROR: Could not create backup copy of bits.c. $diemsg\n";
system("./dlc -z -o zap-bits.c bits.c") == 0
or die "$0: ERROR: zapped bits.c did not compile. $diemsg\n";
#
# Run btest or BDD checker to determine correctness score
#
if ($USE_BTEST) {
print "\n2. Compiling and running './btest -g' to determine correctness score.\n";
system("cp zap-bits.c bits.c");
# Compile btest
system("make btestexplicit") == 0
or die "$0: Could not make btest in $tmpdir. $diemsg\n";
# Run btest
$status = system("./btest -g > btest-zapped.out 2>&1");
if ($status != 0) {
die "$0: ERROR: btest check failed. $diemsg\n";
}
}
else {
print "\n2. Running './bddcheck/check.pl -g' to determine correctness score.\n";
system("cp zap-bits.c bits.c");
$status = system("./bddcheck/check.pl -g > btest-zapped.out 2>&1");
if ($status != 0) {
die "$0: ERROR: BDD check failed. $diemsg\n";
}
}
#
# Run dlc to identify operator count violations.
#
print "\n3. Running './dlc -Z' to identify operator count violations.\n";
system("./dlc -Z -o Zap-bits.c save-bits.c") == 0
or die "$0: ERROR: dlc unable to generated Zapped bits.c file.\n";
#
# Run btest or the bdd checker to compute performance score
#
if ($USE_BTEST) {
print "\n4. Compiling and running './btest -g -r 2' to determine performance score.\n";
system("cp Zap-bits.c bits.c");
# Compile btest
system("make btestexplicit") == 0
or die "$0: Could not make btest in $tmpdir. $diemsg\n";
print "\n";
# Run btest
$status = system("./btest -g -r 2 > btest-Zapped.out 2>&1");
if ($status != 0) {
die "$0: ERROR: Zapped btest failed. $diemsg\n";
}
}
else {
print "\n4. Running './bddcheck/check.pl -g -r 2' to determine performance score.\n";
system("cp Zap-bits.c bits.c");
$status = system("./bddcheck/check.pl -g -r 2 > btest-Zapped.out 2>&1");
if ($status != 0) {
die "$0: ERROR: Zapped bdd checker failed. $diemsg\n";
}
}
#
# Run dlc to get the operator counts on the zapped input file
#
print "\n5. Running './dlc -e' to get operator count of each function.\n";
$status = system("./dlc -W1 -e zap-bits.c > dlc-opcount.out 2>&1");
if ($status != 0) {
die "$0: ERROR: bits.c did not compile. $diemsg\n";
}
#################################################################
# Collect the correctness and performance results for each puzzle
#################################################################
#
# Collect the correctness results
#
%puzzle_c_points = (); # Correctness score computed by btest
%puzzle_c_errors = (); # Correctness error discovered by btest
%puzzle_c_rating = (); # Correctness puzzle rating (max points)
$inpuzzles = 0; # Becomes true when we start reading puzzle results
$puzzlecnt = 0; # Each puzzle gets a unique number
$total_c_points = 0;
$total_c_rating = 0;
open(INFILE, "$tmpdir/btest-zapped.out")
or die "$0: ERROR: could not open input file $tmpdir/btest-zapped.out\n";
while ($line = <INFILE>) {
chomp($line);
# Notice that we're ready to read the puzzle scores
if ($line =~ /^Score/) {
$inpuzzles = 1;
next;
}
# Notice that we're through reading the puzzle scores
if ($line =~ /^Total/) {
$inpuzzles = 0;
next;
}
# Read and record a puzzle's name and score
if ($inpuzzles) {
($blank, $c_points, $c_rating, $c_errors, $name) = split(/\s+/, $line);
$puzzle_c_points{$name} = $c_points;
$puzzle_c_errors{$name} = $c_errors;
$puzzle_c_rating{$name} = $c_rating;
$puzzle_number{$name} = $puzzlecnt++;
$total_c_points += $c_points;
# $total_c_rating += $c_rating;
$total_c_rating++;
}
}
close(INFILE);
#
# Collect the performance results
#
%puzzle_p_points = (); # Performance points
$inpuzzles = 0; # Becomes true when we start reading puzzle results
$total_p_points = 0;
$total_p_rating = 0;
open(INFILE, "$tmpdir/btest-Zapped.out")
or die "$0: ERROR: could not open input file $tmpdir/btest-Zapped.out\n";
while ($line = <INFILE>) {
chomp($line);
# Notice that we're ready to read the puzzle scores
if ($line =~ /^Score/) {
$inpuzzles = 1;
next;
}
# Notice that we're through reading the puzzle scores
if ($line =~ /^Total/) {
$inpuzzles = 0;
next;
}
# Read and record a puzzle's name and score
if ($inpuzzles) {
($blank, $p_points, $p_rating, $p_errors, $name) = split(/\s+/, $line);
$puzzle_p_points{$name} = $p_points;
$total_p_points += $p_points;
$total_p_rating += $p_rating;
}
}
close(INFILE);
#
# Collect the operator counts generated by dlc
#
open(INFILE, "$tmpdir/dlc-opcount.out")
or die "$0: ERROR: could not open input file $tmpdir/dlc-opcount.out\n";
$tops = 0;
while ($line = <INFILE>) {
chomp($line);
if ($line =~ /(\d+) operators/) {
($foo, $foo, $foo, $name, $msg) = split(/:/, $line);
$puzzle_p_ops{$name} = $1;
$tops += $1;
}
}
close(INFILE);
#
# Print a table of results sorted by puzzle number
#
print "\n";
printf("%s\t%s\n", "Correctness Results", "Perf Results");
printf("%s\t%s\t%s\t%s\t%s\t%s\n", "Points", "Rating", "Errors",
"Points", "Ops", "Puzzle");
foreach $name (sort {$puzzle_number{$a} <=> $puzzle_number{$b}}
keys %puzzle_number) {
printf("%d\t%d\t%d\t%d\t%d\t\%s\n",
$puzzle_c_points{$name},
$puzzle_c_rating{$name},
$puzzle_c_errors{$name},
$puzzle_p_points{$name},
$puzzle_p_ops{$name},
$name);
}
$tpoints = $total_c_points + $total_p_points;
$trating = $total_c_rating + $total_p_rating;
print "\nScore = $tpoints/$trating [$total_c_points/$total_c_rating Corr + $total_p_points/$total_p_rating Perf] ($tops total operators)\n";
#
# Optionally send the autoresult to the contest server if the driver
# was called with the -u command line flag.
#
if ($nickname) {
# Generate the autoresult
$autoresult = "$tpoints|$total_c_points|$total_p_points|$tops";
foreach $name (sort {$puzzle_number{$a} <=> $puzzle_number{$b}}
keys %puzzle_number) {
$autoresult .= " |$name:$puzzle_c_points{$name}:$puzzle_c_rating{$name}:$puzzle_p_points{$name}:$puzzle_p_ops{$name}";
}
# Post the autoresult to the server. The Linux login id is
# concatenated with the user-supplied nickname for some (very) loose
# authentication of submissions.
&Driverlib::driver_post("$login:$nickname", $autoresult, $autograded);
}
# Clean up and exit
clean ($tmpdir);
exit;
##################
# Helper functions
#
#
# check_nickname - Check a nickname for legality
#
sub check_nickname {
my $nickname = shift;
# Nicknames can't be empty
if (length($nickname) < 1) {
die "$0: Error: Empty nickname.\n";
}
# Nicknames can't be too long
if (length($nickname) > 35) {
die "$0: Error: Nickname exceeds 35 characters.\n";
}
# Nicknames can have restricted set of metacharacters (e.g., no #
# HTML tags)
if (!($nickname =~ /^[_-\w.,'@ ]+$/)) {
die "$0: Error: Illegal character in nickname. Only alphanumerics, apostrophes, commas, periods, dashes, underscores, and ampersands are allowed.\n";
}
# Nicknames can't be all whitespace
if ($nickname =~ /^\s*$/) {
die "$0: Error: Nickname is all whitespace.\n";
}
}
#
# clean - remove the scratch directory
#
sub clean {
my $tmpdir = shift;
system("rm -rf $tmpdir");
}

151
labs/01_datalab/fshow.c Normal file
View File

@@ -0,0 +1,151 @@
/* Display structure of floating-point numbers */
#include <stdio.h>
#include <stdlib.h>
float strtof(const char *nptr, char **endptr);
#define FLOAT_SIZE 32
#define FRAC_SIZE 23
#define EXP_SIZE 8
#define BIAS ((1<<(EXP_SIZE-1))-1)
#define FRAC_MASK ((1<<FRAC_SIZE)-1)
#define EXP_MASK ((1<<EXP_SIZE)-1)
/* Floating point helpers */
unsigned f2u(float f)
{
union {
unsigned u;
float f;
} v;
v.u = 0;
v.f = f;
return v.u;
}
static float u2f(unsigned u)
{
union {
unsigned u;
float f;
} v;
v.u = u;
return v.f;
}
/* Get exponent */
unsigned get_exp(unsigned uf)
{
return (uf>>FRAC_SIZE) & EXP_MASK;
}
/* Get fraction */
unsigned get_frac(unsigned uf)
{
return uf & FRAC_MASK;
}
/* Get sign */
unsigned get_sign(unsigned uf)
{
return (uf>>(FLOAT_SIZE-1)) & 0x1;
}
void show_float(unsigned uf)
{
float f = u2f(uf);
unsigned exp = get_exp(uf);
unsigned frac = get_frac(uf);
unsigned sign = get_sign(uf);
printf("\nFloating point value %.10g\n", f);
printf("Bit Representation 0x%.8x, sign = %x, exponent = 0x%.2x, fraction = 0x%.6x\n",
uf, sign, exp, frac);
if (exp == EXP_MASK) {
if (frac == 0) {
printf("%cInfinity\n", sign ? '-' : '+');
} else
printf("Not-A-Number\n");
} else {
int denorm = (exp == 0);
int uexp = denorm ? 1-BIAS : exp - BIAS;
int mantissa = denorm ? frac : frac + (1<<FRAC_SIZE);
float fman = (float) mantissa / (float) (1<<FRAC_SIZE);
printf("%s. %c%.10f X 2^(%d)\n",
denorm ? "Denormalized" : "Normalized",
sign ? '-' : '+',
fman, uexp);
}
}
/* Extract hex/decimal/or float value from string */
static int get_num_val(char *sval, unsigned *valp) {
char *endp;
/* See if it's an integer or floating point */
int ishex = 0;
int isfloat = 0;
int i;
for (i = 0; sval[i]; i++) {
switch (sval[i]) {
case 'x':
case 'X':
ishex = 1;
break;
case 'e':
case 'E':
if (!ishex)
isfloat = 1;
break;
case '.':
isfloat = 1;
break;
default:
break;
}
}
if (isfloat) {
float fval = strtof(sval, &endp);
if (!*endp) {
*valp = *(unsigned *) &fval;
return 1;
}
return 0;
} else {
long long int llval = strtoll(sval, &endp, 0);
long long int upperbits = llval >> 31;
/* will give -1 for negative, 0 or 1 for positive */
if (valp && (upperbits == 0 || upperbits == -1 || upperbits == 1)) {
*valp = (unsigned) llval;
return 1;
}
return 0;
}
}
void usage(char *fname) {
printf("Usage: %s val1 val2 ...\n", fname);
printf("Values may be given as hex patterns or as floating point numbers\n");
exit(0);
}
int main(int argc, char *argv[])
{
int i;
unsigned uf;
if (argc < 2)
usage(argv[0]);
for (i = 1; i < argc; i++) {
char *sval = argv[i];
if (get_num_val(sval, &uf)) {
show_float(uf);
} else {
printf("Invalid 32-bit number: '%s'\n", sval);
usage(argv[0]);
}
}
return 0;
}

75
labs/01_datalab/ishow.c Normal file
View File

@@ -0,0 +1,75 @@
/* Display value of fixed point numbers */
#include <stdlib.h>
#include <stdio.h>
/* Extract hex/decimal/or float value from string */
static int get_num_val(char *sval, unsigned *valp) {
char *endp;
/* See if it's an integer or floating point */
int ishex = 0;
int isfloat = 0;
int i;
for (i = 0; sval[i]; i++) {
switch (sval[i]) {
case 'x':
case 'X':
ishex = 1;
break;
case 'e':
case 'E':
if (!ishex)
isfloat = 1;
break;
case '.':
isfloat = 1;
break;
default:
break;
}
}
if (isfloat) {
return 0; /* Not supposed to have a float here */
} else {
long long int llval = strtoll(sval, &endp, 0);
long long int upperbits = llval >> 31;
/* will give -1 for negative, 0 or 1 for positive */
if (valp && (upperbits == 0 || upperbits == -1 || upperbits == 1)) {
*valp = (unsigned) llval;
return 1;
}
return 0;
}
}
void show_int(unsigned uf)
{
printf("Hex = 0x%.8x,\tSigned = %d,\tUnsigned = %u\n",
uf, (int) uf, uf);
}
void usage(char *fname) {
printf("Usage: %s val1 val2 ...\n", fname);
printf("Values may be given in hex or decimal\n");
exit(0);
}
int main(int argc, char *argv[])
{
int i;
unsigned uf;
if (argc < 2)
usage(argv[0]);
for (i = 1; i < argc; i++) {
char *sval = argv[i];
if (get_num_val(sval, &uf)) {
show_int(uf);
} else {
printf("Cannot convert '%s' to 32-bit number\n", sval);
}
}
return 0;
}

124
labs/01_datalab/tests.c Normal file
View File

@@ -0,0 +1,124 @@
/* Testing Code */
#include <limits.h>
#include <math.h>
/* Routines used by floation point test code */
/* Convert from bit level representation to floating point number */
float u2f(unsigned u) {
union {
unsigned u;
float f;
} a;
a.u = u;
return a.f;
}
/* Convert from floating point number to bit-level representation */
unsigned f2u(float f) {
union {
unsigned u;
float f;
} a;
a.f = f;
return a.u;
}
/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This header is separate from features.h so that the compiler can
include it implicitly at the start of every compilation. It must
not itself include <features.h> or any other header that includes
<features.h> because the implicit include comes before any feature
test macros that may be defined in a source file before it first
explicitly includes a system header. GCC knows the name of this
header in order to preinclude it. */
/* glibc's intent is to support the IEC 559 math functionality, real
and complex. If the GCC (4.9 and later) predefined macros
specifying compiler intent are available, use them to determine
whether the overall intent is to support these features; otherwise,
presume an older compiler has intent to support these features and
define these macros by default. */
/* wchar_t uses Unicode 10.0.0. Version 10.0 of the Unicode Standard is
synchronized with ISO/IEC 10646:2017, fifth edition, plus
the following additions from Amendment 1 to the fifth edition:
- 56 emoji characters
- 285 hentaigana
- 3 additional Zanabazar Square characters */
//1
int test_bitAnd(int x, int y)
{
return x&y;
}
int test_bitXor(int x, int y)
{
return x^y;
}
int test_isTmin(int x) {
return x == 0x80000000;
}
//2
int test_isEqual(int x, int y)
{
return x == y;
}
int test_negate(int x) {
return -x;
}
int test_getByte(int x, int n)
{
unsigned char byte;
switch(n) {
case 0:
byte = x;
break;
case 1:
byte = x >> 8;
break;
case 2:
byte = x >> 16;
break;
default:
byte = x >> 24;
break;
}
return (int) (unsigned) byte;
}
//3
int test_isLess(int x, int y)
{
return x < y;
}
int test_conditional(int x, int y, int z)
{
return x?y:z;
}
//4
unsigned test_floatScale2(unsigned uf) {
float f = u2f(uf);
float tf = 2*f;
if (isnan(f))
return uf;
else
return f2u(tf);
}
int test_floatFloat2Int(unsigned uf) {
float f = u2f(uf);
int x = (int) f;
return x;
}

View File

@@ -0,0 +1,7 @@
I can see Russia from my house!
1 2 4 8 16 32
0 262
6 6 DrEvil
eabhcc
2 1 5 3 4 6
22

BIN
labs/02_bomblab/bomb Executable file

Binary file not shown.

115
labs/02_bomblab/bomb.c Normal file
View File

@@ -0,0 +1,115 @@
/***************************************************************************
* Dr. Evil's Insidious Bomb, Version 1.1
* Copyright 2011, Dr. Evil Incorporated. All rights reserved.
*
* LICENSE:
*
* Dr. Evil Incorporated (the PERPETRATOR) hereby grants you (the
* VICTIM) explicit permission to use this bomb (the BOMB). This is a
* time limited license, which expires on the death of the VICTIM.
* The PERPETRATOR takes no responsibility for damage, frustration,
* insanity, bug-eyes, carpal-tunnel syndrome, loss of sleep, or other
* harm to the VICTIM. Unless the PERPETRATOR wants to take credit,
* that is. The VICTIM may not distribute this bomb source code to
* any enemies of the PERPETRATOR. No VICTIM may debug,
* reverse-engineer, run "strings" on, decompile, decrypt, or use any
* other technique to gain knowledge of and defuse the BOMB. BOMB
* proof clothing may not be worn when handling this program. The
* PERPETRATOR will not apologize for the PERPETRATOR's poor sense of
* humor. This license is null and void where the BOMB is prohibited
* by law.
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "support.h"
#include "phases.h"
/*
* Note to self: Remember to erase this file so my victims will have no
* idea what is going on, and so they will all blow up in a
* spectaculary fiendish explosion. -- Dr. Evil
*/
FILE *infile;
int main(int argc, char *argv[])
{
char *input;
/* Note to self: remember to port this bomb to Windows and put a
* fantastic GUI on it. */
/* When run with no arguments, the bomb reads its input lines
* from standard input. */
if (argc == 1) {
infile = stdin;
}
/* When run with one argument <file>, the bomb reads from <file>
* until EOF, and then switches to standard input. Thus, as you
* defuse each phase, you can add its defusing string to <file> and
* avoid having to retype it. */
else if (argc == 2) {
if (!(infile = fopen(argv[1], "r"))) {
printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]);
exit(8);
}
}
/* You can't call the bomb with more than 1 command line argument. */
else {
printf("Usage: %s [<input_file>]\n", argv[0]);
exit(8);
}
/* Do all sorts of secret stuff that makes the bomb harder to defuse. */
initialize_bomb();
printf("Welcome to my fiendish little bomb. You have 6 phases with\n");
printf("which to blow yourself up. Have a nice day!\n");
/* Hmm... Six phases must be more secure than one phase! */
input = read_line(); /* Get input */
phase_1(input); /* Run the phase */
phase_defused(); /* Drat! They figured it out!
* Let me know how they did it. */
printf("Phase 1 defused. How about the next one?\n");
/* The second phase is harder. No one will ever figure out
* how to defuse this... */
input = read_line();
phase_2(input);
phase_defused();
printf("That's number 2. Keep going!\n");
/* I guess this is too easy so far. Some more complex code will
* confuse people. */
input = read_line();
phase_3(input);
phase_defused();
printf("Halfway there!\n");
/* Oh yeah? Well, how good is your math? Try on this saucy problem! */
input = read_line();
phase_4(input);
phase_defused();
printf("So you got that one. Try this one.\n");
/* Round and 'round in memory we go, where we stop, the bomb blows! */
input = read_line();
phase_5(input);
phase_defused();
printf("Good work! On to the next...\n");
/* This phase will never be used, since no one will get past the
* earlier ones. But just in case, make this one extra hard. */
input = read_line();
phase_6(input);
phase_defused();
/* Wow, they got it! But isn't something... missing? Perhaps
* something they overlooked? Mua ha ha ha ha! */
return 0;
}

93
labs/03_shlab/Makefile Normal file
View File

@@ -0,0 +1,93 @@
# Makefile for the Shell Lab
TEAM = INDIVIDUAL
VERSION = 1
DRIVER = ./sdriver.pl
TSH = ./tsh
TSHREF = ./tshref
TSHARGS = "-p"
CC = gcc
CFLAGS = -Wall -O2
FILES = $(TSH) ./myspin ./mysplit ./mystop ./myint
all: $(FILES)
##################
# Regression tests
##################
tests: test01 test02 test03 test04 test05 test06 test07 test08 test09 test10 test11 test12 test13 test14 test15 test16
# Run tests using the student's shell program
test01:
$(DRIVER) -t trace01.txt -s $(TSH) -a $(TSHARGS)
test02:
$(DRIVER) -t trace02.txt -s $(TSH) -a $(TSHARGS)
test03:
$(DRIVER) -t trace03.txt -s $(TSH) -a $(TSHARGS)
test04:
$(DRIVER) -t trace04.txt -s $(TSH) -a $(TSHARGS)
test05:
$(DRIVER) -t trace05.txt -s $(TSH) -a $(TSHARGS)
test06:
$(DRIVER) -t trace06.txt -s $(TSH) -a $(TSHARGS)
test07:
$(DRIVER) -t trace07.txt -s $(TSH) -a $(TSHARGS)
test08:
$(DRIVER) -t trace08.txt -s $(TSH) -a $(TSHARGS)
test09:
$(DRIVER) -t trace09.txt -s $(TSH) -a $(TSHARGS)
test10:
$(DRIVER) -t trace10.txt -s $(TSH) -a $(TSHARGS)
test11:
$(DRIVER) -t trace11.txt -s $(TSH) -a $(TSHARGS)
test12:
$(DRIVER) -t trace12.txt -s $(TSH) -a $(TSHARGS)
test13:
$(DRIVER) -t trace13.txt -s $(TSH) -a $(TSHARGS)
test14:
$(DRIVER) -t trace14.txt -s $(TSH) -a $(TSHARGS)
test15:
$(DRIVER) -t trace15.txt -s $(TSH) -a $(TSHARGS)
test16:
$(DRIVER) -t trace16.txt -s $(TSH) -a $(TSHARGS)
rtests: rtest01 rtest02 rtest03 rtest04 rtest05 rtest06 rtest07 rtest08 rtest09 rtest10 rtest11 rtest12 rtest13 rtest14 rtest15 rtest16
# Run the tests using the reference shell program
rtest01:
$(DRIVER) -t trace01.txt -s $(TSHREF) -a $(TSHARGS)
rtest02:
$(DRIVER) -t trace02.txt -s $(TSHREF) -a $(TSHARGS)
rtest03:
$(DRIVER) -t trace03.txt -s $(TSHREF) -a $(TSHARGS)
rtest04:
$(DRIVER) -t trace04.txt -s $(TSHREF) -a $(TSHARGS)
rtest05:
$(DRIVER) -t trace05.txt -s $(TSHREF) -a $(TSHARGS)
rtest06:
$(DRIVER) -t trace06.txt -s $(TSHREF) -a $(TSHARGS)
rtest07:
$(DRIVER) -t trace07.txt -s $(TSHREF) -a $(TSHARGS)
rtest08:
$(DRIVER) -t trace08.txt -s $(TSHREF) -a $(TSHARGS)
rtest09:
$(DRIVER) -t trace09.txt -s $(TSHREF) -a $(TSHARGS)
rtest10:
$(DRIVER) -t trace10.txt -s $(TSHREF) -a $(TSHARGS)
rtest11:
$(DRIVER) -t trace11.txt -s $(TSHREF) -a $(TSHARGS)
rtest12:
$(DRIVER) -t trace12.txt -s $(TSHREF) -a $(TSHARGS)
rtest13:
$(DRIVER) -t trace13.txt -s $(TSHREF) -a $(TSHARGS)
rtest14:
$(DRIVER) -t trace14.txt -s $(TSHREF) -a $(TSHARGS)
rtest15:
$(DRIVER) -t trace15.txt -s $(TSHREF) -a $(TSHARGS)
rtest16:
$(DRIVER) -t trace16.txt -s $(TSHREF) -a $(TSHARGS)
# clean up
clean:
rm -f $(FILES) *.o *~

22
labs/03_shlab/README Normal file
View File

@@ -0,0 +1,22 @@
************************************
* CSE4009 Assignment 3 - Shell Lab *
************************************
Files:
Makefile # Compiles your shell program and runs the tests
README # This file
tsh.c # The shell program that you will write and hand in
tshref # The reference shell binary.
# The remaining files are used to test your shell
sdriver.pl # The trace-driven shell driver
trace*.txt # The 15 trace files that control the shell driver
tshref.out # Example output of the reference shell on all 16 traces
# Little C programs that are called by the trace files
myspin.c # Takes argument <n> and spins for <n> seconds
mysplit.c # Forks a child that spins for <n> seconds
mystop.c # Spins for <n> seconds and sends SIGTSTP to itself
myint.c # Spins for <n> seconds and sends SIGINT to itself

36
labs/03_shlab/myint.c Normal file
View File

@@ -0,0 +1,36 @@
/*
* myint.c - Another handy routine for testing your tiny shell
*
* usage: myint <n>
* Sleeps for <n> seconds and sends SIGINT to itself.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
int main(int argc, char **argv)
{
int i, secs;
pid_t pid;
if (argc != 2) {
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
exit(0);
}
secs = atoi(argv[1]);
for (i=0; i < secs; i++)
sleep(1);
pid = getpid();
if (kill(pid, SIGINT) < 0)
fprintf(stderr, "kill (int) error");
exit(0);
}

24
labs/03_shlab/myspin.c Normal file
View File

@@ -0,0 +1,24 @@
/*
* myspin.c - A handy program for testing your tiny shell
*
* usage: myspin <n>
* Sleeps for <n> seconds in 1-second chunks.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int i, secs;
if (argc != 2) {
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
exit(0);
}
secs = atoi(argv[1]);
for (i=0; i < secs; i++)
sleep(1);
exit(0);
}

35
labs/03_shlab/mysplit.c Normal file
View File

@@ -0,0 +1,35 @@
/*
* mysplit.c - Another handy routine for testing your tiny shell
*
* usage: mysplit <n>
* Fork a child that spins for <n> seconds in 1-second chunks.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
int main(int argc, char **argv)
{
int i, secs;
if (argc != 2) {
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
exit(0);
}
secs = atoi(argv[1]);
if (fork() == 0) { /* child */
for (i=0; i < secs; i++)
sleep(1);
exit(0);
}
/* parent waits for child to terminate */
wait(NULL);
exit(0);
}

36
labs/03_shlab/mystop.c Normal file
View File

@@ -0,0 +1,36 @@
/*
* mystop.c - Another handy routine for testing your tiny shell
*
* usage: mystop <n>
* Sleeps for <n> seconds and sends SIGTSTP to itself.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
int main(int argc, char **argv)
{
int i, secs;
pid_t pid;
if (argc != 2) {
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
exit(0);
}
secs = atoi(argv[1]);
for (i=0; i < secs; i++)
sleep(1);
pid = getpid();
if (kill(-pid, SIGTSTP) < 0)
fprintf(stderr, "kill (tstp) error");
exit(0);
}

210
labs/03_shlab/sdriver.pl Executable file
View File

@@ -0,0 +1,210 @@
#!/usr/bin/perl
#!/usr/local/bin/perl
use Getopt::Std;
use FileHandle;
use IPC::Open2;
#######################################################################
# sdriver.pl - Shell driver
#
# Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved.
# May not be used, modified, or copied without permission.
#
# The driver runs a student's shell program as a child, sends
# commands and signals to the child as directed by a trace file,
# and captures and displays the output produced by the child.
#
# Tracefile format:
#
# The tracefile consists of text lines that are either blank lines,
# comment lines, driver commands, or shell commands. Blank lines are
# ignored. Comment lines begin with "#" and are echo'd without change
# to stdout. Driver commands are intepreted by the driver and are not
# passed to the child shell. All other lines are shell commands and
# are passed without modification to the shell, which reads them on
# stdin. Output produced by the child on stdout/stderr is read by
# the parent and printed on its stdout.
#
# Driver commands:
# TSTP Send a SIGTSTP signal to the child
# INT Send a SIGINT signal to the child
# QUIT Send a SIGQUIT signal to the child
# KILL Send a SIGKILL signal to the child
# CLOSE Close Writer (sends EOF signal to child)
# WAIT Wait() for child to terminate
# SLEEP <n> Sleep for <n> seconds
#
######################################################################
#
# usage - print help message and terminate
#
sub usage
{
printf STDERR "$_[0]\n";
printf STDERR "Usage: $0 [-hv] -t <trace> -s <shellprog> -a <args>\n";
printf STDERR "Options:\n";
printf STDERR " -h Print this message\n";
printf STDERR " -v Be more verbose\n";
printf STDERR " -t <trace> Trace file\n";
printf STDERR " -s <shell> Shell program to test\n";
printf STDERR " -a <args> Shell arguments\n";
printf STDERR " -g Generate output for autograder\n";
die "\n" ;
}
# Parse the command line arguments
getopts('hgvt:s:a:');
if ($opt_h) {
usage();
}
if (!$opt_t) {
usage("Missing required -t argument");
}
if (!$opt_s) {
usage("Missing required -s argument");
}
$verbose = $opt_v;
$infile = $opt_t;
$shellprog = $opt_s;
$shellargs = $opt_a;
$grade = $opt_g;
# Make sure the input script exists and is readable
-e $infile
or die "$0: ERROR: $infile not found\n";
-r $infile
or die "$0: ERROR: $infile is not readable\n";
# Make sure the shell program exists and is executable
-e $shellprog
or die "$0: ERROR: $shellprog not found\n";
-x $shellprog
or die "$0: ERROR: $shellprog is not executable\n";
# Open the input script
open INFILE, $infile
or die "$0: ERROR: Couldn't open input file $infile: $!\n";
#
# Fork a child, run the shell in it, and connect the parent
# and child with a pair of unidirectional pipes:
# parent:Writer -> child:stdin
# child:stdout -> parent:Reader
#
$pid = open2(\*Reader, \*Writer, "$shellprog $shellargs");
Writer->autoflush();
# The autograder will want to know the child shell's pid
if ($grade) {
print ("pid=$pid\n");
}
#
# Parent reads a trace file, sends commands to the child shell.
#
while (<INFILE>) {
$line = $_;
chomp($line);
# Comment line
if ($line =~ /^#/) {
print "$line\n";
}
# Blank line
elsif ($line =~ /^\s*$/) {
if ($verbose) {
print "$0: Ignoring blank line\n";
}
}
# Send SIGTSTP (ctrl-z)
elsif ($line =~ /TSTP/) {
if ($verbose) {
print "$0: Sending SIGTSTP signal to process $pid\n";
}
kill 'TSTP', $pid;
}
# Send SIGINT (ctrl-c)
elsif ($line =~ /INT/) {
if ($verbose) {
print "$0: Sending SIGINT signal to process $pid\n";
}
kill 'INT', $pid;
}
# Send SIGQUIT (whenever we need graceful termination)
elsif ($line =~ /QUIT/) {
if ($verbose) {
print "$0: Sending SIGQUIT signal to process $pid\n";
}
kill 'QUIT', $pid;
}
# Send SIGKILL
elsif ($line =~ /KILL/) {
if ($verbose) {
print "$0: Sending SIGKILL signal to process $pid\n";
}
kill 'KILL', $pid;
}
# Close pipe (sends EOF notification to child)
elsif ($line =~ /CLOSE/) {
if ($verbose) {
print "$0: Closing output end of pipe to child $pid\n";
}
close Writer;
}
# Wait for child to terminate
elsif ($line =~ /WAIT/) {
if ($verbose) {
print "$0: Waiting for child $pid\n";
}
wait;
if ($verbose) {
print "$0: Child $pid reaped\n";
}
}
# Sleep
elsif ($line =~ /SLEEP (\d+)/) {
if ($verbose) {
print "$0: Sleeping $1 secs\n";
}
sleep $1;
}
# Unknown input
else {
if ($verbose) {
print "$0: Sending :$line: to child $pid\n";
}
print Writer "$line\n";
}
}
#
# Parent echoes the output produced by the child.
#
close Writer;
if ($verbose) {
print "$0: Reading data from child $pid\n";
}
while ($line = <Reader>) {
print $line;
}
close Reader;
# Finally, parent reaps child
wait;
if ($verbose) {
print "$0: Shell terminated\n";
}
exit;

View File

@@ -0,0 +1,5 @@
#
# trace01.txt - Properly terminate on EOF.
#
CLOSE
WAIT

View File

@@ -0,0 +1,5 @@
#
# trace02.txt - Process builtin quit command.
#
quit
WAIT

View File

@@ -0,0 +1,5 @@
#
# trace03.txt - Run a foreground job.
#
/bin/echo tsh> quit
quit

View File

@@ -0,0 +1,5 @@
#
# trace04.txt - Run a background job.
#
/bin/echo -e tsh> ./myspin 1 \046
./myspin 1 &

11
labs/03_shlab/trace05.txt Normal file
View File

@@ -0,0 +1,11 @@
#
# trace05.txt - Process jobs builtin command.
#
/bin/echo -e tsh> ./myspin 2 \046
./myspin 2 &
/bin/echo -e tsh> ./myspin 3 \046
./myspin 3 &
/bin/echo tsh> jobs
jobs

View File

@@ -0,0 +1,8 @@
#
# trace06.txt - Forward SIGINT to foreground job.
#
/bin/echo -e tsh> ./myspin 4
./myspin 4
SLEEP 2
INT

14
labs/03_shlab/trace07.txt Normal file
View File

@@ -0,0 +1,14 @@
#
# trace07.txt - Forward SIGINT only to foreground job.
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo -e tsh> ./myspin 5
./myspin 5
SLEEP 2
INT
/bin/echo tsh> jobs
jobs

14
labs/03_shlab/trace08.txt Normal file
View File

@@ -0,0 +1,14 @@
#
# trace08.txt - Forward SIGTSTP only to foreground job.
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo -e tsh> ./myspin 5
./myspin 5
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs

20
labs/03_shlab/trace09.txt Normal file
View File

@@ -0,0 +1,20 @@
#
# trace09.txt - Process bg builtin command
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo -e tsh> ./myspin 5
./myspin 5
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> bg %2
bg %2
/bin/echo tsh> jobs
jobs

22
labs/03_shlab/trace10.txt Normal file
View File

@@ -0,0 +1,22 @@
#
# trace10.txt - Process fg builtin command.
#
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
SLEEP 1
/bin/echo tsh> fg %1
fg %1
SLEEP 1
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> fg %1
fg %1
/bin/echo tsh> jobs
jobs

12
labs/03_shlab/trace11.txt Normal file
View File

@@ -0,0 +1,12 @@
#
# trace11.txt - Forward SIGINT to every process in foreground process group
#
/bin/echo -e tsh> ./mysplit 4
./mysplit 4
SLEEP 2
INT
/bin/echo tsh> /bin/ps a
/bin/ps a

17
labs/03_shlab/trace12.txt Normal file
View File

@@ -0,0 +1,17 @@
#
# trace12.txt - Forward SIGTSTP to every process in foreground process group
#
/bin/echo -e tsh> ./mysplit 4
./mysplit 4
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> /bin/ps a
/bin/ps a

23
labs/03_shlab/trace13.txt Normal file
View File

@@ -0,0 +1,23 @@
#
# trace13.txt - Restart every stopped process in process group
#
/bin/echo -e tsh> ./mysplit 4
./mysplit 4
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> /bin/ps a
/bin/ps a
/bin/echo tsh> fg %1
fg %1
/bin/echo tsh> /bin/ps a
/bin/ps a

47
labs/03_shlab/trace14.txt Normal file
View File

@@ -0,0 +1,47 @@
#
# trace14.txt - Simple error handling
#
/bin/echo tsh> ./bogus
./bogus
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo tsh> fg
fg
/bin/echo tsh> bg
bg
/bin/echo tsh> fg a
fg a
/bin/echo tsh> bg a
bg a
/bin/echo tsh> fg 9999999
fg 9999999
/bin/echo tsh> bg 9999999
bg 9999999
/bin/echo tsh> fg %2
fg %2
/bin/echo tsh> fg %1
fg %1
SLEEP 2
TSTP
/bin/echo tsh> bg %2
bg %2
/bin/echo tsh> bg %1
bg %1
/bin/echo tsh> jobs
jobs

46
labs/03_shlab/trace15.txt Normal file
View File

@@ -0,0 +1,46 @@
#
# trace15.txt - Putting it all together
#
/bin/echo tsh> ./bogus
./bogus
/bin/echo tsh> ./myspin 10
./myspin 10
SLEEP 2
INT
/bin/echo -e tsh> ./myspin 3 \046
./myspin 3 &
/bin/echo -e tsh> ./myspin 4 \046
./myspin 4 &
/bin/echo tsh> jobs
jobs
/bin/echo tsh> fg %1
fg %1
SLEEP 2
TSTP
/bin/echo tsh> jobs
jobs
/bin/echo tsh> bg %3
bg %3
/bin/echo tsh> bg %1
bg %1
/bin/echo tsh> jobs
jobs
/bin/echo tsh> fg %1
fg %1
/bin/echo tsh> quit
quit

16
labs/03_shlab/trace16.txt Normal file
View File

@@ -0,0 +1,16 @@
#
# trace16.txt - Tests whether the shell can handle SIGTSTP and SIGINT
# signals that come from other processes instead of the terminal.
#
/bin/echo tsh> ./mystop 2
./mystop 2
SLEEP 3
/bin/echo tsh> jobs
jobs
/bin/echo tsh> ./myint 2
./myint 2

606
labs/03_shlab/tsh.c Normal file
View File

@@ -0,0 +1,606 @@
/*
* tsh - A tiny shell program with job control
*
* Name: Hajin Ju
* Student id: 2024062806
*/
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
/* Misc manifest constants */
#define MAXLINE 1024 /* max line size */
#define MAXARGS 128 /* max args on a command line */
#define MAXJOBS 16 /* max jobs at any point in time */
#define MAXJID 1 << 16 /* max job ID */
/* Job states */
#define UNDEF 0 /* undefined */
#define FG 1 /* running in foreground */
#define BG 2 /* running in background */
#define ST 3 /* stopped */
/*
* Jobs states: FG (foreground), BG (background), ST (stopped)
* Job state transitions and enabling actions:
* FG -> ST : ctrl-z
* ST -> FG : fg command
* ST -> BG : bg command
* BG -> FG : fg command
* At most 1 job can be in the FG state.
*/
/* Global variables */
extern char **environ; /* defined in libc */
char prompt[] = "tsh> "; /* command line prompt (DO NOT CHANGE) */
int verbose = 0; /* if true, print additional output */
int nextjid = 1; /* next job ID to allocate */
char sbuf[MAXLINE]; /* for composing sprintf messages */
struct job_t { /* The job struct */
pid_t pid; /* job PID */
int jid; /* job ID [1, 2, ...] */
int state; /* UNDEF, BG, FG, or ST */
char cmdline[MAXLINE]; /* command line */
};
struct job_t jobs[MAXJOBS]; /* The job list */
/* End global variables */
/* Function prototypes */
/*----------------------------------------------------------------------------
* Functions that you will implement
*/
void eval(char *cmdline);
int builtin_cmd(char **argv);
void do_bgfg(char **argv);
void waitfg(pid_t pid);
void sigchld_handler(int sig);
void sigint_handler(int sig);
void sigtstp_handler(int sig);
/*----------------------------------------------------------------------------*/
/* These functions are already implemented for your convenience */
int parseline(const char *cmdline, char **argv);
void sigquit_handler(int sig);
void clearjob(struct job_t *job);
void initjobs(struct job_t *jobs);
int maxjid(struct job_t *jobs);
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline);
int deletejob(struct job_t *jobs, pid_t pid);
pid_t fgpid(struct job_t *jobs);
struct job_t *getjobpid(struct job_t *jobs, pid_t pid);
struct job_t *getjobjid(struct job_t *jobs, int jid);
int pid2jid(pid_t pid);
void listjobs(struct job_t *jobs);
void usage(void);
void unix_error(char *msg);
void app_error(char *msg);
typedef void handler_t(int);
handler_t *Signal(int signum, handler_t *handler);
/*
* main - The shell's main routine
*/
int main(int argc, char **argv) {
char c;
char cmdline[MAXLINE];
int emit_prompt = 1; /* emit prompt (default) */
/* Redirect stderr to stdout (so that driver will get all output
* on the pipe connected to stdout) */
dup2(1, 2);
/* Parse the command line */
while ((c = getopt(argc, argv, "hvp")) != EOF) {
switch (c) {
case 'h': /* print help message */
usage();
break;
case 'v': /* emit additional diagnostic info */
verbose = 1;
break;
case 'p': /* don't print a prompt */
emit_prompt = 0; /* handy for automatic testing */
break;
default:
usage();
}
}
/* Install the signal handlers */
/* These are the ones you will need to implement */
Signal(SIGINT, sigint_handler); /* ctrl-c */
Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */
Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
/* This one provides a clean way to kill the shell */
Signal(SIGQUIT, sigquit_handler);
/* Initialize the job list */
initjobs(jobs);
/* Execute the shell's read/eval loop */
while (1) {
/* Read command line */
if (emit_prompt) {
printf("%s", prompt);
fflush(stdout);
}
if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))
app_error("fgets error");
if (feof(stdin)) { /* End of file (ctrl-d) */
fflush(stdout);
exit(0);
}
/* Evaluate the command line */
eval(cmdline);
fflush(stdout);
fflush(stdout);
}
exit(0); /* control never reaches here */
}
/*
* eval - Evaluate the command line that the user has just typed in
*
* If the user has requested a built-in command (quit, jobs, bg or fg)
* then execute it immediately. Otherwise, fork a child process and
* run the job in the context of the child. If the job is running in
* the foreground, wait for it to terminate and then return. Note:
* each child process must have a unique process group ID so that our
* background children don't receive SIGINT (SIGTSTP) from the kernel
* when we type ctrl-c (ctrl-z) at the keyboard.
*/
void eval(char *cmdline) {
pid_t pid;
sigset_t mask, prev_mask;
char *argv[MAXARGS];
int bg = parseline(cmdline, argv);
if (argv[0] && !builtin_cmd(argv)) {
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &prev_mask);
if ((pid = fork()) == 0) {// Child process
sigprocmask(SIG_SETMASK, &prev_mask, NULL);
setpgid(0, 0);// Set new process group
if (execve(argv[0], argv, environ) < 0) {
printf("%s: Command not found\n", argv[0]);
exit(0);
}
}
if (!bg) {// Foreground job
addjob(jobs, pid, FG, cmdline);
} else {// Background job
addjob(jobs, pid, BG, cmdline);
fprintf(stderr, "[%d] (%d) %s", pid2jid(pid), pid, cmdline);
}
sigprocmask(SIG_SETMASK, &prev_mask, NULL);
if (!bg) waitfg(pid);
}
}
/*
* parseline - Parse the command line and build the argv array.
*
* Characters enclosed in single quotes are treated as a single
* argument. Return true if the user has requested a BG job, false if
* the user has requested a FG job.
*/
int parseline(const char *cmdline, char **argv) {
static char array[MAXLINE]; /* holds local copy of command line */
char *buf = array; /* ptr that traverses command line */
char *delim; /* points to first space delimiter */
int argc; /* number of args */
int bg; /* background job? */
strcpy(buf, cmdline);
buf[strlen(buf) - 1] = ' '; /* replace trailing '\n' with space */
while (*buf && (*buf == ' ')) /* ignore leading spaces */
buf++;
/* Build the argv list */
argc = 0;
if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
} else {
delim = strchr(buf, ' ');
}
while (delim) {
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
while (*buf && (*buf == ' ')) /* ignore spaces */
buf++;
if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
} else {
delim = strchr(buf, ' ');
}
}
argv[argc] = NULL;
if (argc == 0) /* ignore blank line */
return 1;
/* should the job run in the background? */
if ((bg = (*argv[argc - 1] == '&')) != 0) {
argv[--argc] = NULL;
}
return bg;
}
/*
* builtin_cmd - If the user has typed a built-in command then execute
* it immediately.
*/
int builtin_cmd(char **argv) {
// Built-in-commands: jobs, bg, fg, quit
if (!strcmp(argv[0], "quit")) {
exit(0);
} else if (!strcmp(argv[0], "jobs")) {
listjobs(jobs);
return 1;
} else if (!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")) {
do_bgfg(argv);
return 1;
}
return 0;
}
/*
* do_bgfg - Execute the builtin bg and fg commands
*/
void do_bgfg(char **argv) {
struct job_t *job = NULL;
pid_t pid = 0;
int jid = 0;
if (argv[1] == NULL) {
printf("%s command requires PID or %%jobid argument\n", argv[0]);
return;
}
if (argv[1][0] == '%') {
if (sscanf(&argv[1][1], "%d", &jid) != 1) {
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
job = getjobjid(jobs, jid);
if (job == NULL) {
printf("%s: No such job\n", argv[1]);
return;
}
} else if (isdigit((unsigned char) argv[1][0])) {
if (sscanf(argv[1], "%d", &pid) != 1) {
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
job = getjobpid(jobs, pid);
if (job == NULL) {
printf("(%s): No such process\n", argv[1]);
return;
}
} else {
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
pid = job->pid;
if (!strcmp(argv[0], "bg")) {
job->state = BG;
if (kill(-pid, SIGCONT) < 0)
unix_error("kill (SIGCONT) error");
printf("[%d] (%d) %s", job->jid, job->pid, job->cmdline);
} else { /* fg */
job->state = FG;
if (kill(-pid, SIGCONT) < 0)
unix_error("kill (SIGCONT) error");
waitfg(pid);
}
}
/*
* waitfg - Block until process pid is no longer the foreground process
*/
void waitfg(pid_t pid) {
while (pid == fgpid(jobs)) {
sleep(1);
}
return;
}
/*****************
* Signal handlers
*****************/
/*
* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
* a child job terminates (becomes a zombie), or stops because it
* received a SIGSTOP or SIGTSTP signal. The handler reaps all
* available zombie children, but doesn't wait for any other
* currently running children to terminate.
*/
void sigchld_handler(int sig) {
int olderrno = errno;
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
if (WIFEXITED(status)) {
deletejob(jobs, pid);
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status));
deletejob(jobs, pid);
} else if (WIFSTOPPED(status)) {
struct job_t *job = getjobpid(jobs, pid);
if (job != NULL) {
job->state = ST;
fprintf(stderr, "Job [%d] (%d) stopped by signal %d\n", job->jid, pid, WSTOPSIG(status));
}
}
}
errno = olderrno;
}
/*
* sigint_handler - The kernel sends a SIGINT to the shell whenver the
* user types ctrl-c at the keyboard. Catch it and send it along
* to the foreground job.
*/
void sigint_handler(int sig) {
pid_t pid;
if ((pid = fgpid(jobs)) > 0) {
kill(-pid, SIGINT);
}
}
/*
* sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
* the user types ctrl-z at the keyboard. Catch it and suspend the
* foreground job by sending it a SIGTSTP.
*/
void sigtstp_handler(int sig) {
pid_t pid;
if ((pid = fgpid(jobs)) > 0) {
kill(-pid, SIGTSTP);
}
}
/*********************
* End signal handlers
*********************/
/***********************************************
* Helper routines that manipulate the job list
**********************************************/
/* clearjob - Clear the entries in a job struct */
void clearjob(struct job_t *job) {
job->pid = 0;
job->jid = 0;
job->state = UNDEF;
job->cmdline[0] = '\0';
}
/* initjobs - Initialize the job list */
void initjobs(struct job_t *jobs) {
int i;
for (i = 0; i < MAXJOBS; i++)
clearjob(&jobs[i]);
}
/* maxjid - Returns largest allocated job ID */
int maxjid(struct job_t *jobs) {
int i, max = 0;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].jid > max)
max = jobs[i].jid;
return max;
}
/* addjob - Add a job to the job list */
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) {
int i;
if (pid < 1)
return 0;
for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid == 0) {
jobs[i].pid = pid;
jobs[i].state = state;
jobs[i].jid = nextjid++;
if (nextjid > MAXJOBS)
nextjid = 1;
strcpy(jobs[i].cmdline, cmdline);
if (verbose) {
printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
}
return 1;
}
}
printf("Tried to create too many jobs\n");
return 0;
}
/* deletejob - Delete a job whose PID=pid from the job list */
int deletejob(struct job_t *jobs, pid_t pid) {
int i;
if (pid < 1)
return 0;
for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid == pid) {
clearjob(&jobs[i]);
nextjid = maxjid(jobs) + 1;
return 1;
}
}
return 0;
}
/* fgpid - Return PID of current foreground job, 0 if no such job */
pid_t fgpid(struct job_t *jobs) {
int i;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].state == FG)
return jobs[i].pid;
return 0;
}
/* getjobpid - Find a job (by PID) on the job list */
struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
int i;
if (pid < 1)
return NULL;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].pid == pid)
return &jobs[i];
return NULL;
}
/* getjobjid - Find a job (by JID) on the job list */
struct job_t *getjobjid(struct job_t *jobs, int jid) {
int i;
if (jid < 1)
return NULL;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].jid == jid)
return &jobs[i];
return NULL;
}
/* pid2jid - Map process ID to job ID */
int pid2jid(pid_t pid) {
int i;
if (pid < 1)
return 0;
for (i = 0; i < MAXJOBS; i++)
if (jobs[i].pid == pid) {
return jobs[i].jid;
}
return 0;
}
/* listjobs - Print the job list */
void listjobs(struct job_t *jobs) {
int i;
for (i = 0; i < MAXJOBS; i++) {
if (jobs[i].pid != 0) {
printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
switch (jobs[i].state) {
case BG:
printf("Running ");
break;
case FG:
printf("Foreground ");
break;
case ST:
printf("Stopped ");
break;
default:
printf("listjobs: Internal error: job[%d].state=%d ",
i, jobs[i].state);
}
printf("%s", jobs[i].cmdline);
}
}
}
/******************************
* end job list helper routines
******************************/
/***********************
* Other helper routines
***********************/
/*
* usage - print a help message
*/
void usage(void) {
printf("Usage: shell [-hvp]\n");
printf(" -h print this message\n");
printf(" -v print additional diagnostic information\n");
printf(" -p do not emit a command prompt\n");
exit(1);
}
/*
* unix_error - unix-style error routine
*/
void unix_error(char *msg) {
fprintf(stdout, "%s: %s\n", msg, strerror(errno));
exit(1);
}
/*
* app_error - application-style error routine
*/
void app_error(char *msg) {
fprintf(stdout, "%s\n", msg);
exit(1);
}
/*
* Signal - wrapper for the sigaction function
*/
handler_t *Signal(int signum, handler_t *handler) {
struct sigaction action, old_action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask); /* block sigs of type being handled */
action.sa_flags = SA_RESTART; /* restart syscalls if possible */
if (sigaction(signum, &action, &old_action) < 0)
unix_error("Signal error");
return (old_action.sa_handler);
}
/*
* sigquit_handler - The driver program can gracefully terminate the
* child shell by sending it a SIGQUIT signal.
*/
void sigquit_handler(int sig) {
printf("Terminating after receipt of SIGQUIT signal\n");
exit(1);
}

BIN
labs/03_shlab/tshref Executable file

Binary file not shown.

218
labs/03_shlab/tshref.out Normal file
View File

@@ -0,0 +1,218 @@
./sdriver.pl -t trace01.txt -s ./tsh -a "-p"
#
# trace01.txt - Properly terminate on EOF.
#
./sdriver.pl -t trace02.txt -s ./tsh -a "-p"
#
# trace02.txt - Process builtin quit command.
#
./sdriver.pl -t trace03.txt -s ./tsh -a "-p"
#
# trace03.txt - Run a foreground job.
#
tsh> quit
./sdriver.pl -t trace04.txt -s ./tsh -a "-p"
#
# trace04.txt - Run a background job.
#
tsh> ./myspin 1 &
[1] (26252) ./myspin 1 &
./sdriver.pl -t trace05.txt -s ./tsh -a "-p"
#
# trace05.txt - Process jobs builtin command.
#
tsh> ./myspin 2 &
[1] (26256) ./myspin 2 &
tsh> ./myspin 3 &
[2] (26258) ./myspin 3 &
tsh> jobs
[1] (26256) Running ./myspin 2 &
[2] (26258) Running ./myspin 3 &
./sdriver.pl -t trace06.txt -s ./tsh -a "-p"
#
# trace06.txt - Forward SIGINT to foreground job.
#
tsh> ./myspin 4
Job [1] (26263) terminated by signal 2
./sdriver.pl -t trace07.txt -s ./tsh -a "-p"
#
# trace07.txt - Forward SIGINT only to foreground job.
#
tsh> ./myspin 4 &
[1] (26267) ./myspin 4 &
tsh> ./myspin 5
Job [2] (26269) terminated by signal 2
tsh> jobs
[1] (26267) Running ./myspin 4 &
./sdriver.pl -t trace08.txt -s ./tsh -a "-p"
#
# trace08.txt - Forward SIGTSTP only to foreground job.
#
tsh> ./myspin 4 &
[1] (26274) ./myspin 4 &
tsh> ./myspin 5
Job [2] (26276) stopped by signal 20
tsh> jobs
[1] (26274) Running ./myspin 4 &
[2] (26276) Stopped ./myspin 5
./sdriver.pl -t trace09.txt -s ./tsh -a "-p"
#
# trace09.txt - Process bg builtin command
#
tsh> ./myspin 4 &
[1] (26281) ./myspin 4 &
tsh> ./myspin 5
Job [2] (26283) stopped by signal 20
tsh> jobs
[1] (26281) Running ./myspin 4 &
[2] (26283) Stopped ./myspin 5
tsh> bg %2
[2] (26283) ./myspin 5
tsh> jobs
[1] (26281) Running ./myspin 4 &
[2] (26283) Running ./myspin 5
./sdriver.pl -t trace10.txt -s ./tsh -a "-p"
#
# trace10.txt - Process fg builtin command.
#
tsh> ./myspin 4 &
[1] (26290) ./myspin 4 &
tsh> fg %1
Job [1] (26290) stopped by signal 20
tsh> jobs
[1] (26290) Stopped ./myspin 4 &
tsh> fg %1
tsh> jobs
./sdriver.pl -t trace11.txt -s ./tsh -a "-p"
#
# trace11.txt - Forward SIGINT to every process in foreground process group
#
tsh> ./mysplit 4
Job [1] (26298) terminated by signal 2
tsh> /bin/ps a
PID TTY STAT TIME COMMAND
25181 pts/3 S 0:00 -usr/local/bin/tcsh -i
26239 pts/3 S 0:00 make tshrefout
26240 pts/3 S 0:00 /bin/sh -c make tests > tshref.out 2>&1
26241 pts/3 S 0:00 make tests
26295 pts/3 S 0:00 perl ./sdriver.pl -t trace11.txt -s ./tsh -a -p
26296 pts/3 S 0:00 ./tsh -p
26301 pts/3 R 0:00 /bin/ps a
./sdriver.pl -t trace12.txt -s ./tsh -a "-p"
#
# trace12.txt - Forward SIGTSTP to every process in foreground process group
#
tsh> ./mysplit 4
Job [1] (26305) stopped by signal 20
tsh> jobs
[1] (26305) Stopped ./mysplit 4
tsh> /bin/ps a
PID TTY STAT TIME COMMAND
25181 pts/3 S 0:00 -usr/local/bin/tcsh -i
26239 pts/3 S 0:00 make tshrefout
26240 pts/3 S 0:00 /bin/sh -c make tests > tshref.out 2>&1
26241 pts/3 S 0:00 make tests
26302 pts/3 S 0:00 perl ./sdriver.pl -t trace12.txt -s ./tsh -a -p
26303 pts/3 S 0:00 ./tsh -p
26305 pts/3 T 0:00 ./mysplit 4
26306 pts/3 T 0:00 ./mysplit 4
26309 pts/3 R 0:00 /bin/ps a
./sdriver.pl -t trace13.txt -s ./tsh -a "-p"
#
# trace13.txt - Restart every stopped process in process group
#
tsh> ./mysplit 4
Job [1] (26313) stopped by signal 20
tsh> jobs
[1] (26313) Stopped ./mysplit 4
tsh> /bin/ps a
PID TTY STAT TIME COMMAND
25181 pts/3 S 0:00 -usr/local/bin/tcsh -i
26239 pts/3 S 0:00 make tshrefout
26240 pts/3 S 0:00 /bin/sh -c make tests > tshref.out 2>&1
26241 pts/3 S 0:00 make tests
26310 pts/3 S 0:00 perl ./sdriver.pl -t trace13.txt -s ./tsh -a -p
26311 pts/3 S 0:00 ./tsh -p
26313 pts/3 T 0:00 ./mysplit 4
26314 pts/3 T 0:00 ./mysplit 4
26317 pts/3 R 0:00 /bin/ps a
tsh> fg %1
tsh> /bin/ps a
PID TTY STAT TIME COMMAND
25181 pts/3 S 0:00 -usr/local/bin/tcsh -i
26239 pts/3 S 0:00 make tshrefout
26240 pts/3 S 0:00 /bin/sh -c make tests > tshref.out 2>&1
26241 pts/3 S 0:00 make tests
26310 pts/3 S 0:00 perl ./sdriver.pl -t trace13.txt -s ./tsh -a -p
26311 pts/3 S 0:00 ./tsh -p
26320 pts/3 R 0:00 /bin/ps a
./sdriver.pl -t trace14.txt -s ./tsh -a "-p"
#
# trace14.txt - Simple error handling
#
tsh> ./bogus
./bogus: Command not found
tsh> ./myspin 4 &
[1] (26326) ./myspin 4 &
tsh> fg
fg command requires PID or %jobid argument
tsh> bg
bg command requires PID or %jobid argument
tsh> fg a
fg: argument must be a PID or %jobid
tsh> bg a
bg: argument must be a PID or %jobid
tsh> fg 9999999
(9999999): No such process
tsh> bg 9999999
(9999999): No such process
tsh> fg %2
%2: No such job
tsh> fg %1
Job [1] (26326) stopped by signal 20
tsh> bg %2
%2: No such job
tsh> bg %1
[1] (26326) ./myspin 4 &
tsh> jobs
[1] (26326) Running ./myspin 4 &
./sdriver.pl -t trace15.txt -s ./tsh -a "-p"
#
# trace15.txt - Putting it all together
#
tsh> ./bogus
./bogus: Command not found
tsh> ./myspin 10
Job [1] (26343) terminated by signal 2
tsh> ./myspin 3 &
[1] (26345) ./myspin 3 &
tsh> ./myspin 4 &
[2] (26347) ./myspin 4 &
tsh> jobs
[1] (26345) Running ./myspin 3 &
[2] (26347) Running ./myspin 4 &
tsh> fg %1
Job [1] (26345) stopped by signal 20
tsh> jobs
[1] (26345) Stopped ./myspin 3 &
[2] (26347) Running ./myspin 4 &
tsh> bg %3
%3: No such job
tsh> bg %1
[1] (26345) ./myspin 3 &
tsh> jobs
[1] (26345) Running ./myspin 3 &
[2] (26347) Running ./myspin 4 &
tsh> fg %1
tsh> quit
./sdriver.pl -t trace16.txt -s ./tsh -a "-p"
#
# trace16.txt - Tests whether the shell can handle SIGTSTP and SIGINT
# signals that come from other processes instead of the terminal.
#
tsh> ./mystop 2
Job [1] (26359) stopped by signal 20
tsh> jobs
[1] (26359) Stopped ./mystop 2
tsh> ./myint 2
Job [2] (26362) terminated by signal 2

View File

@@ -1,9 +1,55 @@
# Bits, Bytes, and Integers
In computers, everything consists of bits.
By encoding sets of bits in various ways, they made meanings:
* instructions
* and data(numbers, sets, strings, etc..)
## Boolean Algebra
* and `A & B`
* or `A | B`
* not `~A`
* xor `A ^ B`
### in C
* Shift (`<<`, `>>`)
* Left Shift(`<<`)
Zero fill on right
* Right Shift(`>>`)
Logical Shift: zero fill with 0's on left
Arithmetic shift Relicate most significant bit on left
```c {cmd="gcc" args=[-x c $input_file -O0 -m32 -o 1_1.out]}
#include <stdio.h>
int main() {
int a = 0x7fffffff;
int as = a << 1;
printf("shl of %d: %d(%08x)\n", a, as, as);
unsigned b = 0x7fffffff;
unsigned bs = b << 1;
printf("shl of %u: %u(%08x)\n", b, bs, bs);
}
```
```sh {cmd hide}
while ! [ -f 1_1.out ]; do sleep .1; done; ./1_1.out
```
## Integers
### Representation
### Representation & Encoding
* for $w$ bits data $x$
$$B2U(X)=\sum_{i=0}^{w-1} x_i 2^{i}\quad B2T(X)=-x_{w-1}*2^{w-1} + \sum_{i=0}^{w-2}{x_i 2^i}$$
### Conversion

218
notes/10.md Normal file
View File

@@ -0,0 +1,218 @@
# Signals and Nonlocal Jumps
## Shell
Linux Process Hierachy: all is rooted from `init` or `systemd` (pid = 1).
A shell is an application that runs programs on behalf of the user.
* `sh` Original Unix shell
* `csh` BSD Unix C Shell
* `bash` GNU Bourne-Again Shell (default Linux shell)
## Signals
All signal is a small message that notifies a process that an event has occured in the system.
Signal is sent by kernel.
Signal is identified by small integer ID (1-30).
Only information in a signal is **its ID** and **the fact that it arrived**.
| ID | name | Default Action | Corresponding Event |
| --- | ------- | -------------- | ----------------------------------- |
| 2 | SIGINT | terminate | user types `Ctrl+C` |
| 9 | SIGKILL | terminate | kill program |
| 11 | SIGSEGV | terminate | segmentation violation |
| 14 | SIGALRM | terminate | timer expired |
| 17 | SIGCHLD | ignore | child process stopped or terminated |
Sending a signal is essentially **updating some state** in the context of the destination process.
Kernel sends a signal for one of the following reasons:
A destination process receives a signal when it is forced by the kernel and reacts some way to the signal.
* Ignore
* Terminate (with optional core-dump)
* Catch
* executing a user-level handler function called **signal handler**.
### Pending & Blocking
A signal is **pending** if sent but not yet received. There can be at most one pending signal of any particular type because signals are **not queued**. Signal is managed by bitmask. So subsequent signals of same type is discarded.
* sets `k` bit when delivered
* clears `k` bit when received
Blocking signals: a process can block the receipt of certain signals. It is not received until unblocked. It is also managed by bitmask.
* can be set and cleared by using `sigprocmask`
### Receiving
Suppose kernel is returning from an exception handler and is ready to pass control to process `p`
Kernel computes `pnb = pending(p) & ~blocked(p)`.
* if `pnb == 0`, no unblocked pending signals for `p`, so just return to `p` normally.
* otherwise, choose least nonzero bit `k` in `pnb` and force process `p` to receive signal `k`
* The receipt of the signal triggers action by `p`
* Repeat for all nonzero `k`
### Default Action & Signal Handler
Each signal type has a predefined default action:
* terminates
* stops until restarted by a SIGCONT signal
* ignores
```c
handler_t * signal(int signum, handler_t *handler);
```
* `SIG_IGN` ignore
* `SIG_DFL` revert to default action
* handler function pointer
### Blocking and Unblocking
* Implicit blocking
Kernel blocks any pending signals of type currently being handled.
* Explicit blocking `sigprocmask()`
Supporting functions:
* `sigemptyset()` create empty set
* `sigfillset()` add every signal number to set
* `sigaddset()` add signal number to set
* `sigdelset()` delete signal number from set
```c
sigset_t mask, prev_mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigprocmaksk(SIG_BLOCK, &mask, &prev_mask);
// critical section
sigprocmask(SIG_SETMASK, &prev_mask, NULL);
```
### Safe Signal Handling
Handlers are concurrent with main program and share global data structures. This can lead to troubles.
Some guidlines help to avoid troubles:
0. Keep you handlers as simple as possible
1. Call only **async-signal-safe** functions in your handlers
2. Save and restore `errno` on entry and exit
3. Protect accesses to shared data structure by temporarily blocking all signals
4. Declare global variables as `volatile`
5. Declare global flags as `volatile sig_atomic_t`
#### Async-Signal-Safety
A function satisfying either two conditions(below) is **Async-Signal-Safety function**:
* **reentrant**: not modifying global data (or static data), only use local data(stack frame).
* **non-interruptible**: blocking singnals during modifying global data.
POSIX guarantees that the 117 functions to be async-signal-safe, including:
* `_exit`, `write`, `wait`, `waitpid`, `sleep`, `kill`
**IMPORTANT**: `printf`, `malloc`, `sprintf`, `eixt` are **NOT** async-signal-safe.
### Correct Signal Handling Example for reaping multiple childs
```c
void child_handler(int sig) {
int olderrno = errno; pid_t pid;
while ((pid = wait(NULL)) > 0) {
ccount --;
sio_puts("Handler reaped child ");
sio_putl((long) pid);
sio_puts(" \n");
}
if (errno != ECHILD) {
sio_error("wait error");
}
errno = olderrno;
}
```
### Portable Signal Handling
Different UNIX systems have different signal handling sematnics.
So `sigaction` is introduced to provide a portable interface for signal handling.
```c
handler_t * Signal(int signum, handler_t *handler) {
struct sigaction action, old_action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask); // block sigs of type being handled
action.sa_flags = SA_RESTART; // restart syscalls if possible
if (sigaction(signum, &action, &old_action) < 0)
unix_error("Signal error");
return (old_action.sa_handler);
}
```
### Concurrent Flows to lead Races
```c
void handler(int sig) {
int olderrno = errno;
pid_t pid;
while ((pid = waitpid(-1, NULL, 0)) > 0) {
deletejob(pid);
}
if (errno != ECHILD) sio_error("waitpid error");
errno = olderrno;
}
int main() {
while (1) {
if ((pid = Fork()) == 0) {
execve("/bin/date", argv, NULL);
}
}
addjob(pid);
}
```
In above example, the ecf flow can be executed before the deletejob; thus it cannot be deleted.
Therefore, we need to synchronize the two concurrent flows.
```c
void handler(int sig) {
int olderrno = errno;
sigset_t mask_all, prev_all;
pid_t pid;
sigfillset(&mask_all);
while ((pid = waitpid(-1, NULL, 0)) > 0) {
sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
deletejob(pid);
sigprocmask(SIG_SETMASK, &prev_all, NULL);
}
if (errno != ECHILD) sio_error("waitpid error");
errno = olderrno;
}
int main() {
pid_t pid;
sigset_t mask_all, prev_one;
sigfillset(&mask_all);
signal(SIGCHLD, handler);
while (1) {
sigprocmask(SIG_BLOCK, &mask_all, &prev_one); // Block SIGCHLD
if ((pid = Fork()) == 0) {
sigprocmask(SIG_SETMASK, &prev_one, NULL); // Unblock SIGCHLD
execve("/bin/date", argv, NULL);
}
sigprocmask(SIG_BLOCK, &mask_one, NULL);
addjob(pid);
sigprocmask(SIG_SETMASK, &prev_one, NULL);
}
}
```

223
notes/11.md Normal file
View File

@@ -0,0 +1,223 @@
# System-Level I/O
IO is the process of coping data between the main memory and external devices.
In a Linux, **file** is a sequence of $m$ bytes.
All I/O devices are represented as files. Even the kernel is represented as a file.
## Unix IO
* `open` and `close`
* `read` and `write`
* `lseek` changing **current file position**
### File Types
* Regular files
* Directory
* Socket
* ...
#### Regular Files
A regular file contains arbitary data.
For example **text file** is a sequence of text lines. EOL is different in different OS: (`\n` in Unix, `\r\n` in Windows & Internet).
#### Directories
Directory contains an array of links. Least two links are `.`(itself) and `..`(parent dir).
* `ls`
* `mkdir`
* `rmdir`
All files are orgnaized as a hierarchy anchored by root dir named `/`.
Kernel maintains curr working dir (cwd) for each process that modified using the `cd` command.
Path names
* Absolute `/home/yenru0/workspace`
* Relative `../workspace`
### Open & Close & Read & Write
```c
int fd;
if ((fd = open("file.txt", O_RDONLY)) < 0) {
perror("open");
exit(1);
}
```
* `open` returns a non-negative integer called **file descriptor** (fd).
* `fd == -1` indicates an error.
* `0`: stdin, `1`: stdout, `2`: stderr
```c
int fd; int ret;
if ((ret = close(fd)) < 0) {
perror("close");
exit(1);
}
```
Closing an already closed can lead to a disastrous situation in threaded programs. So always check the return code.
```c
char buf[512];
nbytes = read(fd, buf, sizeof(buf));
```
```c
ssize_t read(int fd, void *usrbuf, size_t n);
```
read returns the number of bytes read from the `fd` into `buf`.
`ssize_t` is signed version of `size_t`.
If `read` returns negative value, an error occurred.
```c
ssize_t write(int fd, const void *usrbuf, size_t n);
```
If `write` returns negative value, an error occurred.
### Short Counts
It means that `read` or `write` transfers fewer bytes than requested. It can occur in these situations:
* `EOF` on reads
* Reading text lines from an terminal
* Reading from a network socket
Never occurs:
* Reading from disk files (except for `EOF`)
* Writing to disk files
## RIO pakcage
RIO is a set of wrappers efficient and robust I/O functions subject to **short couunts**.
* unbuffered RIO functions `rio_readn`, `rio_writen`
* buffered RIO functions `rio_readnb`, `rio_readlineb`
* buffered RIO functions are thread-safe and can be interleaved arbitrarily on the same descriptor.
### Buffered RIO
To read efficiently from a file, RIO uses partially cached in an interal memory buffer. (`rio_t` structure)
For reading from file, Buffer has buffered portion of already read and unread data. It is refilled automatically by `rio_readnb` and `rio_readlineb` as needed. This is **partially cached**.
```c
typedef struct {
int rio_fd; // Descriptor for this internal buf
int rio_cnt; // Unread bytes in internal buf
char *rio_bufptr; // Next unread byte in internal buf
char rio_buf[RIO_BUFSIZE]; // Internal buffer
} rio_t;
```
example:
```c
int main(int argc, char **argv) {
int n; rio_t rio; char buf[MAXLINE];
rio_readinitb(&rio, STDIN_FILENO);
while ((n = rio_readlineb(&rio, buf, MAXLINE)) != 0) {
rio_writen(STDOUT_FILENO, buf, n);
}
exit(0);
}
```
## Metadata
Metadata is data about data. (file access, file size, file type)
* Per-process metadata
* when a process opens a file, the kernel creates an entry in a per-process table called the **file descriptor table**
* Per-file metadata
* can be accessed using `stat` system call
```c
struct stat {
dev_t st_dev; // ID of device containing file
ino_t st_ino; // inode number
mode_t st_mode; // protection
nlink_t st_nlink; // number of hard links
uid_t st_uid; // user ID of owner
gid_t st_gid; // group ID of owner
dev_t st_rdev; // device ID (if special file)
off_t st_size; // total size, in bytes
blksize_t st_blksize; // blocksize for filesystem I/O
blkcnt_t st_blocks; // number of 512B blocks allocated
time_t st_atime; // time of last access
time_t st_mtime; // time of last modification
time_t st_ctime; // time of last status change
};
```
### How to Kernel represents Open Files
* Descriptor table(per-process)
* Open file table(shared by all processes)
* v-node table(shared by all processes)
When a process opens a file, the kernel creates an entry in the per-process file descriptor table. Each entry contains a pointer to an entry in the open file table. Each entry in the open file table contains a pointer to an entry in the v-node table.
When a `fork` calls: the child process inherits copies of the parent's file descriptors. And the entry points to open file table's entry increasing `refcnt`.
### IO redirection
for example: `ls > foo.txt`
Answer: `dup2(oldfd, newfd)` it means copies descriptor table entry `oldfd` to `newfd`
so `dup2(4, 1)` makes `stdout` point to the same open file as descriptor 4.
## stdio
The C standard library (`libc.so`) provides a collection of higher-level standard I/O functions.
* `fopen`, `fclose`, `fread`, `fwrite`, `fgets`, `fputs`, `fscanf`, `fprintf`
`stdio` models open files as **streams**, which are abstraction for a file descriptor and a buffer in memory.
```c
extern FILE * stdin;
extern FILE * stdout;
extern FILE * stderr;
```
### Buffered I/O
Application often read and write one char at a time. However, UNIX System calls `read` and `write` calls expensive. So we need buffered read & write; use unix `read` & `write` to **get a block of data into a buffer**. And then user application reads/writes **one char at a time from/to the buffer**; it is efficient because it is simple memory access.
`stdio` uses buffer. `printf` is not write immediately to the `stdout` file; it is stored in a buffer. And then when `fflush(stdout)`, `exit`, or return from `main`, the buffer is flushed to the file using `write` syscall.
## Remark
* UNIX IO
* RIO package
* stdio
When to use
* stdio: disk or terminal files
* unix io: signal handlers, or when you need absolute high performance
* RIO: networking
### Binary
DO NOT USE:
* text oriented I/O: `fgets`, `scanf`, `rio_readlineb`
* string functions: `strlen`, `strcpy`, `strcat`, `strcmp`

14
notes/12.md Normal file
View File

@@ -0,0 +1,14 @@
# Memory
## Memory Hierachies
Storage media typically trades off speed with capacity
## Locality
* **Temporal Locality**: recently referenced items are likely to be referenced again soon
* **Spaitial Locality**: items with nearby addresses are tend to be referenced close together in time
## Cache Concepts

207
notes/2.md Normal file
View File

@@ -0,0 +1,207 @@
# Floating Point
## Fractional Binary Number
representation:
* for $w = i + j + 1$ bits data $b$
$$\sum_{k = -j}^{i}b_k\times 2^k$$
for example:
* $5+3/4 = 23/4 = 101.11_2$
* $1 7/16 = 23/16 = 1.0111_2$
**Limitations**
* Can only exactly represent numbers of the form of $x/2^k$
* Just one setting of binary point within the $w$ bits, which means that very small value or very large value cannot be represented
## IEEE Floating Point Definition
**IEEE Standard 754**
Driven by numerical concerns:
* Nice standards for rounding, overflow, underflow
* But Hard to make fast in hardware
* Numberical Analysts predominated over hw designers in defining standard
### Representation
**Form**
$$(-1)^s M 2^E$$
* $s$: sign bit
* $M$: mantissa fractional value in $[1.0,2.0)$
* $E$: exponent
**Encoding**
```mermaid
---
title: "Single Precision"
config:
packet:
bitsPerRow: 32
rowHeight: 32
---
packet
+1: "s"
+8: "exp"
+23: "frac"
```
```mermaid
---
title: "Double Precision"
config:
packet:
bitsPerRow: 32
rowHeight: 32
---
packet
+1: "s"
+11: "exp"
+52: "frac"
```
There is three kinds of `float`: **normalized**, **denormalized**, **special**
**normalized**
$E = \exp - B$
$B = 2^{k-1}-1$ where $k$ is number of exp bits
* single: 127
* double: 1023
$M = 1.xxxxx$
minumum when $\text {frac} = 0000...\quad (M = 1.0)$
maximum when $\text{frac }= 1111... \quad (M = 2.0 - \epsilon)$
**denormalized**
when `exp=000...0`
$\exp = 1 - Bias$
$M = 0.xxxxx$
**special**
when `exp = 111...1`
* case `exp = 111...1, frac = 000...0`
* repr $\infty$
* operation that overflows
* case `exp = 111...1, frac = 111...1`
* repr `NaN`
* repr case when no numeric value can be determined
* e.g., `sqrt(-1)`, `inf - inf`, `inf * 0`
```c {cmd="gcc-14" args=[-x c $input_file --std=c23 -O0 -m32 -o 2_1.out]}
#include <stdio.h>
int main() {
unsigned x_a = 0b0'11111111'00000000000000000000000;
unsigned x_b = 0b0'11111111'00000000000000000000001;
unsigned x_c = 0b0'01111111'00000000000000000000000;
float a = *(float*)&x_a;
float b = *(float*)&x_b;
float c = *(float*)&x_c;
double cx = c;
printf("%08x: %f\n", x_a, a);
printf("%08x: %f\n", x_b, b);
printf("%08x: %f\n", x_c, c);
printf("%016llx: %f\n", *(unsigned long long *)&cx, cx);
return 0;
}
```
```sh {cmd hide}
while ! [ -f 2_1.out ]; do sleep .1; done; ./2_1.out
```
### Properties
* FP0 is Same as Int0
* Can (almost) use unsigned int comparison
### Arithmetic
$x + y = \text{Round}(x+y)$
$x \times y = \text{Round}(x\times y)$
Idea:
1. compute exact result
2. Make it fit into desired precision
* overflow if too large
* **round** to fit into frac
#### Rounding
* Twowards zero
* Round down
* Round up
* **Nearest Even***(default)
**Nearest Even** is default rounding mode
Any other kind rounding mode is hard to get without dropping into assembly, but C99 has support for rounding mode management.
This rounding mode is used because **reduced statistically bias**.
For binary fractional numbers:
* "Even" when least significant bit is $0$
* "Half way" when bits to right of rounding position is $100..._2$
so for example of rounding to neareast $1/4$:
| Binary Value | Rounded | Action |
| ------------ | ------- | ---------- |
| `10.00011` | `10.00` | (<1/2)Down |
| `10.00110` | `10.01` | (>1/2)Up |
| `10.11100` | `11.00` | (=1/2)Up |
| `10.10100` | `10.10` | (=1/2)Down |
`BBGRXXX`
* `G`: **G**uard bit: LSB of result
* `R`: **R**ound bit: first bit of removed
* `X`: **S**ticky bits: OR of remaining bits(001 = 1, 000 = 0)
Round up conditions
1. R = 1, S = 1 -> `>.5`
2. G = 1, R = 1, S = 0 -> Round to even
```c {cmd="gcc-14" args=[-x c $input_file --std=c23 -O0 -m32 -o 2_2.out]}
#include <stdio.h>
int main() {
unsigned long long
tb = 0b0'10000010000'0000000000000000000001010000000000000000000000000000;
unsigned xb = 0b0'10000001'01000000000000000000011;
double t = *(double*)&tb;
float x = t;
for(int i=31; i>=0;i--) {
if(i == 31 - 1) {
printf("/");
} else if (i == 31 - 1 - 8){
printf("/");
}
printf("%d", !!((*(unsigned *)&x) & (1<<i)));
}
printf("\n");
printf("%f", x);
}
```
```sh {cmd hide}
while ! [ -f 2_2.out ]; do sleep .1; done; ./2_2.out
```
## Float Quiz

793
notes/3.md Normal file
View File

@@ -0,0 +1,793 @@
# Machine Level Programming
## History of Intel Processors
* Eveolutionary design: **Backwards compatible** up until `8086` in 1978
* **C**omplex **I**nstruction **S**et **C**omputer (CISC)
**RISC vs CISC**
* CISC has variable length instructions
* RISC has constant length instructions
1. Intel x86(8086)
1. IA32 to IA64
2. (after x86-64) EM64T(almost same as AMD x86-64)
2. AMD x86-64
## C, Assembly, machine code
* ***Architecture***: The parts of a processor design that one needs to understand or write assembly/machine code
* **ISA(Instruction Set Architecture)**
* e.g., x86, IA32, Itanium, x86-64, ARM
* ***Microarchitecture***: Implementation of the architecture
form of code:
* Machine Code: the byte-level programs that a processor executes
* Assembly Code: A text representation of machine code
### Assembly/Machine Code View
Programmer-Visible State (shown by ISAs)
* PC(Program Counter)
* Address of next instruction
* RIP in (x86-64)
* Register file
* Heavily used program data
* Condition codes
* store status information about most recent arithmetic or logical op
* Used for conditional branching
* Memory(external)
Compiling Into Assembly
```c {cmd=gcc, args=[-Og -x c -c $input_file -o 3_1.o]}
long plus(long x, long y);
void sumstore(long x, long y, long *dest) {
long t = plus(x, y);
*dest = t;
}
```
```sh {cmd hide}
while ! [ -r 3_1.o ]; do sleep .1; done; objdump -d 3_1.o
```
### Integer Registers
* In x86-64
`ax` `bx` `cx` `dx` `si` `di` `sp` `bp` (in 8bytes `r` 4bytes `e`)
`r8` `r9` `r10` `r11` `r12` `r13` `r14` `r15` (in 4bytes: add `d`)
* In IA32
`eax`(32bit): 16bit `ax`(`ah`, `al`); origin: accumulate
`ecx`(32bit): 16bit `cx`(`ch`, `cl`); origin: counter
`edx`(32bit): 16bit `bx`(`bh`, `bl`); origin: data
`ebx`(32bit): 16bit `dx`(`dh`, `dl`); origin: base
`esi`(32bit): 16bit `si`(`sih`, `sil`); origin: source index
`edi`(32bit): 16bit `di`(`dil`, `dil`); origin: destination index
`esp`(32bit): 16bit `sp`(`spl`, `spl`); origin: stack pointer
`ebp`(32bit): 16bit `bp`(`bpl`, `bpl`); origin: base pointer
#### understanding `movq`
In x86asm, There are three operand types: **immediate, register, memory**
* immediate: constant integer data like `$0x400` `$-533`
* register: one of 16 integer regs
* e.g., `%rax`, `%r13`
* but `%rsp` is reserved for special use
* memory: 8 bytes of memory at address given by register
* e.g., `(%rax)`
**movq usage**
`movq $src, $dest`
* limit: Cannot do memory-memory transfer with a single instruction(because memory is external device to cpu)
**memory addressing modes**
`(R)` means `Mem[Reg[R]]`
`D(R)` means `Mem[Reg[R]+D]`, constant displacement `D` specifies offset
`movq 8(%rbp), %rdx`
<table>
<tr>
<td>
```c
int swap (long *xp, long *yp) {
long t0 = *xp;
long t1 = *yp;
*xp = t1;
*yp = t0;
}
```
</td>
<td>
```nasm
swap:
movq (%rdi), %rax
movq (%rsi), %rdx
movq %rdx, (%rdi)
movq %rax, (%rsi)
ret
```
</td>
</tr>
</table>
Complete form of memory addressing modes:
`D(Rb, Ri, S)` means `Mem[Reg[Rb] + S*Reg[Ri] + D]`
* `D`: Constant "displacement"
* `Rb`: Base Register
* `Ri`: Index Register
* `S`: Scale Factor(1, 2, 4, or 8)
for example:
| `%rdx` | `%rcx` |
| -------- | -------- |
| `0xf000` | `0x0100` |
* `0x8(%rdx)` = `0xf008`
* `(%rdx, %rcx)` = `0xf100`
* `(%rdx, %rcx, 4)` = `0xf400`
* `0x80(,%rdx, 2)` = `0x1e080`
#### Arithmetic & Logical Operations
* `leaq $src, $dst`
* computing address without memory reference like `p = &x[i]`
* computing arithmetic expression `x + k * y`
* `addq $src, $dst`
* `subq $src, $dst`
* `imulq $src, $dst`
* `salq $src, $dst`
* `sarq $src, $dst`
* `shrq $src, $dst`
* `xorq $src, $dst`
* `andq $src, $dst`
* `orq $src, $dst`
all the above operator operates like `dest = dest # src`
* `incq $dest`
* `decq $dest`
* `negq $dest`
* `notq $dest`
## Control
**Processor State(x86-64, Partial)**
* Temporary data(`%rax`, ...)
* Location of runtime stack(`%rsp`)
* Location of current code control point(`%rip`, instruction point)
* Status of recent tests(`CF`, `ZF`, `SF`, `OF`)
### Condition Codes
* Single bit registers
* `CF` Carry flag (for unsigned)
* `SF` Sign flag (for signed)
* `ZF` Zero flag
* `OF` Overflow flag (for signed)
**Conditional Codes(Implicit Setting)**
Implicit setting is codes are set by arithmetic operations(`addq`, `subq`, `mulq`)
for example: `addq`: `t = a + b`
* `CF` set if carry out from most significant bit or unsigned overflow
* `ZF` set if `t == 0`
* `SF` set if `t < 0` (as signed)
* `OF` set if two's-complement overflow or signed overflow
`(a > 0 && b > 0 && (a + b) < 0) || (a < 0 && b < 0 && (a + b) >= 0)`
The codes are not implictly set by `leaq`, because it is not designed to be used as arithmetic but used as **address calculation**. so it cannot affect to conditional codes.
**Conditional Codes(Explicit Setting)**
The codes are set explictly by compare instruction.
`cmpq b, a` is computing `a - b` without setting destination.
* `CF` set if carry out from most significant bit or unsigned overflow
* `ZF` set if `a == b` or `a - b == 0`
* `SF` set if `(a - b) < 0` (as signed)
* `OF` set if two's-complement overflow or signed overflow
`(a > 0 && b > 0 && (a - b) < 0) || (a < 0 && b < 0 && (a - b) >= 0)`
And explictly set by test instruction
`testq b, a` is computing `a & b` without setting destination.
Sets condition codes based on value of `a & b` it is useful to have one of the operands be a mask.
* `ZF` set when `a & b == 0`
* `SF` set when `a & b < 0`
**Reading Condition Codes**
`setX`: set single byte based on combination of condition codes
| setX | effect | desc |
| ------- | ---------------- | ------------------------- |
| `sete` | `ZF` | Equal / Zero |
| `setne` | `~ZF` | Not Equal / Not Zero |
| `sets` | `SF` | Negative |
| `setns` | `~SF` | Nonnegative |
| `setg` | `~(SF^OF) & ~ZF` | Greater (signed) |
| `setge` | `~(SF^OF)` | Greater or Equal (signed) |
| `setl` | `SF^OF` | Less (signed) |
| `setle` | `(SF^OF) \| ZF` | Less or Equal (signed) |
| `seta` | `~CF & ~ZF` | Above (unsigned) |
| `setb` | `CF` | Below (unsigned) |
it deos not alter remaining bytes of registers. only use 1 byte register(`%al`, `%bl`)
```nasm
cmpq %rsi(y), %rdi(x) # compare x and y
setg %al # set when >(greater)
movzbl %al, %eax # move zero extend byte to long
ret
```
### Conditional Branches
#### Jumping
`jX` jump to different part of code depending on condition codes.
| jX | condition | desc |
| ----- | ---------------- | ------------------------- |
| `jmp` | 1 | Unconditional |
| `je` | `ZF` | Equal / Zero |
| `jne` | `~ZF` | Not Equal / Not Zero |
| `js` | `SF` | Negative |
| `jns` | `~SF` | Nonnegative |
| `jg` | `~(SF^OF) & ~ZF` | Greater (signed) |
| `jge` | `~(SF^OF)` | Greater or Equal (signed) |
| `jl` | `SF^OF` | Less (signed) |
| `jle` | `(SF^OF) \| ZF` | Less or Equal (signed) |
| `ja` | `~CF & ~ZF` | Above (unsigned) |
| `jb` | `CF` | Below (unsigned) |
Old Style Conditional Branch
```c {cmd=gcc args=[-Og -x c -fno-if-conversion -c $input_file -o 3_3.o]}
long absdiff(long x, long y) {
long result;
if (x > y) result = x - y;
else result = y - x;
return result;
}
```
```sh { cmd hide }
while ! [ -r 3_3.o ]; do sleep .1; done; objdump -d 3_3.o -Msuffix
```
**expressing with `goto`**
```c {cmd=gcc args=[-Og -x c -rno-if-conversion -c $input_file -o 3_4.o]}
long absdiff_j(long x, long y) {
long result;
int ntest = x <= y;
if (ntest) goto Else;
result = x-y;
goto Done;
Else:
result = y-x;
Done:
return result;
}
```
#### Conditional Move
But this branchings are very disruptive to instruction flow through pipelines, **Conditional Moves** are highly used because they do not require control transfer.
```c {cmd=gcc args=[-O3 -x c -c $input_file -o 3_5.o]}
long absdiff(long x, long y) {
long result;
if (x > y) result = x - y;
else result = y - x;
return result;
}
```
```sh {cmd hide}
while ! [ -r 3_5.o ]; do sleep .1; done; objdump -d 3_5.o -Msuffix
```
However, there are several *bad cases* for conditional move.
* expansive computations
```c
val = Test(x) ? Hard1(x) : Hard2(x);
```
because both values are get computed. only simple computations are effective for conditional moves.
* risky computations
```c
val = p ? *p : 0;
```
both values get computed may have undesiarable effects.
* Computations with side effects
```c
val = x > 0 ? x*=7 : x+=3;
```
each expression has side-effect.
### Loop
#### do-while
<table>
<tr>
<td>
```c
long pcount_do(unsigned long x) {
long result = 0;
do {
result += x & 0x1;
x >>= 1;
} while (x);
return result;
}
```
</td>
<td>
```c {cmd=gcc args=[-Og -x c -c $input_file -o 3_6.o]}
long pcount_goto(unsigned long x) {
long result = 0;
loop:
result += x & 0x1;
x >>= 1;
if (x) goto loop;
return result;
}
```
</td>
</tr>
</table>
```sh {cmd hide}
while ! [ -r 3_6.o ]; do sleep .1; done; objdump -d 3_6.o -Msuffix
```
**general do-while translation**
<table>
<tr>
<td>
```c
do {
Body
} while (Test);
```
</td>
<td>
```c
loop:
Body
if (Test) goto loop;
```
</td>
</tr>
</table>
#### while
**general while translation#1**
it is called **jump-to-middle translation**, used with `-O0` (or `-Og`) flag.
<table>
<tr>
<td>
```c
while(Test) {
Body
}
```
</td>
<td>
```c
goto test;
loop:
Body
test:
if (Test)
goto loop;
done:
```
</td>
</tr>
</table>
```c {cmd=gcc args=[-Og -x c -c $input_file -o 3_7.o]}
long pcount_while(unsigned long x) {
long result = 0;
while (x) {
result += x & 0x1;
x >>= 1;
}
return result;
}
```
```sh {cmd hide}
echo "jmp-to-middle translation"
while ! [ -r 3_7.o ]; do sleep .1; done; objdump -d 3_7.o -Msuffix
```
**general while translation#2**
while to do-while conversion, used with `-O1` flag.
<table>
<tr>
<td>
```c
while(Test) {
Body
}
```
</td>
<td>
```c
if (!Test) goto done;
do {
Body
} while (Test);
done:
```
</td>
<td>
```c
if (!Test) goto done;
loop:
Body
if (Test) goto loop;
done:
```
</td>
</tr>
</table>
```c {cmd=gcc args=[-O1 -x c -c $input_file -o 3_8.o]}
long pcount_while(unsigned long x) {
long result = 0;
while (x) {
result += x & 0x1;
x >>= 1;
}
return result;
}
```
```sh {cmd hide}
echo "while to do-while conversion"
while ! [ -r 3_8.o ]; do sleep .1; done; objdump -d 3_8.o -Msuffix
```
#### for loop form
<table>
<tr>
<td>
```c
for (init; test; update) {
Body
}
```
</td>
</tr>
</table>
**for-to-while conversion**
<table>
<tr>
<td>
```c
for (Init; Test; Update) {
Body
}
```
</td>
<td>
```c
Init;
while(Test) {
Body
Update;
}
```
</td>
</tr>
<tr>
<td>
```c {cmd=gcc args=[-O3 -x c -c $input_file -o 3_9.o]}
#include <stddef.h>
#define WSIZE 8 * sizeof(int)
long pcount_for(unsigned long x) {
size_t i;
long result = 0;
for (i = 0; i < WSIZE; i++) {
unsigned bit = (x >> i) & 0x1;
result += bit;
}
return result;
}
```
</td> <td>
```c {cmd=gcc args=[-O3 -x c -c $input_file -o 3_10.o]}
#include <stddef.h>
#define WSIZE 8 * sizeof(int)
long pcount_for(unsigned long x) {
size_t i;
long result = 0;
i = 0;
while(i < WSIZE) {
unsigned bit = (x >> i) & 0x1;
result += bit;
i++;
}
return result;
}
```
</td>
</tr>
<tr>
<td>
```sh {cmd hide}
while ! [ -r 3_9.o ]; do sleep .1; done; objdump -d 3_9.o -Msuffix
```
</td>
<td>
```sh {cmd hide}
while ! [ -r 3_10.o ]; do sleep .1; done; objdump -d 3_10.o -Msuffix
```
</td>
</tr>
</table>
for to do-while conversion, initial test can be optimized away.
### Switch
#### Jump Table Structure
Switch form
<table>
<tr>
<td>
```c {cmd=gcc args=[-Og -fno-asynchronous-unwind-tables -fno-stack-protector -x c -S $input_file -o 3_11.s]}
long switch_eg (long x, long y, long z) {
long w = 1;
switch(x) {
case 1:
w = y*z;
break;
case 2:
w = y/z;
/* Fall Through */
case 3:
w += z;
break;
case 5:
case 6:
w -= z;
break;
case 7:
w *= z;
break;
default:
w = 2;
}
return w;
}
```
</td>
<td>
```sh {cmd hide}
while ! [ -r 3_11.s ]; do sleep .1; done; cat 3_11.s
```
</td>
</tr>
</table>
## Procedures
Mechanisms in Procedures
* **Passing control**
* to beginning of procedure code
* back to return point
* **Passing data**
* procedure arguments
* return value
* **Memory management**
* allocate during procedure execution
* deallocate upon return
this mechanisms are all implemented with machine instructions. **x86-64 implementation** of a procedure used only those mechanisms required.
### Stack Structure
**x86-64 Stack**
Region of memory managed with *stack discipline*. It grows toward lower addresses. `%rsp` contains lowest stack address(address of top element).
`pushq $src`
* fetches operand at src
* decrement `%rsp` by 8
* write operand at address given by `%rsp`
`popq $dest`
* read value at address given by `%rsp`
* increment `%rsp` by 8
* store value at dest(must be register)
### Procedure Control Flow
```c {cmd=gcc args=[-Og -x c -c $input_file -o 3_12.o]}
long mult2(long x, long y) {
long t = x * y;
return t;
}
void multstore(long x, long y, long *dest) {
long t = mult2(x, y);
*dest = t;
}
```
```sh {cmd hide}
while ! [ -r 3_12.o ]; do sleep .1; done; objdump -d 3_12.o -Msuffix
```
Procedure call `call label`
* push return address on stack
* jmp to label
Return address:
* Address of the next instruction right after call
Procedure return: `ret`
### Procedure Data Flow
* registers
* first 6 args: `%rdi`, `%rsi`, `%rdx`, `%rcx`, `%r8`, `%r9`
* return value: `rax`
* stack
for example with above example
```sh {cmd hide}
while ! [ -r 3_12.o ]; do sleep .1; done; objdump -d 3_12.o -Msuffix
```
* with above `mult2` variable `t` is already stored in `%rax`
* so `movq %rax,(%rbx)` where `%rbx` is `long*dest`
### Managing local data
**Stack-Based Languages**
In languages that support recursion
* Code must be "reentrant", which means multiple simultaneous instantiations of single procedure.
* Need some place to store ***state*** of each instantiation: (**args**, **local variables**, **return pointer**)
In order to get this, **stack discipline** is used. state for given procedure needed for limited time(from called to return): Calle returns before caller does.
Stack allocated in **frames**, state for single procdure instantiation.
When function is called, a new stack frame is created above stack top. And then when the function is returned, a corresponding frame is popped. and return to previous call state.
#### Stack Frame
is consist of **return information**, **local storage(if needed)** and **temporary space(if needed)**.
* `%rbp` frame pointer
* `%rsp` stack pointer
Space allocated when enter procedure, "set-up" code and includes push by `call`.
Deallocated when return, "finish" code and includes pop by `ret`.
#### x86-64/Linux Stack Frame
![stack frame image](/assets/3_1stackframe.png)
* Arguments
* Local variables
* Old `rbp`
### Register Saving Conventions
When calling function, the temporary value of registers could be removed by called function, it could be trouble. So there are **conventions** to save the registers value.
When procedure `yoo` calls `who`: `yoo` is `caller`, `who` is `callee`
* Caller saves temporary values in its frame before the call.
* Callee saves saves temporary values in its frame before using and restores them before returning to caller.
#### x86-64 Linux Register Usage
`%rbx`, `%r12`, `%r13`, `%r14`, `%r15`
* Callee-saved
* Callee must save & restore
`%rbp`
* Callee-saved
* Callee must save & restore
* May be used as frame pointer by callee
* Can mix & match
`%rsp`
* Special form of callee-saved
* Restored to original value upon exit from procedure
#### EX
* for compile w/o *stack canary*, add option `-fno-stack-protector`
```c {cmd=gcc args=[-Og -x c -fno-stack-protector -c $input_file -o 3_13.o]}
long incr(long *p, long val) {
long x = *p;
long y = x + val;
*p = y;
return x;
}
long call_incr() {
long v1 = 15213;
long v2 = incr(&v1, 3000);
return v1 + v2;
}
```
```sh {cmd hide}
while ! [ -r 3_13.o ]; do sleep .1; done; objdump -d 3_13.o -Msuffix
```
### Recursive Function
```c {cmd=gcc args=[-O1 -x c -fno-stack-protector -c $input_file -o 3_14.o]}
long pcount_r(unsigned long x) {
if (x == 0) {
return 0;
} else {
return (x & 1) + pcount_r(x >> 1);
}
}
```
```sh {cmd hide}
while ! [ -r 3_14.o ]; do sleep .1; done; objdump -d 3_14.o -Msuffix
```
Recursion is not a special function.
* Stack frames mean that each function call has private storage.
* Register saving conventions prevent one function call from corrupting another's data. *unless the explictly corrupting like buffer overflow*
* Stack discipline follows call/return pattern LIFO

284
notes/4.md Normal file
View File

@@ -0,0 +1,284 @@
# Optimization
There's more to performance than asymptotic complexity(time complexity).
But all the instructions are not consume the same amount of time. Constant factors matter too! So we need to understand system to optimize performance.
* How programs are compiled and executed
* How modern processors and memory system operate
* How to measure performance and identify bottlenecks
* How to improve performance without destroying code modularity and generality
Provide efficent mapping of program to machine code
* Register allocation
* Code selection and ordering (scheduling)
* Dead code elimination
* Elimininating minor inefficiencies
**Don't improve asymptotic efficiency**.
## Generally Useful Optimizations
### Code Motion(Hoisting)
Reduce frequecy where computation performed. If it will always produce the same result, then move it to a place where it is computed once and reused.
Especially moving code out of loop.
```c {cmd=gcc args=[-Og -x c -c $input_file -o 4_1.o]}
void set_row(double *a, double *b, long i, long n) {
long j;
for (j = 0; j < n; j++) {
a[i * n + j] = b[j];
}
}
```
<table>
<tr><th>Default</th><th>Optimized</th></tr>
<tr><td>
```c {cmd=gcc args=[-O1 -x c -c $input_file -o 4_2.o]}
void set_row(double *a, double *b, long i, long n) {
long j;
for (j = 0; j < n; j++) {
a[i * n + j] = b[j];
}
}
```
</td><td>
```c
void set_row_opt(double *a, double *b, long i, long n) {
long j;
int ni = n * i;
for (j = 0; j < n; j++) {
a[ni + j] = b[j];
}
}
```
</td></tr>
<tr>
<td>
```sh {cmd hide}
while ! [ -r 4_1.o ]; do sleep .1; done; objdump -d 4_1.o
```
`imul` is located in the loop.
</td>
<td>
```sh {cmd hide}
while ! [ -r 4_2.o ]; do sleep .1; done; objdump -d 4_2.o
```
can see that `imul` is located out of the loop.
</td>
</tr>
</table>
Above two codes have same number of instructions. But optimized version has **fewer executed instructions**.
GCC will do this with `-O1` options
### Reduction in Strength
Replace costly operation with simpler one.
for example: power of 2 multiply to shift operation. normally, multiply and divide are expensive exmaple. on Intel Nehalem, `imul` requires 3 CPU cylcles on the other hand, `add` requires 1 cycle.
<table>
<tr><th>Default</th><th>Optimized</th></tr>
<tr><td>
```c
void test_reduction(double *a, double *b, long i, long n) {
int i, j;
for (i = 0;i < n; i++) {
int ni = n * i;
for (j = 0; j < n; j++) {
a[ni + j] = b[j];
}
}
}
```
</td><td>
```c
void test_reduction_opt(double *a, double *b, long i, long n) {
int i, j;
int ni = 0;
for (i = 0;i < n; i++) {
for (j = 0; j < n; j++) {
a[ni + j] = b[j];
}
ni += n;
}
}
```
</td></tr>
</table>
### Share Common Subexpressions
Reuse portations of expressions
GCC will do this with `-O1`
<table>
<tr><th>Default</th><th>Optimized</th></tr>
<tr><td>
```c {cmd=gcc args=[-O1 -x c -c $input_file -o 4_3.o]}
double test_scs(double* val, long i, long j, long n) {
double up, down, left, right;
up = val[(i - 1) * n + j];
down = val[(i + 1) * n + j];
left = val[i * n + (j - 1)];
right = val[i * n + (j + 1)];
return up + down + left + right;
}
```
</td><td>
```c
double test_scs_opt(double *a, double *b, long i, long n) {
double up, down, left, right;
long inj = i * n + j;
up = a[inj - n];
down = a[inj + n];
left = b[inj - 1];
right = b[inj + 1];
return up + down + left + right;
}
```
</td></tr>
</table>
```sh {cmd hide}
while ! [ -r 4_3.o ]; do sleep .1; done; objdump -d 4_3.o
```
Above dump shows only one `imul`, which shows that share common subexpressions are applied.
### Remove Unnecessary Procedure
Think with your intuition.
## Optimization Blockers
Compilers cannot always optimize your code.
```c
void lower(char *s) {
size_t i;
for (i = 0; i < strlen(s); i++) {
if (s[i] >= 'A' && s[i] <= 'Z') {
s[i] -= ('A' - 'a');
}
}
}
```
Above code's performance is bad. time quadruples when double string length.
Because `strlen` is executed on every loop. so `strlen` is $O(n)$, therefore overall performance of `lower` is $O(n^2)$
Therefore we optimized by Code Motion by moving the calculation length parts to out of the loop.
```c
void lower(char *s) {
size_t i;
size_t len = strlen(s);
for (i = 0; i < len; i++) {
if (s[i] >= 'A' && s[i] <= 'Z') {
s[i] -= ('A' - 'a');
}
}
}
```
### #1 Procedure Calls
Procedure may have side effects. and Function may not return same value for given arguments.
So compiler treats procedure call as a black box. Weak optimizations near them. Therefore strong optimizations like **Code Motion** are not applied.
In order to apply strong optimizations, First, use of inline function with `-O1` option, or **do your self**.
### Memory Aliasing
```c {cmd=gcc args=[-O1 -x c -c $input_file -o 4_4.o]}
void sum_rows(double *a, double *b, long n) {
long i, j;
for (i = 0; i < n; i++) {
b[i] = 0;
for (j = 0; j < n; j++) {
b[i] += a[i * n + j];
}
}
}
```
```sh {cmd hide}
while ! [ -r 4_4.o ]; do sleep .1; done; objdump -d 4_4.o -Msuffix
```
Compiler leave `b[i]` on every iteration. Because compiler must consider possibility that the updates will affect program behavior. (`b` and `a` is shared, memory aliasing)
Memory aliasing means two different memory references specify single location.
in C, it is easy to have happen. because address arithmetic and direct access to storage structures.
```c {cmd=gcc args=[-O1 -x c -c $input_file -o 4_5.o]}
void sum_rows(double *a, double *b, long n) {
long i, j;
for (i = 0; i < n; i++) {
double val = 0;
for (j = 0; j < n; j++) {
val += a[i * n + j];
}
b[i] = val;
}
}
```
```sh {cmd hide}
while ! [ -r 4_5.o ]; do sleep .1; done; objdump -d 4_5.o -Msuffix
```
By introducing local local variables, we can easy to get optimized code.
## Exploiting Instruction-Level Parallelism(ILP)
Execute multiple instructions at the same time. it can reduce average instruction cycle, which needs general understanding of modern processor design: HW can execute many operations in parallel.
* performance limited by data dependency
simple transformations can yield dramatic performance improvement.
### Superscalar Processors
Issue and Execute Multiple Instructions in one cycle.
pipelining -> data dependency.
for example Haswell CPU Functional Units
* 2 load
* 1 store
* 4 integer
* 2 FP mult
* 1 FP add
* 1 FP div
* 1 int mult
### Programming with AVX2
YMM register: 256bit, total 16 registers.
**SIMD Operations**
for single precision
`vaddps %ymm0, %ymm1, %ymm1`:
for double precision
`vaddpd %ymm0, %ymm1, %ymm1`

25
notes/8.md Normal file
View File

@@ -0,0 +1,25 @@
# Linking
When Calling Functions in Other Files, We Need to Link Them Together. Because Caller do know how to pass data by calling convention, but do know where the callee is located in memory.
## Why Linker Needed?
* Modularity
* Efficiency
* Time: Seperate Compiliation
* Space: Libraries
## What do Linker do?
1. Symbol Resolution
2. Reloacation
## 3 Types of Object
1. Relocatable Object File (`*.o`)
2. Executable File (`a.out`)
3. Shared Object File (`*.so`)
## ELF* Executable and Linkable Format

254
notes/9.md Normal file
View File

@@ -0,0 +1,254 @@
# Exception
An Exception is a **transfer of control** to the OS kernel in response to some event(like div by 0, overflow, ctrl+C). that is change in processor state.
**Exception Tables**
Each type of event has an unique exc number `k`: `k` is index into **exception table(interrupt vector table)**
Handler `k` is called each time exception `k` occurs.
## Asyncronous exceptions
It is caused by external events. Handler returns to next instruction.
for example: Timer interrupt, I/O interrupt.
## Syncronous exceptions
It is caused by events that occur as a result of exxecuting an current instruction.
* Traps
* Intentional like procedure calls (e.g. system calls)
* Returns to next instruction
* Faults
* Unintentional but possibly recoverable (e.g. page fault(recoverable), protection fault(not recoverable), floating point exception)
* re-executes by kernel or aborts
* Aborts
* Unintentional and not recoverable (e.g. illegal instruction, parity error, machine check)
* Aborts the program
## System Calls
Each x86-64 system call has a unique syscall number.
| Num | Name | Desc |
| --- | ------ | --------------- |
| 0 | read | read file |
| 1 | write | write file |
| 2 | open | open file |
| 3 | close | close file |
| 4 | stat | get file status |
| 57 | fork | create process |
| 59 | execve | execute program |
| 62 | kill | send signal |
## Fault Example
### Page Fault
```c
int a[1000];
main() {
a[500] = 13;
}
```
In this situation, a page containing `a[500]` is currently on disk, so page fault occurs, CPU cannot find the data in physical RAM. So kernel copy page from disk to memory, and return and re-executes the instruction `movl`
### Invalid Memory Ref
```c
int a[1000];
main() {
a[5000] = 13;
}
```
In this situation, address `a[5000]` is invalid, so protection fault occurs, kernel terminates the program by sending `SIGSEGV` signal to the user process. Then user process exits with error code `Segmentation Fault`.
## Process
An instance of a running program.
Process provides each program with two key abstractions:
* Logical control flow
* Each program seems to have **exclusive use of the CPU** provided by **context switching** of the kernel
* Private address space
* Each program seems to have **exclusive use of main memory** provided by **virtual memory** of the kernel
But in reality, computer runs multiple processes simultaneously by time-sharing CPU and multiplexing memory.
### Multiprocessing
Single processor executes multiple processes concurrently. process execution interleaved by time-slicing. Address spaces managed by virtual memory system. And register values for non-executing processes saved in memory.
Multicore processor share main memory each can execute a separate process. scheduling of processors onto cores done by kernel.
#### Concurrent Processes
Concurrency is **not at the exact same time**.
Two processes are **concurrent** if their flows **overlap in time**. Otherwise, they are **sequential**.
Control flows for concurrent processes are pysically disjoint in time. But user think that they are logically running in parallel.
* Execution time of instruction may vary because of the Nondeterminism of the System: OS scheduling, Interrupts, Cache miss or Page fault, I/O device delays.
#### Context Switching
Prcess are managed by a shared chunk of memory-resident OS code called the **kernel**.
What is important is that the kernel is **not a seprate process**. It is invoked by processes when they need OS services, or when exceptions occur. That is Part of the processor.
Control flow passes via a context switching.
## Syscall Error Handling
On error, Linux sys level function typically returns `-1` and sets the global variable `errno` to indicate the specific error.
Hard and fast rule:
* You must check the return status of every system-level function
* Only exception is the handful of functions that return void
Error reporting functions:
```c
void unix_error(char *msg) {
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(0);
}
```
Error handling wrappers:
```c
pid_t Fork(void) {
pid_t pid;
if ((pid = fork()) < 0) unix_error("Fork error");
return pid;
}
```
## Creating and Terminating Processes
* `pid_t getpid(void)` returnes pid of current process
* `pid_t getppid(void)` returns pid of parent process
We can think of a process as being in one of three states:
* Running
* Executing or waiting to be executed
* Stopped
* Process execution is suspended and will not be scheduled until futher notice
* Terminated
* Stopped permanently
### Terminating Processes
1. `return` from `main`
2. call `exit(status)` function
3. Receive a termination signal
```c
void exit(int status);
```
### Creating Process
Parent process can creates a new running child process by calling `fork()` system call.
```c
int fork(void);
```
it returns `0` to the newly created child, and returns **child's pid** to the parent.
Child is almost identical to parent: child get an identical copy of the parent's virtual address space, file descriptors, and process state.
But child has its own unique pid.
```c {cmd=gcc, args=[-O2 -x c $input_file -o 9_1.out]}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t pid;
int x = 1;
pid = fork();
if (pid == 0) { /* Child */
printf("child: x=%d\n", ++x);
exit(0);
}
printf("parent: x=%d\n", --x);
exit(0);
}
```
```sh {cmd hide}
while ! [ -r 9_1.out ]; do sleep .1; done; ./9_1.out
```
Concurrent execution of parent and child processes. `fork` duplicates but separates address space.
File descriptors are shared between parent and child like `stdout`, `stderr`.
Modeling fork with Process Graphs
* Each vertex represents a process state
* Directive Edges represent is ordering of execution.
* Edge can be labeled with current value of variables
Any topological sort of the graph corresponds to a feasible total ordering.
### Reaping Child Processes
When process terminates, it still consumes system resources. It is called a "zombie".
**"Reaping"** is performed by the parent by using `wait` or `waitpid`. And then parent is given exit status information. Finally kernel then deletes zombie child process.
If any parent terminates without reaping a child, then the orphaned child will be reaped by the `init` process (pid 1).
However, long-running processes should reap their children to avoid accumulating zombies.
```c {cmd=gcc, args=[-O2 -x c $input_file -o 9_2.out]}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
if(fork() == 0) {
printf("Terminating child process\n");
exit(0);
} else {
printf("Running Parent\n");
sleep(1);
printf("Parent exiting\n");
}
}
```
```sh {cmd hide}
while ! [ -r 9_2.out ]; do sleep .1; done; ./9_2.out & ps -ef | grep "9_2.out" | grep -v grep;
```
```c
pid_t wait(int * child_status);
```
`wait` suspends current process until **one of its children** terminates.
* **return value**: pid of terminated child
* **child_status**: if not `NULL`, the integer points will be set to the termination status(reason and exit status) of the child.
* It can be checked by using macros defined in `wait.h`
## execve
```c
int execve(const char *filename, char *const argv[], char *envp[]);
```
Load and runs in the current process.
* `filename`: path of the executable file
* `argv`: argument list
* `envp`: environment variables "name=value"
* `getenv`, `putenv`, `printenv`
It does **overwrite code, data, stack**. retain only **pid, file descriptors, signal context**.
Called **once and never returns on success**.

BIN
out/notes/1.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
out/notes/2.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
out/notes/3.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
out/notes/4.pdf (Stored with Git LFS) Normal file

Binary file not shown.

6941
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

5
package.json Normal file
View File

@@ -0,0 +1,5 @@
{
"dependencies": {
"crossnote": "^0.9.15"
}
}

BIN
pdf/L10.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L11.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L12.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L13.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L14.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L3.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L4.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L5.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L6.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L7.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L8.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/L9.pdf (Stored with Git LFS) Normal file

Binary file not shown.

BIN
pdf/P3.pdf (Stored with Git LFS) Normal file

Binary file not shown.