SEARCH RESULTS
88 results found with an empty search
- Understanding C++ Data Types, Vulnerabilities, and Key Differences with Java
This article provides an in-depth look at the various data types in C++, including primitive, derived, and user-defined types, while also addressing common vulnerabilities such as buffer overflows and incorrect type conversions. Additionally, it highlights the key differences between C++ and Java, focusing on how each language handles data types and memory management, with practical code examples for secure programming. Alexander S. Ricciardi October 10, 2024 Java and C++ are two Object Oriented Programming (OOP) languages that have benefits and disadvantages. This article explores some of the differences between the two languages, with a focus on C++ data types and vulnerability. As well as, code example on As well as, code examples on how to prevent common issues such as buffer overflows, integer overflows, incorrect type conversions, and null pointer dereferencing in C++. C++ Data Types In C++, there are broadly three types of data: Primitive (or Built-in) Data Types, Derived Data Types, and User-defined Data Types, see Figure 1: Figure 1 C/C++ Data Types Note: From “C++ Data Types” by Argarwal (2023) - The Primitive types also refer as fundamental types are the basic data types that are predefined in C++, see below for a list of them: Integer (int) : Stores whole numbers, usually require 4 bytes of memory, and have a range from -2,147,483,648 to 2,147,483,647 (Argarwal, 2023) Table 1 C++ Integers Note: From “Fundamental Types” by C++ Reference (n.d.) Character (char) : Stores character, require, 1 byte, and has a range of -128 to 127 (or 0 to 255 for unsigned char). Note they are technically positive integers storing characters as integers using their underlying ASCII codes. Wide Character (wchar_t) stores wide characters, usually requiring 2 or 4 bytes depending on the platform. Boolean (bool) : Stores logical values, true or false, usually requires 1 byte. They are technically positive integers, where O is true all other positive integers is false. Floating Point (float) : Stores single-precision floating-point numbers, usually requiring 4 bytes. Double (double): Stores double-precision floating-point numbers, requiring 8 bytes long double — extended precision floating-point type. Does not necessarily map to types mandated by IEEE-754. (C++ Reference , n.d., p.1) Void (void) : type with an empty set of values or absence of value, often used in functions that do not return any value. “ type with an empty set of values. It is an incomplete typeLinks to an external site. that cannot be completed (consequently, objects of type void are disallowed). There are no arraysLinks to an external site. of void, nor referencesLinks to an external site. to void. However, pointers to voidLinks to an external site. and functionsLinks to an external site. returning type void ( procedures in other languages) are permitted.” (C++ Reference , n.d., p.1) - Derived data types are derived from primitive data types, and they include: Array: collection of elements of the same type. Pointer: memory addresses of variables. Function: block of code that performs a specific task. Reference: alias to another variable. (Argarwal, 2023) - User-defined Data Types, as the name says are defined by the user Class: stores variables and functions into a single unit. Structure (struct): Similar to classes but with public default access. Union: Stores different data types in the same memory location. Enumeration (enum): Sets of names associated with an integer constant, uses extensively in the video game industry. Typedef: Allown to create or assign new names for existing data types. Main Differences Between Models: C++ vs Java Before listing the differences between the C++ and Java data types, I would like to discuss the main differences and similarities between the two languages. Both languages are Object Oriented Programming (OOP) languages. However, However, C++ is platform-dependent, while Java is platform-independent due to Java's Virtual Machine (JVM) component that compiles Java to bytecode using an interpreter, which can run on any system with a JVM, this is referred to as "Write Once, Run Anywhere" capability (Eck, 2015). Additionally, C++ supports both procedural and object-oriented programming, whereas Java is strictly OPP, making C++ more suitable for programming operating systems. Furthermore, Java, being strictly object-oriented, lacks support for global variables or free functions (as C++ does). Java encapsulates everything within classes. Data Types Differences Between C++ and Java Below is a list of data type differences between C++ and Java. Both languages have primitive data types; however, C++ supports both signed and unsigned types, making C++ primitive data types more flexible but also less secure when unsigned. Java primitive data types are signed by default (Eck, 2015). Java primitive data types have fixed sizes and are platform-independent, whereas C++ primitive data types storage sizes are platform-dependent. C++ provides consistency between primitive and object types. Java also distinguishes between primitives and objects; however, it has "wrapper classes" for primitive types like Integer for int, allowing them to have object-like behavior (Sruthy, 2024). Java does not support structure or union data types, whereas C++ does. C++ supports pointers and reference types, whereas Java has very limited support. This is by choice and for security reasons. C++ variables have a global scope as well as namespace scope. On the other hand, Java variables have no global scope; however, they can have package scope. Java supports documentation comments ‘Javadocs’, whereas C++ does not. Possible Vulnerabilities When Using C++ Data Types If not handled right C++ data types may create security issues. Thus, it is crucial to understand and identify C++ data type vulnerabilities. Below is a list of the most common vulnerabilities that may arise with handling C++ data types and on how to mitigate them. 1. Buffer Overflows Buffer overflow occurs when the data written in memory surpasses a buffer-allocated capacity. This can lead to program crashes or malicious code execution.To prevent buffer overflow, it is important to check bounds before coding, and use library functions resistant to buffer overflow like “fgets.” Refrain from using “scanf,” “strcpy,” “printf,” “get,” and “strcaf,” which are prone to buffer overflows (GuardRails, 2023). See the code example below. #include #include int main() { char buffer[10]; std::cout << "Enter a string: "; fgets(buffer, sizeof(buffer), stdin); // Avoids buffer overflow by // limiting input std::cout << "You entered: " << buffer << std::endl; return 0; } 2. Integer Overflow and Underflow Integer overflow occurs when a trying to store in an integer variable exceeds the maximum value an integer can hold (Snyk Security Research Team, 2022). Integer underflow occurs when a value becomes less than the minimum value an integer can hold. To prevent integer overflow and underflow always validate the values before performing arithmetic operations, see the code example below: #include #include // For INT_MAX int add(int a, int b) { if (a > 0 && b > INT_MAX - a) { std::cerr << "Integer overflow detected!" << std::endl; return -1; } return a + b; } int main() { int x = INT_MAX - 1; int y = 2; std::cout << "Result: " << add(x, y) << std::endl; return 0; } Note that before adding a and b, in the add function, the condition of adding them would cause an overflow is checked, here the sum is greater than the integer max value allowed. 3. Incorrect Type Conversion Incorrect type conversions often happen when converting a signed integer to an unsigned integer leading to unintentional data loss. This can be avoided by Not-implicitly converting data type, for example, avoid casting double to float or a signed to an unsigned type. See the code example below for example: #include void checkLength(int len) { if (len < 0) { std::cerr << "Negative length detected!" << std::endl; return; } std::cout << "Length is: " << len << std::endl; } int main() { // Incorrect implicit conversion unsigned int value = -5; // unsigned int are always non-negative checkLength(static_cast(value)); // Properly handle conversion return 0; } 4. Pointer initialization Null pointers are a well-known vulnerability that can lead to program crashing and potential nefarious exploitation. Always check pointers for ‘nullptr’ before dereferencing them. See the code below for an example. struct list { void *p; struct list *next; }; int f(struct list *l) { int max = -1; while (l) { int i = *((int*)l->p); // Cast void* to int* max = i > max ? i : max; l = l->next; } return max; } Note that the 'void ’ is used to store a generic pointer in the linked list and it is cast to ‘int ’ before being used. To summarize, understanding the difference between Java and C++, by understanding C++ data types, along with their associated risks, is critical for writing secure programs. This can be done by carefully managing data types, performing bounds checks, validating type conversions, and handling pointers safely, such as by checking for null values and casting void pointers correctly. References: Adam, J. & Kell, S. (2020). Type checking beyond type checkers, via slice & run. In Proceedings of the 11th ACM SIGPLAN International Workshop on Tools for Automatic Program Analysis (TAPAS 2020). Association for Computing Machinery, 23–29. Retrieved from: https://dl-acm-org.csuglobal.idm.oclc.org/doi/10.1145/3427764.3428324 Argarwal, H. (2023, September 23). C++ data types. GoogsforGeeks. https://www.geeksforgeeks.org/cpp-data-types/ C++ Reference (n.d.). Fundamental types . cppreference.com . https://en.cppreference.com/w/cpp/language/types/ Eck, D. J. (2015). Chapter 1 Overview: The mental landscape. Introduction to programming using Java (7th ed.). CC BY-NC-SA 3.0. http://math.hws.edu/javanotes/ GuardRails, (2023, April 27). The Top C++ security vulnerabilities and how to mitigate them. Security Boulevard. https://securityboulevard.com/2023/04/the-top-c-security-vulnerabilities-and-how-to-mitigate-them/ Snyk Security Research Team (2022, August 16). Snyk. https://snyk.io/blog/top-5-c-security-risks/ Sruthy, (2024, March 7). C++ vs Java: Top 30 differences between C++ and Java with examples. Software Testing Help. https://www.softwaretestinghelp.com/cpp-vs-java/
- Java Selection Structures: if, if-else, if-else-if Ladder, and Switch
This article discusses the use of selection structures in Java, such as if, if-else, if-else-if ladder, and switch, which control the flow of execution based on specific conditions. Alexander S. Ricciardi April 29, 2024 In Java and similar object-oriented languages, selection structures are used to control the flow of execution and they are based on conditions. The primary selection structures in Java are if, if-else, if-else-if ladder, and switch. Each serves different purposes depending on the conditions to be evaluated and the functionality needed. Here are some examples: if statement: “The if statement in Java allows you to execute a block of code only if a given condition is true. If the condition is false, the code block is skipped” (Jassal, 2023). Example: int age = 20; if (age >= 18) { System.out.println("You are eligible to vote."); } if-else statement: “The if-else statement in Java allows you to execute one block of code if the condition is true, and another block if the condition is false” (Jassal, 2023). Example: int score = 75; if (score >= 60) { System.out.println("Pass"); } else { System.out.println("Fail"); } if-else-if ladder: “The if-else statement in Java allows you to execute one block of code if the condition is true, and another block if the condition is false” (Jassal, 2023). Example: int marks = 85; if (marks < 50) { System.out.println("Fail"); } else if (marks >= 50 && marks < 60) { System.out.println("D Grade"); } else if (marks >= 60 && marks < 70) { System.out.println("C Grade"); } else if (marks >= 70 && marks < 80) { System.out.println("B Grade"); } else { System.out.println("A Grade"); } switch statement: Java has a built-in switch statement, which allows you to perform multi-way branching based on the value of an expression (Jassal, 2023). Example: int day = 3; switch (day) { case 1: System.out.println("Monday"); break; case 2: System.out.println("Tuesday"); break; case 3: System.out.println("Wednesday"); break; default: System.out.println("Another day"); } When to Use Each Structure: if statement : Opt for this when you need to check a single condition. if-else statement : Use this for straightforward yes/no decisions. if-else-if ladder : Best suited for checking multiple conditions. switch statement : Ideal for checking one variable against various constants. This simplifies the code and makes it more readable. To summarize, the selection structures in Java such as if, if-else, if-else-if ladder, and switch allow developers to control the flow of execution based on various conditions. Each structure has specific use cases that help in writing clear and efficient code depending on the problem needing to be solved. References: Jassal, S. (2023, August 7). Java selection statements: Making smart decisions in your code . LinkedIn. https://www.linkedin.com/pulse/javas-selection-statements-making-smart-decisions-your-satish-jassal/
- Understanding Rasterization in 2D and 3D Image Rendering
This article explains the rasterization process, which converts geometric data into pixels for rendering 2D and 3D images in computer graphics. It covers the key steps, including modeling, geometry processing, and fragment processing, highlighting how pixels are assigned position, color, and depth before being displayed. Alexander S. Ricciardi September 30, 2024 In computer graphics, understanding the rasterization process is essential to efficiently create applications that render 2D and 3D images. Rasterization transforms geometric data into pixels that are displayed usually on a screen. For example, it takes a simple geometric shape (e.g. a triangle) and transforms it into pixels with the position, color, and depth attributes. The process of rendering a geometric shape involves four essential tasks, modeling, geometry processing, rasterization, and fragment processing. These tasks are arranged in a pipeline with rasterization positioned at the center of this process, making it a critical stage that converts geometric data into pixels or fragment shaders, see Figure 1. Note that the process of rasterization includes rasterization itself or scan conversion and fragment processing, the processes where the rasterizer assigns the color and depth attribute to the pixels. Figure 1 The Five Tasks Note: From “Chapter 12: From Geometry to Pixels. Interactive Computer Graphics. 8th edition” by Angel & Shreiner (2020, p.299) The process of rationalizing usually involves three steps. Two pre-rasterization steps include defining the triangle(s) (modeling) and performing transformations and clipping (Geometry Processing), and the third step is rasterization itself. Step-1 Modeling. In computer graphics, polygons are primitive shapes that are used to model other objects. WebGL uses triangles, being the simplest polygon it can be used to model other polygons. A triangle can be defined in 2D space by three vertexes v ₁ (x ₁ , y ₁ ), v ₂ (x ₂ , y ₂ ), and v ₃ (x ₃ , y ₃ ) representing the corner of the triangle. Step-2 Geometry Processing. This step often includes projection (to move from 3D to 2D in perspective or orthographic views) and clipping (to remove parts of the triangle that are outside the viewing volume). Note that the x and y values are used for realization and the z values are stored for hidden surface removal. Step 3: Rasterization. Rasterization, also called scan conversion, is the process that determines which pixels (also called fragments) on the screen correspond to the triangle's interior. After identifying the triangle's pixels, fragment processing is performed, where the rasterizer applies z-buffering (depth testing) to handle overlapping objects in the 3D scene. Finally, each pixel is assigned a color and sent to the framebuffer, which stores the final image before being displayed on a screen. To summarize, understanding the rasterization process is essential to efficiently create applications that render 2D and 3D images. Rasterization transforms pre-processed geometric data into pixels also called fragments by computing the positions, color, and depth attributes for each pixel before sending it to the framebuffer to be displayed. References: Angel, E., & Shreiner, D. (2020). Chapter 12: From geometry to pixels. Interactive computer graphics. 8th edition. Pearson Education, Inc. ISBN: 9780135258262
- Light Interaction in Computer Graphics: Reflection and the Blinn-Phong Model - OpenGL
This article explains the role of reflection in computer graphics, focusing on how light interacts with different surface types, such as specular, diffuse, and translucent surfaces. It introduces the Blinn-Phong lighting model, which breaks down light reflection into ambient, diffuse, and specular components to simulate realistic lighting in digital scenes. Alexander S. Ricciardi September 21th, 2024 In computer graphics, reproducing how light interacts with object surfaces is essential for creating an accurate representation of the real world. This process is called reflection. It is where light bounces off objects’ surfaces determining how objects are perceived. In the real world, reflection is the process of light bouncing off a surface. The angle at which light reflects off the surface of an object determines how the object’s surface appears once it reaches the eye or a camera sensor. The surfaces can be grouped into three main types, which are specular surface, diffuse surface, and translucent surface, see Figure 1. Figure 1 Light–material Interactions Note : The arrow represents the direction of the light rays. From “Chapter 6: Lighting and Shading. Interactive Computer Graphics. 8th edition” by Angel and Shreiner (2020). Modify. Specular surfaces appear shiny due to the light being reflected in a specific direction making the surface appear shiny. Diffuse surfaces reflect light by scattering in all directions. This makes the surface appear matte and uniform. Translucent surfaces allow some light to pass through it. This makes the surface appear partially transparent, lightly distorting the light and giving the surface a soft glow or blurred effect. In computer graphics, reflection is computed using lighting models that simulate how light interacts with surfaces. The most common model is the Blinn-Phong Model, which breaks down reflection into three components based on the real-world diffuse and surface types. The three components are specular reflection, reflection, and diffuse reflection. In computer graphics, this component can be integrated into a 3x3 illumination matrix: The first row of the matrix contains the ambient intensities for the red, green, and blue terms from source i . The second row contains the diffuse terms. The third contains the specular terms. (Angel & Shreiner, 2020, p163) Figure 2 Phong Reflection Note: From "OpenGL - lighting with the Phong reflection model (part 1 of 2)" by Will (2019) Note that: Ambient reflection represents indirect lighting, bouncing light, or general brightness. Specular reflection is related to the specular surfaces where the light is reflected in a specific direction, resulting in the surface looking shiny. Diffuse reflection is related to the diffuse surfaces where the light is scattering in all directions. A homogenous vector derived from the matrix illumination matrix can be used to compute the lighting in a scene.For example, when using WebGL the code could look as follows: Vertex Shader GLSL uniform vec4 uAmbientProduct, uDiffuseProduct, uSpecularProduct; varying vec4 vColor; // Compute ambient component vec4 ambient = uAmbientProduct; // Compute diffuse component using Lambert's cosine law float diffuseFactor = max(dot(normal, lightDirection), 0.0); vec4 diffuse = diffuseFactor * uDiffuseProduct; // Compute specular component using the Blinn-Phong model vec3 halfwayDir = normalize(lightDirection + viewDirection); float specularFactor = pow(max(dot(normal, halfwayDir), 0.0), uShininess); To summarize, reflection is the process where light bounces off objects’ surfaces determining how objects are perceived. In computer graphics, reflection is computed using lighting models like the Blinn-Phong model, which breaks down light interactions into ambient, diffuse, and specular components. This process is essential for reproducing an accurate representation of the real world. References: Angel, E., & Shreiner, D. (2020). Chapter 6: Light and Shading. Interactive computer graphics. 8th edition . Pearson Education, Inc. ISBN: 9780135258262 Will, B. (2019, September 9). OpenGL - lighting with the Phong reflection model (part 1 of 2) [Video]. YouTube. https://www.youtube.com/watch?v=lH61rdpJ5v8
- Orthographic vs. Perspective Projection: Key Differences and Applications
This article explains the key differences between orthographic and perspective projection, highlighting how orthographic projection preserves the true dimensions of objects without distortion, while perspective projection introduces perspective distortion as objects move further from the Center of Projection. It emphasizes the importance of orthographic projection in fields like technical drawing, architecture, and CAD, where accurate representation of object dimensions is essential. Alexander S. Ricciardi September 15th, 2024 Orthographic projection is a type of parallel projection. Parallel projection is a projection where rays (or projectors are parallel whereas in Perspective projection, the rays converge towards a single point (the camera or eye). Meanwhile, Orthographic projection is when objects are projected perpendicularly onto the viewing plane, maintaining their dimensions. Multiple differences exist between the two projections see Table 1; however, the key difference between Orthographic and Perspective projections is that in Orthographic projection, the size and shape of objects remain consistent on the projection plane without perspective distortion (Adware, 2020). Perspective distortion occurs in a Perspective projection as an object gets further away from the Center of Projection (COP) distortion the shape of the object. Figure 1 illustrates the two projections. Figure 1 COP vs DOP Note: The Center of Projection (COP) represents a Perspective Projection, and the Direction of Projection (DOP) represents the Parallel Projection. From Chapter 5: Viewing. Interactive computer graphics. 8th edition, p134, by Angel and Shreiner (2020). Table 1 Difference Between Parallel Projection and Perspective Projection Note: From “Difference between Parallel and Perspective Projection in Computer Graphics” by Aware (2020). As listed in the table above Orthographic projection is one of the two types of Parallel projection, and it is used to represent 3D objects in two dimensions by projecting them orthogonally onto a viewing plane. It is commonly used in drawings, architectural designs, and Computer-Aided Design (CAD) for 3D modeling, where preserving the true dimensions of objects without introducing perspective distortion is crucial. By preserving the size of the object without distortion, orthographic projection offers clear advantages for the fields listed above.In drawings, it allows for the accurate representation of objects’ dimensions, bringing clarity and a better understanding of the object's shape, which is essential for technical drawings.In architectural design, Orthographic projection preserves the true dimensions of building elements such as walls, windows, and doors, ensuring that these dimensions are accurately communicated to builders and that the final construction reflects the architect(s)'s intent. In Computer-Aided Design (CAD) for 3D modeling, Orthographic projection prevents perspective distortion of the object dimensions allowing 3D prevents perspective distortion, allowing 3D models to retain the objects' true dimensions when scaled up or down, making the models easily transferable for use in simulations and manufacturing. To summarize, Orthographic projection is a type of Parallel projection, where an object is projected orthogonally onto a viewing plane. The key difference between Orthographic projection and Perspective projection is that Orthographic projection preserves the size and shape of objects without distortion, while perspective projection does not. Orthographic projection is essential for fields such as drawings, architectural designs, and CAD for 3D modeling, where preserving the true dimensions of objects without introducing distortions is crucial. References: Adware (2020, May 24). Difference between parallel and perspective projection in computer graphics . GeeksforGeeks. https://www.geeksforgeeks.org/difference-between-parallel-and-perspective-projection-in-computer-graphics/ Angel, E., & Shreiner, D. (2020). Chapter 5: Viewing. Interactive computer graphics. 8th edition . Pearson Education, Inc. ISBN: 9780135258262
- Key Benefits of Virtual Machines in Modern Network Environments
This article explores the role of virtual machines (VMs) in modern network environments, highlighting their benefits such as efficient resource utilization, enhanced security, and scalability. It also compares VMs with bare metal and container technologies to provide a deeper understanding of their advantages and use cases. Alexander S. Ricciardi August 1st, 2024 In today's network environment, virtual machines (VMs) or virtualization play a substantial role by providing a wide range of benefits. Such as Efficient Resource Utilization, Security and Isolation, Ideal for Development and Testing, Disaster Recovery, Scalability and Flexibility, Support for Legacy Applications, and Cloud Compatibility. Before listing the benefits offered by virtualization, let to define it in the context of today's network environment. Virtualization (VM) is a technology that allows system hardware to be shared between multiple virtual environments (Amazon, n.d.). In other words, hardware can be shared by multiple virtual machines, allowing these machines to operate simultaneously on a physical system. In today's network environment is mostly related to server virtualization but is not limited to it, it can also refer to the virtualization of network equipment such as routers and switches, and personal computers. For example, you can rent a gaming pc with your desired specs on a cloud base service like Google Stadia, Nvidia's GeForce Now, or Microsoft's xCloud, allowing you to enjoy high-performance gaming without the need for owning hardware. Additionally, most modern application stacks (or tech stack) are developed to work over a network (LAN, WAN, Cloud). “An application stack is simply a combination of technologies (programming languages, libraries, tools, and services) that are used together to build, deploy, and maintain an application” (D3V Technology Solutions, 2021). Modern application stacks can be deployed using three different technologies, Bare Metal, Virtual Machines, and Containers (ByteByteGo, 2022). To understand the benefit provided by VM in today's network environment, it is important to understand the difference between Bare Metal, VM, and Containers. Figure 1 depicts bare metal, virtualized, and containerized systems. Figure 1 Bare metal, VMs, and containers Note. From Big misconceptions about bare metal, virtual machines, and containers, by ByteByteGo, 2021, ( https://www.youtube.com/watch?v=Jz8Gs4UHTO8&t=11s.Links to an external site. ). Copyright 2023 by yuotube.com . Bare Metal is not to be confused with bare metal hypervisor. It is just a physical computer that is a single tenant only. All servers used to be bare metal. Bare metal gives complete control over the hardware resources. The advantages are the highest performance from the hardware and isolation. Isolation provides security, and it is a solution to the noisy neighbor problem. The noisy neighbor problem occurs when one tenant's performance is impacted because of the activities of another tenant sharing the same hardware. The downsides are cost, hard to manage, and hard to scale. VM allows for multiple operating systems to run on a single physical (bare metal) server through the use of a hypervisor, a software layer that abstracts the hardware. Each VM has its own guest operating system and applications. Some systems employ a bare metal hypervisor that directly controls the hardware, offering enhanced performance but requiring more expensive supporting hardware. Additionally, VMs provide benefits such as cost-efficiency, as many VMs can run on the same hardware. They are also scalable, and sophisticated software can even transfer a running VM from one physical server to another without shutting down the VM. However, VMs can be affected by the "noisy neighbor" problem, where a VM's performance suffers due to high resource usage by another VM on the same hardware. VMs also have security risks, such as side-channel attacks like Meltdown and Spectre. Containers are a lightweight form of virtualization that encapsulates an application along with its dependencies, enabling it to run anywhere. Rather than using a hypervisor to virtualize hardware, containers virtualize the operating system via a container engine that oversees many containers, and each container operates as an independent environment. Containers offer several advantages: they're faster to provision resources, more scalable, and more portable. They're also lightweight, allowing a bare metal server to host more containers than VMs. Because they run as native processes of the host operating system, they also start more quickly. However, containers have potential security drawbacks. They all share the same operating system. This exposes them to a broader class of security vulnerabilities at the OS level. Below is a more specific list of VM’s benefits in today's network environment. Efficient Resource Utilization: By allowing multiple VMs to run on a single physical system, organizations can make optimal use of their hardware resources. This improves efficiency and cost savings. Security and Isolation: VMs operate in isolation from one another, even when running on the same physical host. For example, if a VM encounters an issue, such as a system crash or a security breach, the problem is confined to that VM and doesn't impact the others. Ideal for Development and Testing: VMs offer an excellent environment for testing and development. They can be created, used, and deleted without any impact on the primary operating system. Disaster Recovery: VMs can be easily replicated and restored in the event of a disaster. They can also be migrated between different physical machines or locations, providing support for business continuity and disaster recovery. Scalability and Flexibility: VMs are easy to create, delete, or migrate, they allow for a highly flexible and scalable IT infrastructure. Support for Legacy Applications: VMs can run various operating systems, which makes them ideal for running older applications that may not be compatible with newer systems. Cloud Compatibility: As the fundamental building blocks of cloud environments, VMs play a critical role in the migration of workloads to the cloud, facilitating a range of benefits, including scalability and cost. Ref: Carklin (2021) and Sheldon & Kirsch (2023) Finally, VM plays an essential role in today's network environment. They ensure efficient utilization of resources, they provide enhanced security and isolation, they are ideal for development and testing, they allow disaster recovery, they are flexible and scalable, they provide support for legacy applications, and they are cloud compatible. References: Amazon. (n.d.). What is virtualization? Amazon.com . https://aws.amazon.com/what-is/virtualization/#:~:text=Virtualization%20is%20a%20process%20that,%2C%20processing%20power%2C%20and%20storage / Carklin, N. (2021, June 1). What are the benefits of virtual machines?. Parallels Remote Application Server Blog - Application virtualization, mobility and VDI. https://www.parallels.com/blogs/ras/benefits-virtual-machines/ ByteByteGo. (2022, July 14). Big misconceptions about bare metal, virtual machines, and containers [Video][Figure 1]. YouTube. https://www.youtube.com/watch?v=Jz8Gs4UHTO8&t=11s D3V Technology Solutions (2021, November 24). How to achieve a modernized APP STACK: Cloud insights. D3vtech.com . https://www.d3vtech.com/insights/how-to-achieve-a-modernized-app-stack#:~:text=An%20app%20stack%20(or%20tech,characteristics)%20of%20the%20ideal%20architecture / Sheldon, R., & Kirsch, B. (2023, May 1). What is a virtual machine and how does it work? . IT Operations. https://www.techtarget.com/searchitoperations/definition/virtual-machine-VM?Offer=abt_pubpro_AI-Insider /
- Sorting Algorithms: Comparison and Implementation - Java
This article provides an overview of different sorting algorithms, focusing on both comparative and non-comparative methods. It includes practical examples of Quick Sort using Lomuto and Hoare partition schemes, highlighting their efficiency and use cases in various applications. Alexander S. Ricciardi September 17th, 2024 In computing science, different types of sorting algorithms are used, see Table 1 and Table 2. They are a fundamental component for organizing data, which is often a prerequisite step before the data can be processed, searched, or optimized for algorithms in various applications uses. Table 1 Comparative Sorting Algorithms Note: In Java comparative sorting of both primitive data (via algorithms) and user-defined data is implemented through the Comparable or Comparator interfaces. From “Sorting Cheat Sheet” by evanescesn09 (2019). Modify. Table 2 Non-Comparative Sorting Algorithms Below is an example of implementing a Quick Sort algorithm. This algorithm is often implemented by E-commerce businesses to filter products by price, viewing items from the lowest to highest cost or vice versa. Quick Sort is well suited for this kind of scenario, especially when dealing with a large number of products. Figure 1 Quick Sort Note: From “Data Structures and Algorithms Cheat Sheet” by Elhannat (n.d.) Modify. The code below is an implementation in Java of a Quick Sort algorithm sorting prices in ascending order. QuickSortPrices.java /** * Implements a QuickSort algorithm for sorting an array of prices (doubles) */ public class QuickSortPrices { // ---------------------------------------------------------------------------- /*--------------------- | Partition Method | ---------------------*/ /** * Divides the array into two parts: one part with elements smaller than or * equal to the pivot and another with elements larger than the pivot elements * smaller than or equal to the pivot are moved to the left of elements larger * than the pivot are moved to the right * * @param prices The array of doubles to be partitioned * @param low The starting index of the portion of the array to be * partitioned * @param high The ending index of the portion of the array to be * partitioned. * @return The index of the pivot element after partitioning */ public static int partition(double[] prices, int low, int high) { // The last element in the array becomes the pivot double pivot = prices[high]; // i keeps track of the index where smaller elements than the pivot should go // set to low - 1, which means no smaller element has been found yet int i = low - 1; // Iterate from the 'low' index to 'high - 1' (excluding the pivot) for (int j = low; j < high; j++) { // If smaller than or equal to the pivot, it is moved // to the part of the array that holds smaller elements if (prices[j] <= pivot) { i++; // Swap prices[i] and prices[j] double temp = prices[i]; prices[i] = prices[j]; prices[j] = temp; } } // Swap the pivot element (prices[high]) with the element at i+1 double temp = prices[i + 1]; prices[i + 1] = prices[high]; prices[high] = temp; // Return the index of the pivot element. This index is used to divide the array // into two parts to implement further recursive sort return i + 1; } // ---------------------------------------------------------------------------- /*------------------------ | QuickSort algorithm | ------------------------*/ /** * QuickSort algorithm is a recursive method that sorts an array by dividing * using a pivot * * @param prices The array of doubles to be sorted * @param low The starting index of the portion of the array to be sorted * @param high The ending index of the portion of the array to be sorted */ public static void quickSort(double[] prices, int low, int high) { int pivotIndex; // Stores the pivot index // Check if the portion if low is less than high if (low < high) { // ----- Base case low is not < than high ------ // ----- Recursive Base ----- // Divides the array around a pivot // Returns the index of the pivot element pivotIndex = partition(prices, low, high); // --- Recursion call to < than the pivot // Recursive call to a subarray of elements that are smaller than th // pivot quickSort(prices, low, pivotIndex - 1); // Once low is not < than high, // it exists and the next // recursion call to > than // the pivot // --- Recursion call to > than the pivot // Recursively apply quicksort on the subarray of elements that are // larger than the pivot quickSort(prices, pivotIndex + 1, high); } } // ---------------------------------------------------------------------------- } Main.java import java.util.Arrays; public class Main { public static void main(String[] args) { // Example of product prices double[] productPrices = { 199.99, 49.99, 349.95, 5.99, 20.00, 89.99, 150.00 }; System.out.println("\nProduct prices before sorting: " + Arrays.toString(productPrices)); QuickSortPrices.quickSort(productPrices, 0, productPrices.length - 1); System.out.println("Product prices after sorting: " + Arrays.toString(productPrices)); } } Output Note that two partition schemes can be used to partition before recursively sorting the Lomuto Partition scheme and the Hoare partition scheme. The example above uses the Lomuto Partition scheme. The scheme chooses the last element as the pivot, then uses a single (left or right) index to partition the array into elements less than or equal to the pivot and greater than the pivot. On the other hand, the Hoare partition scheme uses the first element of the segment as the pivot, then moves two indices towards each other until swap conditions are met, see code example below: import java.util.Arrays; public class Main { public static void quickSort(int[] array, int start, int end) { if (start < end) { int pivot = array[low]; // The pivot element starting index int left = low - 1; // Start left index just before the segment int right = high + 1; // Start right index just after the segment } } private static int partition(int[] array, int start, int end) { int pivot = array[start + (end - start) / 2]; int left = start - 1; int right = end + 1; while (true) { // Move the left pointer to the right as long as the elements are < than the pivot do { left++; } while (array[left] < pivot); // Move the right pointer to the left as long as the elements are > than the pivot do { right--; } while (array[right] > pivot); // If the two pointers meet, return the partition point if (left >= right) { return right; } // Swap elements that are out of order with respect to the pivot int temp = array[left]; array[left] = array[right]; array[right] = temp; } } public static void main(String[] args) { int[] data = {8, 7, 2, 1, 0, 9, 0}; System.out.println("Unsorted Array: " + Arrays.toString(data)); quickSort(data, 0, data.length - 1); System.out.println("Sorted Array: " + Arrays.toString(data)); } } In some aspects, this scheme is more efficient than Lomuto’s scheme, because it minimizes the number of swaps when compared to the Lomuto one. The first example using the Lomuto partition schema, on the other hand, chooses the last element as the pivot, then uses a single (left or right) index to partition the array into elements less than or equal to the pivot and greater than the pivot. The table below gives a detailed comparison of both schemes. Table 1 Hoare's vs Lomuto Partition Note: From "Hoare’s vs Lomuto partition scheme in QuickSort" by Educative (n.d.) Both partition schemes have a time complexity of O(n) because in both scenarios the array of elements needs to be traversed at least once. When adding the recursive aspect of the sort Quick Sort, both sorts have a time complexity of O(n log n). However, if the elements in the array were already sorted my Lomuto Quick Sort implementation will degrade to an O(n²) time complexity because it is using the last element in the array as a pivot. The Quick Sort with a Hoare partition will also have a time complexity O(n²) with a fully sorted array. Nonetheless, it will perform better than the Lomuto scheme with partially sorted arrays because instead of using a single index to swap it utilizes two indices towards each other until swap conditions are met To summarize, Quick Sort is efficient with large datasets, its time complexity is O(n log n) which is better than algorithms such as Bubble Sort or Selection Sort which have a time complexity of O(n²). Additionally, it operates in-place, meaning that it uses minimal memory O(1), which is crucial when handling large datasets. No one algorithm fits all applications. For example, in the shipping industry where linked lists are used, even though having the same time complexity as Quick Sort, O(n log n) , Merge Sort outperformed it. This is because Merge Sort is more suited for linked lists, as it doesn’t require random access to elements, unlike Quick Sort, which relies on indexes to access elements. Another example is small and nearly sorted data. In this senior insertion is better suited as its time complexity is in a best-case situation O(n) when the data is already sorted. However, in unsorted large data sets its worst-case time complexity is O(n ² ). In conclusion, in computing science, different types of sorting algorithms are used. No one algorithm fits all applications and the choice of which sorting algorithm to use depends on factors such as dataset size, data structure, and whether the data is already partially sorted. References: Elhannat, K. (n.d.). Data structures and slgorithms cheat sheet. Zero to Mastery. https://zerotomastery.io/cheatsheets/data-structures-and-algorithms-cheat-sheet/#contents evanescesn09 (2019, August 17). Sorting cheat sheet [PDF]. Cheatography. https://cheatography.com/evanescesn09/cheat-sheets/sorting/pdf/?last=1566081837
- Recursion in Programming: Techniques, Benefits, and Limitations - Java
This article explains the concept of recursion in programming, where a function calls itself to solve smaller instances of a problem, and its applications in tasks like tree traversal and divide-and-conquer algorithms. It also discusses the benefits of recursion for simplifying complex problems and improving code readability, while highlighting potential issues like stack overflow. Alexander S. Ricciardi September 12th, 2024 In computer science, recursion is an algorithmic technique in which a function calls itself to solve smaller instances of the same problem. In programming, recursion is an amazing technique with the help of which we can reduce the length of our code and make it easier to read and write” (GeeksforGeeks, 2024, p1). Recursion is used in programming to solve complex problems involving repetition and hierarchical structures such as tree traversals, graph algorithms, and divide-and-conquer problems like sorting and searching. The basic components found in recursive functions are base cases and recursive cases.A base case is a condition that, when met, ends the recursion process (Ricciardi, 2024). A recursive case is a set of code lines that are executed until a base condition is met. A classic example where recursion is well suited is in computing the factorial of a number. A factorial can be defined as a non-negative integer 𝑛, denoted 𝑛!, the product of all positive integers less than or equal to 𝑛: 𝑛! = 𝑛×(𝑛−1)! In Java: public class Factorial { public static int factorial(int n) { // --- Base case: factorial of 0 is 1 ---- if (n == 0) { return 1; // --- Recursive case --- } else { return n * factorial(n - 1); } } Note that the factorial() method calls itself until it reaches the base case where 𝑛 = 0 There are various benefits to using recursion. One of the biggest benefits of using recursion is that it allows programmers to easily break down complex problems into simpler, more manageable subproblems. This approach is often referred to as the divide-and-conquer approach, it is implemented in algorithms like mergesort, where recursion divides a complex sort problem into smaller problems leading to a more efficient sort solution than the linear sort iterating solution.Additionally, recursion helps with code readability by simplifying and shortening code lines. When using recursion, programmers can write problems involving repetition or hierarchical structures (trees) without the need to implement complex loops.Recursion also simplifies, and it is efficient at handling dynamic and random data structures such as linked lists and tree structures. For instance, when traversing a binary tree, using recursion simplifies the implementation of the process without the need to implement a stack. Although recursion has various advantages, in some scenarios using a stack is preferable over recursion. For example, recursion can generate a stack overflow error, ‘ StackOverflowError ’, if the recursive depth (number of recursion calls) becomes too large. This can happen in cases like deep tree traversals or depth-first search algorithms, where the number of recursion calls may exceed the system's call stack capacity. In Java, the limit of the call stack varies depending on the platform used and the Java Virtual Machine implemented. Java stack size can be configured using the JVM argument ‘ -Xss ’, for example ‘ java -Xss1M MyProgram ‘ where 1M is the size of the call back for MyProgram (Goodrich, Tamassia, & Goldwasser, 2023). It is best practice to use a stack or tail recursion, if possible, in this scenario. “A recursion is a tail recursion if any recursive call that is made from one context is the very last operation in that context, with the return value of the recursive call (if any) immediately returned by the enclosing recursion” (Goodrich, Tamassia, & Goldwasser, 2023, 5.5 Eliminating tail recursion). Note that while some programming languages optimize tail-recursive functions, Java does not. Thus, in Java, an optimized tail-recursive function needs to be implemented implicitly. Below are examples of implementing a depth-first search (DFS) traversal of a tree, using recursion with a possibility of ‘ StackOverflowError ’and a stack (Dequee) eliminating the possibility of a ‘ StackOverflowError ’: Using recursion possibility of ‘ StackOverflowError ’: public class DFS { // Node class static class Node { int value; Node left, right; // Constructor Node(int value) { this.value = value; left = right = null; // Left and right children are null initially } } // Recursive Depth-First Search (DFS) method public static void depthFirstSearchRecursive(Node node) { // --- Base case --- if (node == null) { return; } // Process the current node (visit) System.out.println(node.value); // Recursively traverse the left subtree depthFirstSearchRecursive(node.left); //--- Recursive case --- depthFirstSearchRecursive(node.right); /* Potential stack overflow issue: Each recursive call adds a new frame to the call stack. If the tree is too deep (e.g., with many levels), the recursion depth can exceed the system's maximum stack size, causing a StackOverflowError. */ } public static void main(String[] args) { // Create a binary tree Node root = new Node(1); root.left = new Node(2); root.right = new Node(3); root.left.left = new Node(4); root.left.right = new Node(5); System.out.println("DFS Traversal using Recursion:"); depthFirstSearchRecursive(root); } } Using the stack approach eliminating the possibility of a ‘ StackOverflowError ’: import java.util.ArrayDeque; import java.util.Deque; public class DFS { // Single node in the binary tree static class Node { int value; Node left, right; // Node Constructor Node(int value) { this.value = value; left = right = null; // Left and right children are null initially } } // Depth-First Search (DFS) traversal method public static void depthFirstSearch(Node root) { Deque stack = new ArrayDeque<>(); stack.push(root); // traverse the stack until is empty while (!stack.isEmpty()) { // Pop the top node from the stack Node current = stack.pop(); System.out.println(current.value); if (current.right != null) { stack.push(current.right); // Add right child to stack } if (current.left != null) { stack.push(current.left); // Add left child to stack } } } public static void main(String[] args) { // Create a binary tree Node root = new Node(1); root.left = new Node(2); root.right = new Node(3); root.left.left = new Node(4); root.left.right = new Node(5); System.out.println("DFS Traversal using Deque:"); depthFirstSearch(root); } } Output: DFS Traversal using Deque: 1 2 4 5 3 To summarize, recursion is a technique in which a function calls itself to solve smaller instances of the same problem, it is often used in problems like tree traversal, graph algorithms, and divide-and-conquer strategies. While recursion simplifies complex problems and code readability, excessive recursive calls can lead to stack overflow errors, particularly in deeply nested structures such as trees, making iterative approaches using explicit stacks preferable in certain cases. References: Arslan, Ş. (2023, February 25). A Comprehensive tree traversal guide in Javascript - General and binary tree traversals. Shinar Arslan Blog. https://www.sahinarslan.tech/posts/a-comprehensive-tree-traversal-guide-in-javascript-general-and-binary-tree-traversals GeeksforGeeks (2024, August 18). Introduction to recursion . GeeksforGeeks. https://www.geeksforgeeks.org/introduction-to-recursion-2/ Goodrich T, M., Tamassia, R., & Goldwasser H. M. (2023, June). Chapter 5: Algorithms recursion. Data structures and algorithms . zyBook ISBN: 979-8-203-40813-6.
- An Overview of RAID Storage: Levels, Performance, and Data Redundancy
This article provides a detailed explanation of RAID (Redundant Array of Independent Disks), a storage technology that merges multiple disk drives into a single unit to enhance data redundancy and performance. It covers various RAID levels, from RAID 0 to RAID 10, explaining their unique configurations, advantages, and ideal use cases based on speed, capacity, cost, and fault tolerance. Alexander S. Ricciardi July 18th, 2023 Redundant Array of Independent Disk or Random Array of Inexpensive Disks (RAID) is a storage technology that combines multiple physical disk drives into a single logical unit recognized by the operating system (Stallings, 2018). Raid storage can also be defined as a storage technology that creates a data loss fail-safe by merging two or more hard disk drives (HDDs) or solid-state drives (SSDs) into one cohesive storage unit, or array (Daniel, 2023). The main use or goal of RAID storage is to protect against the total loss of a disk drive’s data by repeating or recreating that data and storing it on the additional drive or drives, a process also known as data redundancy. Furthermore, by distributing the data across multiple drives, the RAID strategy, the data can be simultaneously accessed from multiple drives, therefore improving I/O performance. The data is distributed across the drive by striping. Striping is the process of dividing data into blocks and spreading the data blocks across multiple devices. Strips may be physical blocks, sectors, or some other unit. The RAID system is organized into various levels, which can be defined as RAID 0, RAID 1, RAID 2, RAID 3, RAID 4, RAID 5, RAID 6, and RAID 10. Note: all RAID levels provide redundancy, except for RAID 0. NOTE . From Stallings, 2018, Figure 11.8 RAID level 0 In RAID 0, the data is striped across multiple drives. As a result, it provides high read/write performances. Additionally, it does not incorporate redundancy. Not providing redundancy, i.e., not providing protection against total data lost, RAID level 0 is not commonly used. RAID level 1 In RAID 1, the data is striped and duplicated into two separate drives. In other words, every drive in the array (of drives) has a mirror drive that contains the same data. This provides redundancy and increases read speeds. But on the other hand, compared to RAID 0, it lowers writing speeds. This technique is referred to as mirroring. RAID level 2 RAID 2 uses parity. The data is striped, but the strips are very small, bite-size. Furthermore, instead of striping the blocks across the drives, the bits are stored in parallel across all the drives (Natarajan, 2016). Furthermore, two groups of the drive are used, one to write/read the data and another one to write/read the error correction codes (redundancy group). In other words, the bits of the code are stored in the corresponding bit positions on multiple parity drives, and on a single read, all disks are simultaneously accessed. Additionally, RAID 2 usually uses Hamming error correction code (ECC) and stores this information in the redundancy drives. If an error occurs, it uses the correction code to rebuild the data. Finally, it requires fewer drives than RAID 1, but it is costly. The number of redundant drives is proportional to the log of the number of data drives. RAID 2 can be beneficial in an environment in which many disk errors occur. However, due to the high reliability of modern drives, “RAID 2 is overkill and is not implemented” (Stallings, 2018, p. 502). RAID level 3 As RAID 2, it uses parity, the difference is that RAID 3 requires only a single redundant drive. Therefore, it is less costly than RAID 2. RAID 3 allows very high data transfer rates, but only one I/O request can be executed at a time. RAID level 4 RAID 4 also uses parity, the difference is that it utilizes an independent access technique, where the strips are relatively large, and drives operate independently allowing several I/O requests to be accessed in parallel. However, “every write operation must involve the parity disk, which therefore can become a bottleneck” (Stallings, 2018, p. 504). RAID Level 5 RAID 5 scheme is similar to RAID 4, the difference is that it implements striping with parity. It implements strips of parity across the drives, this avoids the potential I/O bottleneck of the single parity disk found in RAID 4. It is the most common RAID configuration, and when compared to RAID 0 and RAID 1 which require a minimum of two drives, it requires a minimum of three drives to function. Like RAID 0, RAID 5 read speeds fast but its write speed is lower due to the redundant creation of parity strips. Furthermore, the loss of an entire drive will not result in any data loss. RAID Level 6 RAID 6 scheme is similar to RAID 5, the difference is that instead of implementing one strips of parity across the drives, it implements two different strips of parity across the drives, see Figure 1. This “provides extremely high data availability. Three disks would have to fail within the MTTR (mean time to repair) interval to cause data to be lost” (Stallings, 2018, p. 504), but on the other hand, it severally affects write performance. RAID Level 10 RAID 10 combines both data striping (RAID 0) and disk mirroring (RAID 1). This achieves redundancy by duplicating the data and performance by striping. A minimum of four drives is necessary to implement it. In other words, RAID 10 is the best of both RAID 0 and RAID 1, with fast read and write speeds and fault tolerance (Natarajan, 2016). In conclusion, the best RAID configuration depends on the application's requirements, which may be based on speed, capacity, cost, data redundancy, or a combination of these. For example, a supercomputer application may prefer RAID 0 where performance, capacity, and low-cost are more important than reliability. In comparison, applications where data integrity is crucial, such as healthcare, banking, and defense, may prefer RAID 6. While it may come at a higher cost and offer less performance, particularly when writing data, than RAID 0. Nonetheless, RAID 6 still provides high data access and protects against data loss even in the event of two drives failing simultaneously. References: Daniel, B. (2023, March 7). RAID levels 0, 1, 5, 6 and 10 & raid types (software vs. hardware). Trusted Computing Innovator. https://www.trentonsystems.com/blog/raid-levels-0-1-5-6-10-raid-types#:~:text=The%20best%20RAID%20configuration%20for,RAID%206%20and%20RAID%2010 Natarajan, R. (2016, March 25). RAID 2, RAID 3, RAID 4, raid 6 explained with diagram. The Geek Stuff. https://www.thegeekstuff.com/2011/11/raid2-raid3-raid4-raid6/ Stallings, W. (2018). Operating Systems: Internals and design principles. Pearson
- Protecting Critical Systems: Malware Threats and Cybersecurity Measures
This article discusses the widespread presence of computers in critical infrastructures and the importance of implementing cybersecurity measures to safeguard systems from malware. It highlights various types of malware and outlines key countermeasures, such as antivirus software, firewalls, and encryption, to prevent security breaches and protect sensitive data. Alexander S. Ricciardi July 24th, 2023 Computers are everywhere, in our cars, phones, appliances, our grocery stores, airplanes, banks, and more… they are part of every critical infrastructure that our daily lives depend on. Thus, it is crucial to understand and implement cybersecurity measures. That is, measures that safeguard the functionality of our computer systems and the information stored in those systems. In the context of operating systems, the key to safeguarding a computer system's functionality and the integrity of the information stored in it, is to prevent “malicious software (malware) from gaining unauthorized privileges on the system and, in particular, from gaining root access” (Stallings, 2018. P. 631). Different types of malware exist, and each can affect a system in uniquely destructive ways. From viruses that replicate themselves and ransomware that encrypts crucial data to spyware that monitors and steals sensitive information. Figure 1 depicts a list of 12 types of malware that you are most likely to come across. For each type, a small description and real-world examples are given. Figure 1 Malware List Note . From 12 types of malware + examples that you should know, by Baker, 2023, ( https://www.crowdstrike.com/cybersecurity-101/malware/types-of-malware/ ). Copyright 2023 by crowdstrike.com . Therefore, it is essential to implement countermeasures or security controls to prevent security incidents. That is to prevent malware from accessing a computer system's functionality and data. The Federal Information Processing Standard (FIPS) defines security controls as “the management, operational, and technical controls (i.e., safeguards or, countermeasures) prescribed for an information system to protect the confidentiality, integrity, and availability of the system and its information” (FIPS 199, n.d.). Below is a list of countermeasures that can be implemented: Antivirus/Antimalware Software detects and neutralizes threats like viruses, worms, and spyware. They work by comparing files on a computer to a database of known threats and behaviors. Firewalls are a barrier between a trusted network and an untrusted network. They can prevent unauthorized access to a network and can often detect and block many types of attacks. Intrusion Detection System (IDS) is a system that monitors network traffic and system activities for malicious activities or policy violations and generates an alert when such activity occurs (Lutkevich, 2021). Intrusion Prevention System (IPS) is an IDS that detects potential threats but also takes actions to prevent them from causing harm. Patch Management is the action to manage software or systems patches. It is important to regularly update and patch systems, it can help prevent security incidents. Penetration Testing and Vulnerability Assessments . A vulnerability assessment identifies and measures security weaknesses in a system. A penetration test is a simulated cyber attack designed to identify vulnerabilities in an organization's security system (Escobar, 2021). Security Awareness Training : Educating users about the signs of a security incident can prevent many attacks. Users should be trained to recognize and report phishing attempts, suspicious behavior, and potential malware. Access Controls and User Permissions , it is important to implement controls on which users can access what data and to ensure that users have the appropriate amount of access to perform their duties. Encryption , which involves encoding sensitive data. This data can only be decrypted by authorized applications and users who have access to the data. Systems Event Management (SIEM) is a security solution that helps organizations recognize and address potential security threats and vulnerabilities before they have a chance to disrupt business operations (IBM, n.d.). In conclusion, computer systems are in every critical infrasture, making it essential to safeguard them from malware is essential for the security and stability of the critical infrastructures that underpin our daily lives. By implementing effective countermeasures, such as antivirus software, firewalls, and security training, we can significantly reduce the risk of cyber threats and ensure the continued protection of critical systems and infrastructures. References: Baker, B. (2023, July 19). 12 types of malware + examples that you should know[Figure 1]. crowdstrike.com . https://www.crowdstrike.com/cybersecurity-101/malware/types-of-malware/ Escobar, E. (2021, August 19). Vulnerability assessments versus penetration tests. Secureworks . https://www.secureworks.com/blog/vulnerability-assessments-versus-penetration-tests Lutkevich, B. (2021, October 7). What is an intrusion detection system (IDS)? Techtarget.com . https://www.techtarget.com/searchsecurity/definition/intrusion-detection-system?Offer=abt_pubpro_AI-Insider IBM (n.d.). What is Security Information and Event Management (SIEM)? Ibm.com . https://www.ibm.com/topics/siem FIPS 199, standards for security categorization of federal ... - NIST. (n.d.). National Institute of Standards and Technology. Retrieve from: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.199.pdf Stallings, W. (2018). Operating Systems: Internals and design principles . Pearson
- Mastering Linux File Structure: The Importance of the Filesystem Hierarchy Standard (FHS)
This article explains the importance of the Filesystem Hierarchy Standard (FHS) in Linux and other UNIX-like systems, focusing on its role in organizing files, ensuring cross-distribution compatibility, and supporting system stability. It highlights how understanding FHS is crucial for effective system navigation, troubleshooting, and security management. Alexander S. Ricciardi July 18th, 2023 In computer science, file structure is usually a tree-like hierarchical organization representation of files stored in secondary memory. “It is also a collection of operations for accessing the data. It enables applications to read, write, and modify data. File structures may also help to find the data that matches certain criteria” (Sari, 2023). Linux distributions and other UNIX-like operating systems implement Filesystem Hierarchy Standard (FHS) as a file structure system. FHS is a standard that defines the directory structure and directory contents. This standard consists of a set of requirements and guidelines for file and directory placement. “The guidelines are intended to support interoperability of applications, system administration tools, development tools, and scripts as well as greater uniformity of documentation for these systems” (LSB Workgroup, 2015, Abstract section). In this post, I discuss the importance of understanding file structure, specifically FHS in Linux systems. Understanding FHS is crucial to effectively navigate and managing a Linux system, ensuring cross-distribution compatibility, maintaining system stability, conducting efficient troubleshooting, and implementing appropriate security measures. Navigating and Managing The role of a file structure system is to allow software and user to manage files in secondary memory (create, delete, read, write, and modify files). Additionally, FHS is responsible for retrieving the root filesystem, which is used to boot, restore, and/or repair the system (LSB Workgroup, 2015). In the context of Linux, it is crucial to understand the FHS directory structure and directory contents to navigate and manage files. Figure 1 illustrates the Linux directory FHS structure. Figure 1 Linux Directory FHS Structure Note. From Linux Directory Structure (File System Hierarchy) Explained with Examples, by M. Maruthamuthu, 2019, ( https://www.2daygeek.com/linux-directory-structure-file-system-hierarchy/ ). Copyrights 2023 2daygeek.com Cross-Distribution Compatibility Understanding how FHS differed from other file system structure systems is crucial for applications development and user utilization. FHS’ main quality is that it can support cross-distribution compatibility. In Linux terms, a distribution is an operating system developed by various open-source projects and programmers. “Each distribution includes the Linux kernel (the foundation of the operating system), the GNU shell utilities (the terminal interface and commands)” (“What is a Linux distribution?”, n.d.). In other words, different Linux distributions can have different approaches to file and directory placement and still ensures a level of consistency across distributions. Furthermore, this level of consistency across distributions facilitates the creation of applications compatible across various Linux distributions. System Stability and Troubleshooting Maintaining system stability is another key advantage of understanding the FHS. Linux separates user data from system data, static files from dynamic files, and local files from network files. This separation prevents the unintentional alteration or deletion of critical system files, ensuring a stable and secure system. Effective troubleshooting is an integral part of managing any system. In Linux systems, when problems arise, knowing the FHS will allow you to pinpoint potential sources of issues. For example, if a system service isn't starting as expected, checking files in /var/log might provide useful error messages. Security Knowing which directories contain sensitive data or system-critical files helps when setting permissions or installing a security-enhancing software package. FHS allows users to quickly identify important directories, such as /etc for system configuration files, /var for logs and spool files, and /home for user data, amongst others. This makes it easier to configure and enforce security policies across the system. In conclusion, the file structure is usually a tree-like hierarchical organization representation of files stored in secondary memory, which allows data access and enables applications and users to create, delete, read, write, and modify files. In this post, I discussed Filesystem Hierarchy Standard (FHS) as a file structure system. FHS is a standard that defines the directory structure and directory contents in Linux distributions and other UNIX-like operating systems. Finally, having a good understanding of FHS is essential to effectively navigate and manage files, develop cross-distribution applications, preserve system stability, conduct efficient troubleshooting, and put into place suitable security measures in Linux distributions and other UNIX-like operating systems. References: LSB Workgroup (2015, March 19). Filesystem hierarchy standard [PDF]. The Linux Foundation. Retrieve from: https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.pdf Sari, S. (2023, May 30). File structures. Baeldung on Computer Science. https://www.baeldung.com/cs/file-structures#:~:text=A%20file%20structure%20is%20a,data%20that%20matches%20certain%20criteria Stallings, W. (2018). Operating Systems: Internals and design principles . Pearson What is a Linux distribution?: Answer from SUSE defines. SUSE Defines. (n.d.). https://www.suse.com/suse-defines/definition/linux-distribution/
- Bounded-Buffer, Readers-Writers, and Dining-Philosophers problems: Key OS Challenges and Solutions
This article explains process synchronization in Operating Systems (OS) and challenges like the Bounded-Buffer, Readers-Writers, and Dining-Philosophers problems. It discusses solutions using semaphores, message passing, and monitors to avoid issues like race conditions, deadlock, and starvation. Alexander S. Ricciardi September 27th, 2024 In the context of an operating system (OS), process synchronization can be defined as a set of methods and techniques used to coordinate the concurrent execution of multiple processes or threads. In a multi-process environment, which can also be referred to as "multiprocessing" or "parallel computing", process synchronization plays a crucial role in ensuring that shared data is accessed and modified correctly by addressing several problems that may arise in multi-process environments. Classic problems of process synchronization are the Bounded-Buffer, Readers–Writers, and Dining-Philosophers problems. Solutions to these problems can be developed using tools such as semaphores, binary semaphores, Message passing, and monitors (Silberschatz et al., 2018, p. 314). the Bounded-Buffer Problem: The Bounded-Buffer Problem is a classic synchronization problem that involves coordinating the operations of producers and consumers accessing a fixed-size buffer. A Bounded-Buffer can lead to Race Condition. A Race Condition is “a situation in which multiple threads or processes read and write a shared data item, and the final result depends on the relative timing of their execution” (Stallings, 2018, Chapter 5). Mutual exclusion must be enforced to ensure that only one process can access the shared buffer at a given time. The following is a Bounded-Buffer producer and consumer pseudocode. // producer while (true) { // produce item v while ((in + 1) % n == out) { // do nothing } b[in] = v; in = (in + 1) % n; } // consumer while (true) { while (in == out) { // do nothing } w = b[out]; out = (out + 1) % n; // consume item w } (Stallings, 2018, Chapter 5.4) The problem Description: The fixed-size buffer can hold a limited number of items. Multiple producers may add items to the buffer concurrently. Multiple consumers may remove items from the buffer concurrently. Producers must wait if the buffer is full. Consumers must wait if the buffer is empty. A solution to the Bounded-Buffer Problem is the implementation of semaphore, which is a mutual exclusion method. A semaphore (also called a counting semaphore or a general semaphore) is an integer value used for signaling among processes, and can only be accessed through two standard atomic operations: semWait() and semSignal(). The letter 's' is generally used to denote a semaphore, below is an example of semaphore pseudocode. struct semaphore { int count; queueType queue; }; void semWait(semaphore s) { s.count--; if (s.count < 0) { /* place this process in s.queue */ /* block this process */ } } void semSignal(semaphore s) { s.count++; if (s.count <= 0) { /* remove a process P from s.queue */ /* place process P on ready list */ } } (Stallings, 2018, Figure 5.6) Note that only three operations may be performed on a semaphore, all of which are atomic: initialize, decrement, and increment. The decrement operation may result in the blocking of a process, and the increment operation may result in the unblocking of a process. Furthermore, a binary semaphore is a semaphore that takes on only the values 0 and 1, and a mutex is “similar to a binary semaphore. A key difference between the two is that the process that locks the mutex (sets the value to 0) must be the one to unlock it (sets the value to 1)" (Stallings, 2018, Chapter 5.4). Below is an example of binary semaphore pseudocode: struct semaphore { int count; queueType queue; }; void semWait(semaphore s) { s.count--; if (s.count < 0) { /* place this process in s.queue */ /* block this process */ } } void semSignal(semaphore s) { s.count++; if (s.count <= 0) { /* remove a process P from s.queue */ /* place process P on ready list */ } } Stallings, 2018, Figure 5.7) Note that: A binary semaphore may be initialized to zero or one. The semWaitB(semaphore s) function checks if the value is zero, then the process executing the semWaitB() is blocked. If the value is one, then the value is changed to zero and the process continues execution. The semSignalB(semaphore s) function checks if the queue is empty (s is equal to zero) on the semaphore, if so, the semaphore is set to one, else a process blocked by a semWaitB() is unblocked (removed from the queue) and placed on the ready list. The following pseudocode shows a possible solution to the Bounded-Buffer Problem using semaphores: /* program boundedbuffer */ const int sizeofbuffer = /* buffer size */; semaphore s = 1, n = 0, e = sizeofbuffer; void producer() { while (true) { produce(); // produce item semWait(e); semWait(s); append(); // add item to "the end of the list" semSignal(s); semSignal(n); } } void consumer() { while (true) { semWait(n); semWait(s); take(); // take item from "the list" semSignal(s); semSignal(e); consume(); // consume item } } void main() { parbegin(producer, consumer); } (Stallings, 2018, Figure 5.13) 2- Readers–Writers Problem The Readers–Writers Problem can be described as multiple readers and writers concurrently assessing a resource (e.g., a file or database) and potentially affecting the data integrity. The condition that needs to be met to ensure data integrity are: Any number of readers may simultaneously read the file. Only one writer at a time may write to the file. If a writer is writing to the file, no reader may read it. Note that readers are processes that are not required to exclude one another, and writers are processes that are required to exclude all other processes, readers and writers alike. (Stallings, 2018, Chapter 5.7) A solution to the Readers–Writers Problem is the implementation of semaphore or message passing. Message passing is a mechanism to allow processes or threads to communicate and synchronize their actions. In message passing, communication occurs by explicitly sending a message from a sender to a receiver. The sender encapsulates the data or information to be transmitted into a message and sends it to the receiver. The receiver then receives the message and extracts the information or data from it. 3- Dining-Philosophers problem The dining-Philosophers problem describes a scenario where, for example, five philosophers share a meal at a round table. Every philosopher has a plate and one fork to their right and one fork to their left. They need to use two forks to eat, and it is a total of five forks on the table. The problem arises when all the philosophers decide to eat at the same time. The Dining-Philosophers Problem is a classic synchronization problem, it is a metaphor for the problems of deadlock and starvation, which can occur when multiple processes attempt to access multiple resources simultaneously. Starvation is the situation in which a process or thread waits indefinitely within a semaphore. (Silberschatz et al., 2018, p. G-32) Deadlock is the state in which two processes or threads are stuck waiting for an event that can only be caused by one of the processes or threads. (Silberschatz et al., 2018, p. G-9). Two main methods exist to prevent or avoid deadlocks. The first one is the deadlock avoidance approach, which grants access to a resource if it cannot result in a deadlock. The second one is the deadlock prevention approach which involves changing the rules so that processes will not make requests that could result in deadlock. Note that for a deadlock to occur, each of the four conditions listed below must hold: Mutual Exclusion: Only one process at a time can use a resource. Hold and Wait: A process that holds at least one resource and is waiting to acquire additional resources held by other processes. No Preemption: A resource can be released only voluntarily by the process holding it after that process has completed its task. Circular Wait: A set of waiting processes. In other words, a deadlock will not occur by ensuring that at least one of the conditions does not exist. Deadlock prevention prevents at least one of the four conditions of deadlock. On the other hand, deadlock avoidance allows the four conditions, but it allows only three of the four conditions to exist concurrently. Finally, one of the solutions for Dining-Philosophers Problem is using a monitor-based solution. A monitor is a programming language construct that allows to put a lock on objects, The main characteristics of a monitor are the following: The local data variables are accessible only by the monitor’s procedures and not by any external procedure. A process enters the monitor by invoking one of its procedures. Only one process may be executing in the monitor at a time; any other processes that have invoked the monitor are blocked, waiting for the monitor to become available. (Stallings, 2018, Chapter 5.5). In conclusion, process synchronization is a complex subject and it is essential in operating systems to coordinate the concurrent execution of multiple processes or threads. It addresses various problems such as the Bounded-Buffer Problem, Readers-Writers Problem, and Dining-Philosophers Problem. These problems highlight the challenges of managing shared resources, ensuring mutual exclusion, avoiding race condition and deadlock, and preventing starvation. References: Silberschatz, A., Galvin, P. B., & Gagne, G. (2018). Operating system concepts [PDF]. Wiley. Retrieved from: https://os.ecci.ucr.ac.cr/slides/Abraham-Silberschatz-Operating-System-Concepts-10th-2018.pdfLinks to an external site. Stallings, W. (2018). Operating Systems: Internals and design principles . Pearson













