Name: Khawaja Tahir Zia Roll No.: 451365 Section: B Name: Muhammad Anas Roll no.: 452581 Section: B OS Assignment 3 Q1: #include <iostream> #include <pthread.h> #include <chrono> #include <mutex> #include <atomic> using namespace std; typedef struct thread_arg { int start_index; int end_index; } thread_arg; void* runner(void* param); void single_thread(); void two_threads(); void three_threads(); void four_threads(); atomic<bool> stop = false; string passwd = "99999"; // Password of length 5 string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; // Possible characters in password int main() { single_thread(); // two_threads(); // three_threads(); // four_threads(); } // Thread Function to guess password void* runner(void* param) { thread_arg *th_arg = (thread_arg *) param; // Work Division string attempt = "aaaaa"; // Check all combinations for (int h = th_arg->start_index; h <= th_arg->end_index; h++) { attempt[0] = alphabet[h]; for (int i = 0; i < alphabet.length(); i++) { attempt[1] = alphabet[i]; for (int j = 0; j < alphabet.length(); j++) { attempt[2] = alphabet[j]; for (int k = 0; k < alphabet.length(); k++) { attempt[3] = alphabet[k]; for (int l = 0; l < alphabet.length(); l++) { attempt[4] = alphabet[l]; if (attempt == passwd) { cout << "Match" << endl; stop = true; return NULL; } if (stop == true) { return NULL; } } } } } } delete th_arg; return NULL; } // Single sequential execution void single_thread() { thread_arg th_arg; th_arg.start_index = 0; th_arg.end_index = 61; auto start = chrono::steady_clock::now(); string attempt = "aaaaa"; bool flag = true; // Check all combinations for (int h = th_arg.start_index; h <= th_arg.end_index && flag; h++) { attempt[0] = alphabet[h]; for (int i = 0; i < alphabet.length() && flag; i++) { attempt[1] = alphabet[i]; for (int j = 0; j < alphabet.length() && flag; j++) { attempt[2] = alphabet[j]; for (int k = 0; k < alphabet.length() && flag; k++) { attempt[3] = alphabet[k]; for (int l = 0; l < alphabet.length(); l++) { attempt[4] = alphabet[l]; if (attempt == passwd) // Stop if match { cout << "Match" << endl; flag = false; break; } } } } } } auto end = chrono::steady_clock::now(); cout << "Single Thread: " << chrono::duration_cast<chrono::milliseconds>(end-start).count() / (float)1000 << " seconds\n"; return; } void two_threads() { // Divide work and create threads auto start = chrono::steady_clock::now(); pthread_t workers[2]; thread_arg *arg = new thread_arg; arg->start_index = 0; arg->end_index = 30; pthread_create(&workers[0], NULL, runner, arg); arg = new thread_arg; arg->start_index = 31; arg->end_index = 61; pthread_create(&workers[1], NULL, runner, arg); for (int i = 0; i < 2; i++) { pthread_join(workers[i], NULL); } auto end = chrono::steady_clock::now(); cout << "Two Threads: " << chrono::duration_cast<chrono::milliseconds>(end-start).count() / (float)1000 << " seconds\n"; } void three_threads() { auto start = chrono::steady_clock::now(); pthread_t workers[3]; // Divide work and create threads thread_arg *arg = new thread_arg; arg->start_index = 0; arg->end_index = 19; pthread_create(&workers[0], NULL, runner, arg); arg = new thread_arg; arg->start_index = 20; arg->end_index = 40; pthread_create(&workers[1], NULL, runner, arg); arg = new thread_arg; arg->start_index = 41; arg->end_index = 61; pthread_create(&workers[2], NULL, runner, arg); for (int i = 0; i < 3; i++) { pthread_join(workers[i], NULL); } auto end = chrono::steady_clock::now(); cout << "Three Threads: " << chrono::duration_cast<chrono::milliseconds>(end-start).count() / (float)1000 << " seconds\n"; } void four_threads() { auto start = chrono::steady_clock::now(); pthread_t workers[4]; // Divide work and create threads thread_arg *arg = new thread_arg; arg->start_index = 0; arg->end_index = 15; pthread_create(&workers[0], NULL, runner, arg); arg = new thread_arg; arg->start_index = 16; arg->end_index = 30; pthread_create(&workers[1], NULL, runner, arg); arg = new thread_arg; arg->start_index = 31; arg->end_index = 45; pthread_create(&workers[2], NULL, runner, arg); arg = new thread_arg; arg->start_index = 46; arg->end_index = 61; pthread_create(&workers[3], NULL, runner, arg); for (int i = 0; i < 4; i++) { pthread_join(workers[i], NULL); } auto end = chrono::steady_clock::now(); cout << "Four Threads: " << chrono::duration_cast<chrono::milliseconds>(end-start).count() / (float)1000 << " seconds\n"; } Q2: #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE 9 #define NUM_OF_THREADS 9 #define NUM_OF_GRIDS 9 int sudoku[SIZE][SIZE] = {{6,2,4,5,3,9,1,8,7}, {5,1,9,7,2,8,6,3,4}, {8,3,7,6,1,4,2,9,5}, {1,4,3,8,6,5,7,2,9}, {9,5,8,2,4,7,3,6,1}, {7,6,2,3,9,1,4,5,8}, {3,7,1,9,5,6,8,4,2}, {4,9,6,1,8,2,5,7,3}, {2,8,5,4,7,3,9,1,6}}; int col_status[SIZE], row_status[SIZE], grid_status[SIZE]; typedef struct Grid { int i; int j; } Grid; int row_checker(void *param); int column_checker(void *param); int grid_checker(void *param); int main(int argc, char *argv[]) { // Check Rows - Threads for each Row pthread_t workers[NUM_OF_THREADS]; pthread_attr_t attrs[NUM_OF_THREADS]; // Create Threads for (int i = 0; i < NUM_OF_THREADS; i++) { pthread_attr_init(&attrs[i]); pthread_create(&workers[i], &attrs[i], row_checker, i); } // Wait for threads for (int i = 0; i < NUM_OF_THREADS; i++) { int status; pthread_join(workers[i], &status); row_status[i] = status; } for (int i = 0; i < NUM_OF_THREADS; i++) { if (row_status[i] == 1) { printf("Not Valid\n"); return 0; } } // Check Columns - Threads for each column // Create Threads for (int i = 0; i < NUM_OF_THREADS; i++) { pthread_attr_init(&attrs[i]); pthread_create(&workers[i], &attrs[i], column_checker, i); } // Wait for threads for (int i = 0; i < NUM_OF_THREADS; i++) { int status; pthread_join(workers[i], &status); col_status[i] = status; } // Check if any of the columns were invalid for (int i = 0; i < NUM_OF_THREADS; i++) { if (col_status[i] == 1) { printf("Not Valid\n"); return 0; } } // Grid Checking - Threads for each grid // Create threads Grid *val = malloc(sizeof(Grid)); val->i = 0, val->j = 0; pthread_attr_init(&attrs[0]); pthread_create(&workers[0], &attrs[0], grid_checker, val); val = malloc(sizeof(Grid)); val->i = 0, val->j = 3; pthread_attr_init(&attrs[1]); pthread_create(&workers[1], &attrs[1], grid_checker, val); val = malloc(sizeof(Grid)); val->i = 0, val->j = 6; pthread_attr_init(&attrs[2]); pthread_create(&workers[2], &attrs[2], grid_checker, val); val = malloc(sizeof(Grid)); val->i = 3, val->j = 0; pthread_attr_init(&attrs[3]); pthread_create(&workers[3], &attrs[3], grid_checker, val); val = malloc(sizeof(Grid)); val->i = 3, val->j = 3; pthread_attr_init(&attrs[4]); pthread_create(&workers[4], &attrs[4], grid_checker, val); val = malloc(sizeof(Grid)); val->i = 3, val->j = 6; pthread_attr_init(&attrs[5]); pthread_create(&workers[5], &attrs[5], grid_checker, val); val = malloc(sizeof(Grid)); val->i = 6, val->j = 0; pthread_attr_init(&attrs[6]); pthread_create(&workers[6], &attrs[6], grid_checker, val); val = malloc(sizeof(Grid)); val->i = 6, val->j = 3; pthread_attr_init(&attrs[7]); pthread_create(&workers[7], &attrs[7], grid_checker, val); val = malloc(sizeof(Grid)); val->i = 6, val->j = 6; pthread_attr_init(&attrs[8]); pthread_create(&workers[8], &attrs[8], grid_checker, val); // Wait for threads to join for (int i = 0; i < NUM_OF_THREADS; i++) { int status; pthread_join(workers[i], &status); grid_status[i] = status; } // Check if any of the threads were invalid for (int i = 0; i < NUM_OF_THREADS; i++) { if (grid_status[i] == 1) { printf("Not Valid\n"); return 0; } } printf("Valid\n"); } int row_checker(void *param) { int row = (int)param, flag = 1; int check[SIZE] = {0,0,0,0,0,0,0,0,0}; for (int j = 0; j < SIZE; j++) { check[sudoku[row][j] - 1] = 1; // Check if 1-9 exist in row } for (int i = 0; i < SIZE; i++) { if (check[i] == 0) { flag = 0; break; } } // Invalid if (flag == 0) { return 1; } return 0; } int column_checker(void *param) { int column = (int)param, flag = 1; int check[SIZE] = {0,0,0,0,0,0,0,0,0}; for (int i = 0; i < SIZE; i++) { check[sudoku[i][column] - 1] = 1; // Check if 1-9 exist in column } for (int i = 0; i < SIZE; i++) { if (check[i] == 0) { flag = 0; break; } } // Invalid if (flag == 0) { return 1; } return 0; } int grid_checker(void *param) { Grid *grid_index = (Grid *) param; int flag = 1; int check[SIZE] = {0,0,0,0,0,0,0,0,0}; for (int i = grid_index->i; i < grid_index->i + 3; i++) { for (int j = grid_index->j; j < grid_index->j + 3; j++) { check[sudoku[i][j]- 1] = 1; // Check if 1-9 exist in Grid } } free(grid_index); for (int i = 0; i < SIZE; i++) { if (check[i] == 0) { flag = 0; break; } } // Invalid if (flag == 0) { return 1; } return 0; } Q3: First Fit 212KB is put in 500KB partition. 417KB is put in 600KB partition. 112KB is put in 288KB partition. (288KB created from 500KB – 212KB = 288KB) 426KB must wait for a partition to free up. Best Fit 212KB is put in 300KB partition. 417KB is put in 500KB partition. 112KB is put in 200KB partition. 426KB is put in 600KB partition. Worst Fit 212KB is put in 600KB partition. 417KB is put in 500KB partition. 112KB is put in 388KB partition. (388KB created from 600KB – 212KB = 388KB) 426KB must wait for a partition to free up. Best fit makes the most efficient use of memory in this example. Q4: Question # 5: Why page sizes are usually powers of 2? Page sizes are usually powers of 2 because it simplifies memory management and ensures that memory can be allocated, accessed, and managed efficiently. When a computer represents an address as a number, it stores it as binary bits. When converting a linear address to page: offset, it is most efficient to break the address into X page bits and Y offset bits, rather than perform arithmetic on the address to calculate the page number and offset ¹. Because each bit position represents a power of 2, splitting an address between bits results in a page size that is a power of 2. For example, if the page size is 4 (a power of 2), then 4 in binary is 100, and integer division and modulus can be computed using special 'shortcuts': you can strip the last two binary digits to divide, and you can keep only the last two binary digits for modulus. This allows the memory hardware to extract the page number and byte offset in parallel, without performing any arithmetic operations [1][2]. Question No 6: Consider the following page reference string: 1, 2, 3, 4, 1, 2, 3, 7, 6, 3, 2, 1, 2, 3, 6, 2, 1, 5, 6, 2. How many page faults would occur for the following replacement algorithms, assuming one, two, three, four, five, six, or seven frames? Remember all frames are initially empty, so your first unique pages will all cost one fault each. a) LRU replacement b) FIFO replacement c) Optimal replacement LRU replacement: FIFO Replacement Algorithm: Optimal Replacement Algorithm: Question # 7: a) Compare symmetric and asymmetric encryption schemes, and discuss the circumstances under which a distributed system would use one or the other. Encryption is a technique used to protect data from being deciphered by unauthorized individuals. There are two main types of encryptions: symmetric and asymmetric. Symmetric encryption uses a single secret key for both encryption and decryption. The sender and receiver share the same key, which is used to encrypt and decrypt the data. This method is faster than asymmetric encryption, but it requires that the sender and receiver have a secure way to exchange the key. Asymmetric encryption also known as public key cryptography, uses two keys: a public key and a private key. The public key is used to encrypt the data, while the private key is used to decrypt it. The public key can be shared with anyone, while the private key must be kept secret. This method is slower than symmetric encryption, but it eliminates the need for a secure key exchange. In a distributed system, the choice between symmetric and asymmetric encryption depends on the specific use case. Symmetric encryption is generally used when the sender and receiver are known to each other and have a secure way to exchange the key. Asymmetric encryption is used when the sender and receiver are not known to each other or do not have a secure way to exchange the key. For example, in a peer-to-peer network where nodes communicate with each other, asymmetric encryption can be used to establish a secure channel between two nodes that have never communicated before. Once the secure channel is established, symmetric encryption can be used for subsequent communication. In summary, symmetric encryption is faster but requires a secure key exchange, while asymmetric encryption is slower but eliminates the need for a secure key exchange. The choice between the two depends on the specific use case in a distributed system [3]. b) Make a list of six security concerns for a bank’s computer system. For each item on your list, state whether this concern relates to physical, human, or operating-system security 1. Unauthorized access: This is when someone gains access to the bank's computer system without permission. This is a human security concern, as it involves people accessing the system who should not be able to. 2. Malware: Malware is software that is designed to harm a computer system. This is an operating-system security concern, as it involves protecting the system from malicious software. 3. Phishing: Phishing is when someone tries to trick a user into giving away sensitive information, such as login credentials. This is also a human security concern, as it involves people being tricked into giving away information. 4. Denial of service attacks: Denial of service attacks are when someone tries to make a computer system unavailable to users. This is an operating-system security concern, as it involves protecting the system from being overwhelmed by requests. 5. Insider threats: Insider threats are when someone with authorized access to the system misuses that access. This is a human security concern, as it involves people who are authorized to access the system abusing that access. 6. Physical theft: Physical theft is when someone steals a computer or other device that contains sensitive information. This is a physical security concern, and it involves protecting the physical devices that contain sensitive information. [4] References: [1] - stackoverflow.com [2] - lxadm.com [3] - techrepublic.com [4] - alert-software.com