Tuesday, August 12, 2025

Week 8

 Week 8 (8/13-8/15)

This week I want to make a reflection of all the topics and challenges faced throughout these 8 weeks, since in my last journal, I already talked about this week's content: persistence. Before starting this class, I was apprehensive, especially when I learned we would be working with C. I had always heard that C is low-level, unforgiving, and complex compared to higher-level languages I’ve used before. Since I have been leaning toward career paths like cybersecurity or data science, I was more comfortable with analysis than raw systems programming. 

One of the most challenging topics for me was CPU scheduling, particularly the Round Robin algorithm. Implementing time-slicing logic while maintaining correct queue order required a precise understanding of process states and context switching overhead. I iterated through several incorrect solutions before finally deriving a correct implementation after a teammate shared his approach via a recorded walkthrough.

Collaboration and teamwork were essential throughout the course. Each of my team members had strengths in different OS concepts, and knowledge sharing allowed us to fill each other’s gaps. For example, I created a video explaining FIFO, LRU, and Belady’s Anomaly in caching policies, which I had mastered after repeated simulation exercises. In return, a classmate helped me understand segmentation and memory protection mechanisms.

Persistence mechanisms were also challenging, particularly concepts like hard drive rotation and inode-based indexing. Understanding the relationship between rotational delay, seek time, and data transfer rates, along with inode-based metadata and pointer structures, required deep reading. I struggled partly due to limited study time while balancing my internship workload, where I was finalizing deliverables for my project.

For our final project, my team explored Triangulating Python Performance Issues with SCALENE. While I understood SCALENE’s ability to profile CPU, memory, and GPU usage simultaneously, the hardware test bench—an 8-core 4674 MHz AMD Ryzen 7 with 32 GB RAM and an NVIDIA GeForce RTX 2070 running Linux—required me to contextualize performance data in terms of underlying hardware capabilities.

Overall, this course strengthened my understanding of OS fundamentals—process scheduling, memory management, file systems, and performance analysis—while reinforcing the importance of collaborative problem-solving. Thanks to Dr. Ogden for making this class super interesting and for being one of the best professors I've had so far in the program.

Week 7

 Week 7 (8/6-8/12)

This week, we covered persistence. From I/O Devices, I learned that devices can be block (e.g., hard drives, storing fixed-size blocks with random access) or character (e.g., keyboards, handling byte streams). The OS interacts with them via registers—status, command, and data—using polling, interrupts, or Direct Memory Access (DMA). Device drivers isolate OS code from hardware specifics.

From Hard Drives, I discovered that performance depends on rotational delay, seek time, and transfer time. For example, reading 2 MB with a 4 ms rotational delay, 5 ms seek time, and 100 MB/s transfer rate takes 29 ms. Sequential workloads are much faster than random ones, and I/O scheduling (e.g., SSTF, elevator) reduces unnecessary head movement.

From Files and Directories, I learned that files are linear byte arrays with metadata in inodes, while directories map names to inode numbers. Hard links point multiple names to the same inode; symbolic links are files containing paths to targets. Mounting attaches a filesystem to a directory in the system tree, unifying access.

From File System Implementation: Data, I saw that a filesystem uses disk blocks for data, inodes, bitmaps, and a superblock (global info). Inodes store file type, size, and pointers—direct, indirect, or double indirect—to data blocks, enabling large files. Directories are just files containing (name, inode) entries.

From File System Implementation: Access, I learned how to traverse the directory tree to access /foo/bar: starting at the root inode, reading directory blocks to find each component, then retrieving the file’s inode and data blocks. The VSFS simulator showed how operations like mkdir(), creat(), link(), and unlink() change inodes and bitmaps. For example, creating a hard link adds another directory entry to the same inode without duplicating data.

Monday, August 4, 2025

Week 6

 Week 6 (7/30-8/5)

In this week’s readings, PA5, lectures, and quizzes, I learned how condition variables and semaphores are used to manage synchronization in multi-threaded programs. As an example of this, I learned that a condition variable lets threads wait efficiently for some condition to become true, like waiting for a buffer to be non-empty before reading. The Anderson/Dahlin method showed how to design shared objects safely by first building basic object-oriented code and then adding one lock, condition variables, and wait/signal logic in a step-by-step way. This helped me understand how to write safer and more precise threading code, such as in a simple producer-consumer buffer.

I also learned how semaphores can be used for the same types of problems, like enforcing mutual exclusion or synchronizing multiple threads at a barrier. Unlike condition variables, semaphores are more powerful but also more complex—they combine locking and signaling in one tool. For example, to solve the rendezvous problem (where threads must wait for each other), I saw how to use two semaphores to make sure both threads complete the first step before moving on. In the synchronization barrier problem, I learned that we can count how many threads have arrived and use a turnstile pattern (wait then signal) to let all threads through. These examples made abstract ideas more concrete for me.

This week's concepts helped me understand the overall idea of concurrency, especially how to coordinate and manage access to shared resources safely and correctly, which is a key challenge in modern operating systems.

Tuesday, July 29, 2025

Week 5

 Week 5 (7/23-7/29)

This week, I learned in OS that concurrency means letting different parts of a program make progress “together.” I also learned that threads are light‑weight units of execution inside a process, which share memory but each has its own stack and registers. For example, two threads updating a shared counter can cause problems if they both do counter = counter + 1 at the same time.

