file descriptor:
0 → standard input (stdin)
1 → standard output (stdout)
2 → standard error (stderr)
dup() and dup2()
- dup(fd) → creates a new file descriptor number pointing to the same
file as fd. - dup2(fd, newfd) → same, but you choose the new descriptor number
(closes it first if open).
File Descriptor
It’s just a number the OS gives you when you open a file.
Think of it as a ticket number to access the file.
int fd = open("data.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
// fd might be 3 because 0,1,2 are stdin, stdout, stderr
write(fd, "Hello\n", 6); // writes to the file via fd
dup(fd)
Makes a copy of the descriptor → new number, same file.
int newfd = dup(fd); // creates newfd (e.g., 4) same file as fd
write(newfd, "World\n", 6); // continues writing at same position
dup2(fd, newfd)
Copies fd into exactly newfd.
If newfd was open, it closes it first.
dup2(fd, STDOUT_FILENO); // now printf goes into fd's file
printf("This goes into the file!\n");
unistd.h
It declares many system call interfaces for Unix-like operating systems (Linux, macOS, BSD, etc.).
Useful functions:
#include <string.h> strlen()
open(...)
flags → control the behavior of opening the file (read/write mode, create, truncate, append, etc.).
mode_t → sets the permissions of the file only when it’s created.
How to read a file
#include "source.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// read a file line by line and then identify a specific text in ""
// remember to free() after using sscanf's buffer
void find_movie_score() {
char* buffer = NULL;
size_t capacity = 0;
FILE* fp = fopen("movies.csv", "r");
int year;
int score;
char movie[30];
getline(&buffer, &capacity, fp);
while (getline(&buffer, &capacity, fp) != -1) {
// Read up to 29 characters, stopping if a " is seen.
// in this case 29 means 1 character less than the 30 length long buffer, which is safer
int result = sscanf(buffer, "%d,%d,\"%29[^\"]\"", &year, &score, movie);
// == is not safe
if (strcmp(movie, "New_York,_New_York") == 0) {
printf("%d", score);
break;
}
}
free(buffer);
fclose(fp);
return;
}
Network
Open Source Interconnection 7 layer model (OSI Model)
- Layer 1: The Physical Layer. These are the actual waves that carry the bauds across the wire. As an aside, bits don’t cross the wire because in most mediums you can alter two characteristics of a wave – the amplitude and the frequency – and get more bits per clock cycle.
- Layer 2: The Link Layer. This is how each of the agents reacts to certain events (error detection, noisy channels, etc). This is where Ethernet and WiFi live.
- Layer 3: The Network Layer. This is the heart of the Internet. The bottom two protocols deal with communication between two different computers that are directly connected. This layer deals with routing packets from one endpoint to another.
- Layer 4: The Transport Layer. This layer specifies how the slices of data are received. The bottom three layers make no guarantee about the order that packets are received and what happens when a packet is dropped. Using different protocols, this layer can.
- Layer 5: The Session Layer. This layer makes sure that if a connection in the previous layers is dropped, a new connection in the lower layers can be established, and it looks like nothing happened to the end-user.
- Layer 6: The Presentation Layer. This layer deals with encryption, compression, and data translation. For example, portability between different operating systems like translating newlines to windows newlines.
- Layer 7: The Application Layer. HTTP and FTP are both defined at this level. This is typically where we define protocols across the Internet. As programmers, we only go lower when we think we can create algorithms that are more suited to our needs than all of the below.
int required_family = AF_INET; // Change to AF_INET6 for IPv6, The "AF" stands for Address Family
struct ifaddrs *myaddrs, *ifa; // struct ifaddrs is a structure that holds information about a network interface (e.g., eth0, wlan0)
// This function retrieves a list of all network interfaces (both active and inactive) on your system.
getifaddrs(&myaddrs);
char host[256], port[256];
for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
int family = ifa->ifa_addr->sa_family;
if (family == required_family && ifa->ifa_addr) {
int ret = getnameinfo(ifa->ifa_addr,
(family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, sizeof(host), port, sizeof(port)
, NI_NUMERICHOST | NI_NUMERICSERV)
if (0 == ret) {
puts(host);
}
}
}
getnameinfo() converts a binary network address into a text string.
Parameters:
ifa->ifa_addr: the raw socket address structure.
The second argument: size of the structure — IPv4 and IPv6 have different sizes.
host: buffer to store the IP address.
port: buffer to store the port number.
Flags NI_NUMERICHOST | NI_NUMERICSERV mean:
NI_NUMERICHOST: return the numeric IP (like "192.168.1.10") instead of a hostname.
NI_NUMERICSERV: return the numeric port (like "80") instead of service name (like "http").
To get your IP Address from the command line use ifconfig or Windows’ ipconfig.
To grab the IP Address of a remote website, The function getaddrinfo can convert a human-readable domain name (e.g. www.illinois.edu) into an IPv4 and IPv6 address. It will return a linked-list of addrinfo structs:
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};First, use getaddrinfo to build a linked-list of possible connections. Secondly, use getnameinfo to convert the binary address of one of those into a readable form.
hints.ai_family = AF_INET; // AF_INET means IPv4 only addresses
// Get the machine addresses
int result = getaddrinfo("www.bbc.com", NULL, &hints, &infoptr);
if (result) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result));
exit(1);
}
struct addrinfo *p;
char host[256];
for(p = infoptr; p != NULL; p = p->ai_next) {
// Get the name for all returned addresses
getnameinfo(p->ai_addr, p->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
puts(host);
}The socket call creates a network socket and returns a descriptor that can be used with read and write. In this sense, it is the network analog of open that opens a file stream – except that we haven’t connected the socket to anything yet!
int socket(int domain, int socket_type, int protocol);
connect to an address
struct addrinfo current, *result;
memset(¤t, 0, sizeof(struct addrinfo));
current.ai_family = AF_INET;
current.ai_socktype = SOCK_STREAM;
getaddrinfo(info->hostname, info->port, ¤t, &result);
connect(sock_fd, result->ai_addr, result->ai_addrlen)
freeaddrinfo(result);
Simple TCP server :
- socket, bind, listen, accept
socket programming (server and client)
Process memory layout
- OS Kernel Space
- Stack
- ...
- Heap
- BSS
- Data
- Text
- ...
OS Kernel Space
Reserved for the operating system kernel and device drivers.
User processes cannot access this space directly (for protection and stability).
Contains critical code and data structures needed to manage the system.
Stack
~Grows downwards in memory (toward lower addresses).~
Stores function call information: return addresses, local variables, and function parameters.
Managed automatically by the compiler and runtime.
Each thread in a process gets its own stack.
Heap
Grows upwards in memory (toward higher addresses).
Used for dynamically allocated memory (e.g., via malloc in C, new in C++).
Managed by the programmer (or garbage collector in managed languages).
Can fragment over time due to frequent allocations and deallocations.
BSS (Block Started by Symbol)
Contains uninitialized global and static variables.
Typically zero-initialized at program startup.
For example: static int count; (without an initializer).
Data Segment
Contains initialized global and static variables.
Divided into:
Read/write section: for variables that can change during execution.
Read-only section: for constants that must not change.
Example: static int count = 5;
Text Segment (Code Segment)
Stores the compiled program instructions (machine code).
Usually marked as read-only to prevent accidental modification of code.
May be shared among processes running the same program to save memory.
VIM
Press Esc (goes back to Normal mode).
Then type one of these commands (they start with :):
:w → Save (write) the file.
:q → Quit.
:wq → Save and quit.
:q! → Quit without saving.
Arrow keys → work as expected.
Or use the classic Vim keys:
h → move left
l → move right
j → move down
k → move up
Moving by words
w → jump forward to the beginning of the next word
e → jump to the end of the current/next word
b → jump backward to the beginning of a word
Moving by lines
0 (zero) → move to the start of the line
^ → move to the first non-space character of the line
$ → move to the end of the line
Moving by text blocks
gg → go to the top of the file
G → go to the bottom of the file
:n → jump to line n (example: :15 goes to line 15)
Searching
/word → search forward for “word”
?word → search backward for “word”
n → repeat the last search in the same direction
N → repeat the last search in the opposite direction
Scrolling
Ctrl + d → scroll half a page down
Ctrl + u → scroll half a page up
Ctrl + f → scroll one full page down
Ctrl + b → scroll one full page up
Split Verticle or Horizontal:
vim -O main.c utils.h
vim -o main.c utils.h
:vsplit utils.h // inside vim
vim -p main.c utils.h // taps alternative
Moving Between Splits
Ctrl-w h → move to left split
Ctrl-w l → move to right split
Ctrl-w j → move to split below
Ctrl-w k → move to split above
Ctrl-w w → cycle through all windows
Resizing Splits
Ctrl-w = → equalize all splits
Ctrl-w > → increase width
Ctrl-w < → decrease width
Ctrl-w + → increase height
Ctrl-w - → decrease height
GDB
break main // set break point to function main()
delete // clean all breakpoints
layout src // open interface
info breakpoint // check breakpoint information
Cursor Movement
Keys Action
Ctrl-a Move to beginning of line
Ctrl-e Move to end of line
Ctrl-b Move backward one character (like ←)
Ctrl-f Move forward one character (like →)
Alt-b Move backward one word
Alt-f Move forward one word
Editing Text
Keys Action
Ctrl-d Delete character under cursor
Backspace Delete character before cursor
Ctrl-k Kill (cut) text from cursor to end of line
Ctrl-u Kill (cut) text from cursor to beginning of line
Alt-d Kill (cut) word after cursor
Alt-Backspace Kill (cut) word before cursor
History Navigation
Keys Action
Ctrl-p Previous command (like ↑)
Ctrl-n Next command (like ↓)
Ctrl-r Reverse search in history
Ctrl-g Cancel search
Misc
Keys Action
Ctrl-l Clear the screen (like clear)
Ctrl-y Yank (paste) last killed text
Ctrl-t Swap character before cursor with current one
Get started with your assignment
// open session for: fa25-cs341-276.cs.illinois.edu
cd fa25_cs341_NETID
git remote add release https://github.com/illinois-cs-coursework/fa25_cs341_.release.git
git pull release main --allow-unrelated-histories --no-rebase
git push origin main
git add submission.txt
git commit -m "My Submission" (commit your work to git).
git push origin main (push your committed work to Github – Required for your changes to be visible to the Autograder).
WSL
If you want to visit some files in your WSL via Windows file explorer, use
\\wsl$\Ubuntu\home\
Valgrind
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./shell
valgrind --leak-check=full --show-leak-kinds=all ./your_program