diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 000000000..bec592fae --- /dev/null +++ b/NOTES.md @@ -0,0 +1,230 @@ +# Processes + +## Requirements to Execute a Program +- CPU number of cores +- Memory + +### Example of an Address Space (P1 RAM between P2 RAM and P3 RAM) + +low address | +_______________________________________________________________________| +high address Command-line Args & Environment Variable | + | + stack Static Memory | + | + int main | + func1 | + func2 | | + | + heap Dynamically-allocated memory malloc data | + | + | + uninitialized data (bss) initialized to zero by exec | + | + initialized data \ | + } read from program file by exec | + text / | + | +low address | +_______________________________________________________________________| +high address | + | + + +Processes are isolated by default (unaware of each other by default) + +## Initializing a Process + +- Allocation of working memory. +- Entry is added to the operating system's processing list. +- OS's scheduling algorithm schedules the newly-initialized process. + +## Scheduling +Process List +PID Command +1 a +2 b +3 c --> +4 d Context CPU +5 e Switching +6 f <-- +7 g +8 h + +PID (Process Identifier Number): 2 byte number + +## Privileged vs. Non-Privileged +Privileged operations +- Creating new processes +- destroying existing processes +- Allocating memory +- Accessing hardware functionality +- scheduling algorithm + +Non-Privileged operations +- everything I write. :) + + +# System Calls +- A request to the OS to perform a privileged operation on its behalf. +- Uses requests and response (similar to API calls) + +System calls with requests and responses is: +- Less Performant but Safer + +MS-DOS allowed Processes to perform a privileged operations, which is why games are more prevelant on Windows than Mac. + + Request + --------> ----------> +Processes OS (Gatekeeper) Hardware + <-------- <--------- + Response + +- Most languages rely on C to do the actual requests and responses. + +Example of Request System Call: +- malloc() +- fork() + + + + P1 P2 +x (executed) x (value carried over) +y (executed) y (value carried over) +z (executed) z (value carried over) +fork() => creates a forked copy fork () (carried over) + +Parent code Child Code + + +if P2 also executes the fork it is a problem referred to as a fork bomb. + +## Commonly-Used System Calls +- fork() create copy of existing process +- exec() execute a specified file +- chdir() change working directory +- pipe() creates a way for interprocess communication. + +fork() + - creates a child process, which is a copy from the parent process + - returns twice: + - one result in the parent + - another result in the child + +pipe() + - uni-directional: one write end & one read end; + - initalize pipe first then fork + + P1 P2 +| | | | +| |_____| | + w -> _____ -> r +| | | | +| | | | +| |_____| | + r <- _____ <- w | +| | | | +| | | | + + + + + + + + + +```c +# include +// putchar: writes a character to the standard output (stdout); +// it is equivalent to calling putc iwth stdout as second argument. +void print_str(char *s) { + int i; + char *p = s; // saving a copy of s + // putchar(** take a character argument) + //iterate over s and use putchar for every character on the array of characters. + for(i = 0; s[i] != '\0; i++) { + putchar(s[i]); + } + + // equivalent method + for(i = 0; *(s+i) != '\0; i++) { + putchar(*s(i+i)); + } + + // another method: it manipulates the values themselves + for(; *s != '\0; s++) { + putchar(*s); + } + + // after doing the last method (the s pointer is pointing to '\0' so calling the first method will no longer work since s is already at '\0'). So we need to add: + s = p; + + while (*s != '\0') { + putchar(*(s++)) + /* combines 2 lines of into one; it is equivalent to: + putchar(*s); + s++; + */ + } + + +} +int main(void) { + pid_t pid; + pid = fork(); // doesn't take paramters + if (pid == 0) { + printf("I am the child\n"); + } else { + printf("Parent is about to wait\n"); + wait(NULL) // if this is commentted result will be different. + printf("I am the parent\n"); + } + + + return 0; +} +``` + +```c +# include +// putchar: writes a character to the standard output (stdout); +// it is equivalent to calling putc with stdout as second argument. + + +/* +size_t write(int fd, void* buf, size_t cnt); +fd: file descripter: a pointer to a file +buf: buffer to write data to +cnt: length of buffer + +size_t is typedef howevers it's going to return how many bytes were actually written + +return Number of bytes written on success +return 0 on reaching end of file +return -1 on error +*/ + + +void putchar_ls(char c) { + // c is a vlue we need to have a pointer + //so we use & to point to the address of c + write(1, &c, 1); + // sz = write(1, &c, 1); + // printf("\n%d\n", sz); +} + +void print_str(char *s) { + while(*s != '\0') { + putchar_ls(*(s++)) + } +} + + +} +int main(void) { + + printf("HelloWorld\n"); + return 0; +} + diff --git a/SchedulingDiagram.png b/SchedulingDiagram.png new file mode 100644 index 000000000..ce6dba931 Binary files /dev/null and b/SchedulingDiagram.png differ diff --git a/ShellDemo.c b/ShellDemo.c new file mode 100644 index 000000000..4f5010649 --- /dev/null +++ b/ShellDemo.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include +#include + + +#define MAX_COMMANDLINE_LEN 8192 // constant variable in global scope +#define MAX_COMMANDLINE_ARGS 128 // constant variable in global scope + +/* PROTOTYPE */ +void parse_commandline(char *commandline, char *exec_commands[], int max_count); +// pointers + + + + + +int main(void) { + char commandline[MAX_COMMANDLINE_LEN]; + // char **exec_commands + char *exec_commands[MAX_COMMANDLINE_ARGS] + return 0; +} \ No newline at end of file diff --git a/ShellDemoSean b/ShellDemoSean new file mode 100755 index 000000000..5ef2cb284 Binary files /dev/null and b/ShellDemoSean differ diff --git a/ShellDemoSean.c b/ShellDemoSean.c new file mode 100644 index 000000000..731b18b9f --- /dev/null +++ b/ShellDemoSean.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include + +#define MAX_TOKENS 100 + +char **parse_commandline(char *str, int *argc, char **args) +{ + char *token; + *argc = 0; + + // Break the given str down into tokens using a delimiter + // More specifically, we'll split on tabs, newlines, and carriage returns + token = strtok(str, " \t\n\r"); + + // Add this token to the args array + while (token != NULL && *argc < MAX_TOKENS) { + // Index into args array using the value in argc + // Then increment *argc + args[(*argc)++] = token; + + // Fetch the next token + token = strtok(NULL, " \t\n\r"); + } + + // Lastly, add a NULL as the last element in args + args[*argc] = NULL; + + return args; +} + +int main(void) +{ + // Holds the entire command line input all at once + char commandline[1024]; + // Holds individual parsed tokens + char *args[MAX_TOKENS]; + // Number of args stored in the args array + int argc; + + // In a continuous loop + while (1) { + // Receive input from stdin + // shell should print a prompt for the user + printf("lambda-shell$"); + + // Read input from stdin + fgets(commandline, sizeof(commandline), stdin); + + // Parse that input into tokens + // Parse the commandline buffer into individual args + parse_commandline(commandline, &argc, args); + + // Check for some specific commands + // Like an exit command or other such commands + if (argc == 0) { + continue; + } + + // Check for an "exit" command + if (argc == 1 && strcmp(args[0], "exit") == 0) { + break; + } + + // Check for "cd" command + if (strcmp(args[0], "cd") == 0) { + if (argc != 2) { + printf("usage: cd dirname\n"); + continue; + } + + // Change to the specified directory + if (chdir(args[1]) < 0) { + fprintf(stderr, "chdir failed"); + continue; + } + + // If we successfully changed directories, continue our shell loop + continue; + } + + // To allow us to execute arbitrary programs, + // use fork and exec in concert + // Fork this process + // In the child context, exec with the specified program name + + pid_t child_pid = fork(); + + if (child_pid < 0) { + fprintf(stderr, "fork failed"); + continue; + } + + // In the child context + if (child_pid == 0) { + // Exec the child program, transforming it in the specified command + execvp(args[0], args); + + // If we get here, that means the exec call failed + fprintf(stderr, "exec failed"); + exit(1); + } else { + // In the parent context + // Wait for the child to complete + waitpid(child_pid, NULL, 0); + } + } + + return 0; +} \ No newline at end of file diff --git a/ex1/ex1 b/ex1/ex1 new file mode 100755 index 000000000..db6b7b696 Binary files /dev/null and b/ex1/ex1 differ diff --git a/ex1/ex1.c b/ex1/ex1.c index c4b111641..4937b3a1e 100644 --- a/ex1/ex1.c +++ b/ex1/ex1.c @@ -2,13 +2,45 @@ // (e.g., x) and set its value to something (e.g., 100). What value is the variable in the child process? // What happens to the variable when both the child and parent change the value of x? + + + +// init is PID 1 + #include #include -#include +#include // required for fork() +// #include // required for wait() + // when sys/wait wasn't added it could run in different orders +int main(void) { + + int x = 100; + pid_t pid; // _t means type delcaration pid under the hood it is an int. + pid = fork(); // doesn't take paramters + if (pid < 0) { + printf("Can't fork; error occured\n"); + exit(EXIT_FAILURE); + } else if (pid == 0) { + x = 7; + printf("From child (pid %d): x is %d\n", (int) getpid(), x); // x = 7 + } else { + printf("From parent (pid %d): x is %d\n", (int) getpid(), x); // x = 100 + x = 10; + // wait(NULL); // if this is commentted result will be different. + int wc = waitpid(pid, NULL, 0); + printf("From parent (pid %d) of child (pid %d): x is %d\n", (int) getpid(), pid, x); // x = 10 + } +/* +Order run: +100 +10 +7 -int main(void) -{ - // Your code here +But without wait it could be: +7 +100 +10 +*/ - return 0; + return 0; } diff --git a/ex2/ex2 b/ex2/ex2 new file mode 100755 index 000000000..6e54df93f Binary files /dev/null and b/ex2/ex2 differ diff --git a/ex2/ex2.c b/ex2/ex2.c index 4245375b9..59e090aa7 100644 --- a/ex2/ex2.c +++ b/ex2/ex2.c @@ -8,7 +8,26 @@ int main(void) { - // Your code here + FILE *fptr; + fptr = fopen("text.txt","a"); + printf("Before fork is called\n"); + pid_t pid; // _t means type delcaration pid under the hood it is an int. + pid = fork(); // doesn't take paramters + if (pid == 0) { + fprintf(fptr, "Child wrote in text file\n"); + } else { + fprintf(fptr, "Parent wrote in text file\n"); + } + fclose(fptr); + return 0; } + +/* +"Before fork is called" is written to the terminal. +Both parent and child have access to the file. +Parent & Child both wrote to text.txt +Order observed was always parent before child. +Could this be different since there is no wait? +*/ diff --git a/ex2/text.txt b/ex2/text.txt index e69de29bb..3b9eaa767 100644 --- a/ex2/text.txt +++ b/ex2/text.txt @@ -0,0 +1,6 @@ +Parent wrote in text fileChild wrote in text fileParent wrote in text file +Child wrote in text file +Parent wrote in text file +Child wrote in text file +Parent wrote in text file +Child wrote in text file diff --git a/ex3/ex3 b/ex3/ex3 new file mode 100755 index 000000000..8f2524f22 Binary files /dev/null and b/ex3/ex3 differ diff --git a/ex3/ex3.c b/ex3/ex3.c index 3a3698c1f..e420f7756 100644 --- a/ex3/ex3.c +++ b/ex3/ex3.c @@ -9,7 +9,14 @@ int main(void) { - // Your code here - return 0; + pid_t pid; // _t means type delcaration pid under the hood it is an int. + pid = fork(); // doesn't take paramters + + if (pid == 0) { + printf("hello\n"); + } else { + wait(NULL); // to ensure that child runs first + printf("goodbye\n"); + } } diff --git a/ex4/ex4 b/ex4/ex4 new file mode 100755 index 000000000..624e339c2 Binary files /dev/null and b/ex4/ex4 differ diff --git a/ex4/ex4.c b/ex4/ex4.c index 0221ca96e..d251549f5 100644 --- a/ex4/ex4.c +++ b/ex4/ex4.c @@ -10,7 +10,45 @@ int main(void) { - // Your code here + pid_t pid; + pid = fork(); + char *argv[3] = {"/bin/ls", ".", NULL}; // execvp( "find", argv ); + if (pid == 0) { + execvp( "find", argv ); + // ./ex4 + // ./ex4.c + + } else { + printf("Parent says hello\n"); + } return 0; } + +/* +https://ece.uwaterloo.ca/~dwharder/icsrts/Tutorials/fork_exec/ +"The exec Family of Functions +There is a family of exec() functions, all of which have slightly different characteristics: + + int execl ( const char *path, const char *arg, ... ); + int execlp( const char *file, const char *arg, ... ); + int execle( const char *path, const char *arg, ..., char *const envp[] ); + int execv ( const char *path, char *const argv[] ); + int execvp( const char *file, char *const argv[] ); + int execve( const char *file, char *const argv[], char *const envp[] ); +Each system call is the word exec followed by either l or v and then possibly followed by either e or p. + +The first three have are of the form execl and accept a variable number of arguments. In order to use this feature, you must load the header file. Please see the example stdarg.c. + +The latter three are of the form execv in which case the arguments are passed using an array of pointers to strings where the last entry is NULL. For example, you might have: + char *argv[] = {"Hello ", "world!", NULL}; + +If the name ends in either l or v, the program name must be given in full. + +If the name is appended by a p, it will search for the file using the current environment variable PATH, which usually includes /bin/, /usr/bin/, etc. + +Finally, if the name is appended by a e, one can include an array of strings indicating environment variables, each of the form "ENVVAR=value" and the array being null terminated. For example, + char *envp[] = {"USER=dwharder", "HOME=/home/dwharder", NULL}; +" + +*/ diff --git a/ex5/ex5 b/ex5/ex5 new file mode 100755 index 000000000..312e97fbd Binary files /dev/null and b/ex5/ex5 differ diff --git a/ex5/ex5.c b/ex5/ex5.c index cbf3b8e61..90a2823e0 100644 --- a/ex5/ex5.c +++ b/ex5/ex5.c @@ -16,7 +16,31 @@ char* msg3 = "hello world #3"; int main(void) { - // Your code here - - return 0; + char inbuf[MSGSIZE]; + int p[2]; + + if(pipe(p) < 0) { + fprintf(stderr, "Pipe error occured\n"); + exit(EXIT_FAILURE); + } + + pid_t pid; + pid = fork(); + if (pid < 0) { + printf("Fork error occured\n"); + exit(EXIT_FAILURE); + } else if (pid == 0) { + write(p[1], msg1, MSGSIZE); + write(p[1], msg2, MSGSIZE); + write(p[1], msg3, MSGSIZE); + } else { + // wait(NULL); // if this is commentted result will be different. + int wc = waitpid(pid, NULL, 0); + printf("From parent (pid %d): Child (pid %d) wrote: \n", (int) getpid(), pid); // x = 10 + for (int i = 0; i < 3; i++) { + read(p[0], inbuf, MSGSIZE); + printf("%s\n", inbuf); + } + } + return 0; } diff --git a/ex6/ex6 b/ex6/ex6 new file mode 100755 index 000000000..77db75a42 Binary files /dev/null and b/ex6/ex6 differ diff --git a/ex6/ex6.c b/ex6/ex6.c index 17532d65f..5b3196d38 100644 --- a/ex6/ex6.c +++ b/ex6/ex6.c @@ -20,7 +20,27 @@ and `clock_gettime()` should work just fine. int main() { - // Your code here + uint64_t diff; + struct timespec start, end; + int i; + + /* measure monotonic time */ + clock_gettime(CLOCK_MONOTONIC, &start); /* mark start time */ + write(fileno(stdout), NULL, 0); + clock_gettime(CLOCK_MONOTONIC, &end); /* mark the end time */ + + diff = BILLION * (end.tv_sec - start.tv_sec) + end.tv_nsec - start.tv_nsec; + printf("elapsed time = %llu nanoseconds\n", (long long unsigned int) diff); - return 0; + + /* now re-do this and measure CPU time */ + /* the time spent sleeping will not count (but there is a bit of overhead */ + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start); /* mark start time */ + write(fileno(stdout), NULL, 0); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end); /* mark the end time */ + + diff = BILLION * (end.tv_sec - start.tv_sec) + end.tv_nsec - start.tv_nsec; + printf("elapsed process CPU time = %llu nanoseconds\n", (long long unsigned int) diff); + + exit(0); }