I also learned that a race condition happens when threads access shared data without coordination, and the result depends on timing. I learned that a critical section is the code that touches shared data, and that a mutex lock ensures only one thread enters at a time. I learned that locks rely on atomic instructions like test‑and‑set to flip a flag in one step without interference.

Another thing I learned about the pthreads API: pthread_create lets me start a thread, pthread_join waits for it to finish, pthread_mutex_lock and pthread_mutex_unlock guard critical sections, and pthread_cond_wait and pthread_cond_signal let threads sleep and wake up based on conditions.

I also learned that I can make a simple counter safe by putting a mutex inside a struct and locking it around increment and get operations. The coarse‑grained locks are easy to write but slow down all threads, while fine‑grained locks let more threads work in parallel but are harder to code.

I finally learned that concurrency helps in real programs like browsers, where one thread loads images while I type in another. I learned that balancing safety and speed means choosing the right locking level for your data structures.

Monday, July 21, 2025

Week 4

 Week 4 (7/16-7/22)

This week's content was very interesting; the labs were particularly helpful in simulating the methods. I learned several memory management methods used by operating systems: paging, Translation Lookaside Buffers (TLBs), multi-level paging, and swapping.

Paging splits virtual memory into fixed-size blocks called pages. It avoids memory fragmentation and simplifies memory allocation. Each virtual address has a virtual page number (VPN) and an offset. The page table translates VPNs into physical page numbers (PFNs). However, paging can be slow and needs large tables, making it less efficient.

To speed things up, a Translation Lookaside Buffer (TLB) caches recent VPN to PFN translations. This cache exploits the fact that programs often reuse the same memory areas repeatedly, a phenomenon known as locality. Using a TLB significantly reduces the average memory access time.

Multi-level paging solves the issue of large page tables. It breaks the page table into smaller chunks, storing only necessary parts. A page directory points to these smaller page tables. While this saves memory space, it makes address translation slightly more complex.

Swapping happens when there's not enough physical memory (RAM). Less-used pages are temporarily moved from RAM to disk. When a needed page isn't in memory, a page fault occurs, prompting the OS to bring the page back from disk. Different policies, such as Least Recently Used (LRU), First-In-First-Out (FIFO), Random, or Optimal (Belady’s policy), determine which pages to swap out. I personally struggled more with Belady's policy, so I had to practice that specific method a bit more to get it right. These methods help manage memory efficiently, balancing speed and resource usage.

Saturday, July 12, 2025

Week 3

 Week 3 (7/9-7/15)

What did I learn this week in CST 334?

I learned different concepts this week. The book and lectures teach how computers use virtual memory to let each program act like it has its own private memory, even though all programs share the same physical RAM. This is done through a system of address translation, where virtual addresses (used by programs) are converted into physical addresses (used by the hardware).  One method is base-and-bounds, where each process is assigned a block of memory, and hardware checks ensure it stays within limits. A more flexible method is segmentation, which breaks memory into parts like code, stack, and heap with their own base and size, allowing for better protection and organization.

The book, lecture videos, and assignment for this week include simulations showing how virtual addresses are split and mapped to physical memory, making concepts like translation clearer. They also explain address spaces, which are the ranges of addresses a process can use. Each process has its own address space to prevent interference. To manage memory efficiently, the operating system uses free space management techniques. These include first fit (choosing the first block big enough), best fit (choosing the smallest block that fits), and worst fit (choosing the largest block to avoid leaving tiny gaps). These strategies help reduce wasted memory and prevent fragmentation, when free memory is split into unusable pieces. The chapters and assignments also discuss how malloc() and free() in C are used to request and release memory manually, and the risks of doing so incorrectly, such as memory leaks. Overall, this week's content builds a strong foundation for understanding how the OS manages memory safely, efficiently, and fairly among processes.


Monday, July 7, 2025

Week 2

 Week 2 (7/2/2025-7/8/25)

What did I learn this week at CST334:

The major tools I learned this week in OS are processes, limited directed execution, CPU scheduling, MLFQ, and C Process API. I learned that limited direct execution is used to ensure efficient multitasking. It lets user programs run directly on the CPU, but in a safe way using user mode. The OS uses traps and interrupts to regain control when needed, such as during I/O or when a timer expires. This allows the OS to safely switch between processes, a process known as context switching.

I also learned that processes are created and managed by the OS using tools like fork() (to create a new process), exec() (to replace the process code), and wait() (to wait for a child process to finish). Each process has its own state and memory.

I also studied different ways the OS schedules which process runs next. CPU scheduling policies determine the order in which processes are executed. First-Come, First-Served (FCFS) is simple but can lead to poor performance. Shortest Job First (SJF) minimizes average turnaround time if job lengths are known. Shortest Time to Completion First (STCF) is a preemptive version of SJF. Round Robin (RR) assigns each process a short time slice in turn, improving response time but potentially increasing turnaround time.

Finally, I learned about Multi-Level Feedback Queue (MLFQ), which adjusts process priorities over time. Short or interactive jobs stay at a higher priority, while longer CPU-bound jobs move lower. To prevent starvation, MLFQ boosts all jobs back to high priority from time to time.

Week 8

 Week 8 (8/13-8/15) This week I want to make a reflection of all the topics and challenges faced throughout these 8 weeks, since in my last ...