### Setup Asynchronous I/O Context using io_uring_setup (C) Source: https://unixism.net/loti/ref-iouring/io_uring_setup The io_uring_setup function is used to initialize a new io_uring instance. It requires the number of entries for the ring and a pointer to a structure containing parameters for the setup. Ensure the header is included. ```c #include int io_uring_setup(unsigned int entries, struct io_uring_params *p); ``` -------------------------------- ### Build and Run loti Webserver Example (Shell) Source: https://unixism.net/loti/_sources/tutorial/webserver_liburing These commands demonstrate how to build and run the loti webserver example. It involves creating a build directory, configuring with CMake, building the project, and then executing the webserver binary. The output shows the minimum kernel version required and the actual kernel version, followed by the server listening on port 8000. ```none $ mkdir build $ cd build $ cmake .. $ cmake --build . $ cd .. $ build/webserver_liburing Minimum kernel version required is: 5.5 Your kernel version is: 5.6 ZeroHTTPd listening on port: 8000 ``` -------------------------------- ### C: io_uring Main Function Source: https://unixism.net/loti/low_level The main entry point for the io_uring example program. It handles command-line arguments for filenames, initializes the io_uring setup, and iterates through provided files to submit read requests and process completions. It requires at least one filename argument. ```c int main(int argc, char *argv[]) { struct submitter *s; if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } s = malloc(sizeof(*s)); if (!s) { perror("malloc"); return 1; } memset(s, 0, sizeof(*s)); if(app_setup_uring(s)) { fprintf(stderr, "Unable to setup uring!\n"); return 1; } for (int i = 1; i < argc; i++) { if(submit_to_sq(argv[i], s)) { fprintf(stderr, "Error reading file\n"); return 1; } read_from_cq(s); } return 0; } ``` -------------------------------- ### C: io_uring Server Socket Setup Source: https://unixism.net/loti/tutorial/webserver_liburing This C code sets up a server socket, binds it to a port, and starts listening for incoming connections using basic socket programming functions. It includes error handling for critical operations like bind and listen. ```c int setup_server_socket(int port) { int sock; struct sockaddr_in srv_addr; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) fatal_error("socket()"); memset(&srv_addr, 0, sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_addr.s_addr = INADDR_ANY; srv_addr.sin_port = htons(port); if (bind(sock, (struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) fatal_error("bind()"); if (listen(sock, 10) < 0) fatal_error("listen()"); return (sock); } ``` -------------------------------- ### io_uring Setup and Configuration - C Source: https://unixism.net/loti/low_level This snippet demonstrates the setup of the io_uring interface in C. It initializes the ring file descriptor, maps the submission and completion queue ring buffers, and handles different kernel features for memory mapping. Dependencies include standard C libraries and Linux-specific headers. ```c #include #include #include #include #include #include #include #include #include #include #include /* If your compilation fails because the header file below is missing, * your kernel is probably too old to support io_uring. * */ #include #define QUEUE_DEPTH 1 #define BLOCK_SZ 1024 /* This is x86 specific */ #define read_barrier() __asm__ __volatile__("":::"memory") #define write_barrier() __asm__ __volatile__("":::"memory") struct app_io_sq_ring { unsigned *head; unsigned *tail; unsigned *ring_mask; unsigned *ring_entries; unsigned *flags; unsigned *array; }; struct app_io_cq_ring { unsigned *head; unsigned *tail; unsigned *ring_mask; unsigned *ring_entries; struct io_uring_cqe *cqes; }; struct submitter { int ring_fd; struct app_io_sq_ring sq_ring; struct io_uring_sqe *sqes; struct io_uring_cq_ring cq_ring; }; struct file_info { off_t file_sz; struct iovec iovecs[]; /* Referred by readv/writev */ }; /* * This code is written in the days when io_uring-related system calls are not * part of standard C libraries. So, we roll our own system call wrapper * functions. * */ int io_uring_setup(unsigned entries, struct io_uring_params *p) { return (int) syscall(__NR_io_uring_setup, entries, p); } int io_uring_enter(int ring_fd, unsigned int to_submit, unsigned int min_complete, unsigned int flags) { return (int) syscall(__NR_io_uring_enter, ring_fd, to_submit, min_complete, flags, NULL, 0); } /* * Returns the size of the file whose open file descriptor is passed in. * Properly handles regular file and block devices as well. Pretty. * */ off_t get_file_size(int fd) { struct stat st; if(fstat(fd, &st) < 0) { perror("fstat"); return -1; } if (S_ISBLK(st.st_mode)) { unsigned long long bytes; if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { perror("ioctl"); return -1; } return bytes; } else if (S_ISREG(st.st_mode)) return st.st_size; return -1; } /* * io_uring requires a lot of setup which looks pretty hairy, but isn't all * that difficult to understand. Because of all this boilerplate code, * io_uring's author has created liburing, which is relatively easy to use. * However, you should take your time and understand this code. It is always * good to know how it all works underneath. Apart from bragging rights, * it does offer you a certain strange geeky peace. * */ int app_setup_uring(struct submitter *s) { struct app_io_sq_ring *sring = &s->sq_ring; struct app_io_cq_ring *cring = &s->cq_ring; struct io_uring_params p; void *sq_ptr, *cq_ptr; /* * We need to pass in the io_uring_params structure to the io_uring_setup() * call zeroed out. We could set any flags if we need to, but for this * example, we don't. * */ memset(&p, 0, sizeof(p)); s->ring_fd = io_uring_setup(QUEUE_DEPTH, &p); if (s->ring_fd < 0) { perror("io_uring_setup"); return 1; } /* * io_uring communication happens via 2 shared kernel-user space ring buffers, * which can be jointly mapped with a single mmap() call in recent kernels. * While the completion queue is directly manipulated, the submission queue * has an indirection array in between. We map that in as well. * */ int sring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned); int cring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe); /* In kernel version 5.4 and above, it is possible to map the submission and * completion buffers with a single mmap() call. Rather than check for kernel * versions, the recommended way is to just check the features field of the * io_uring_params structure, which is a bit mask. If the * IORING_FEAT_SINGLE_MMAP is set, then we can do away with the second mmap() * call to map the completion ring. * */ if (p.features & IORING_FEAT_SINGLE_MMAP) { if (cring_sz > sring_sz) { sring_sz = cring_sz; } cring_sz = sring_sz; } /* Map in the submission and completion queue ring buffers. * Older kernels only map in the submission queue, though. * */ sq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_SQ_RING); if (sq_ptr == MAP_FAILED) { perror("mmap"); return 1; } if (p.features & IORING_FEAT_SINGLE_MMAP) { cq_ptr = sq_ptr; } else { /* Map in the completion queue ring buffer in older kernels separately */ ``` -------------------------------- ### io_uring_setup System Call Source: https://unixism.net/loti/_sources/ref-iouring/io_uring_setup Sets up a submission queue (SQ) and completion queue (CQ) for performing asynchronous I/O operations. ```APIDOC ## io_uring_setup ### Description The `io_uring_setup()` system call sets up a submission queue (SQ) and completion queue (CQ) with at least *entries* entries, and returns a file descriptor which can be used to perform subsequent operations on the io_uring instance. The submission and completion queues are shared between userspace and the kernel, which eliminates the need to copy data when initiating and completing I/O. *params* is used by the application to pass options to the kernel, and by the kernel to convey information about the ring buffers. ### Method `SYSCALL` ### Endpoint `/io_uring_setup` ### Parameters #### Path Parameters - None #### Query Parameters - None #### Request Body - **entries** (unsigned int) - The minimum number of entries for the submission and completion queues. - **p** (struct io_uring_params *) - A pointer to a structure used for passing configuration options to the kernel and receiving information about the ring buffers. ### Request Example ```c #include struct io_uring_params params; int fd = io_uring_setup(num_entries, ¶ms); ``` ### Response #### Success Response (0) - **return value** (int) - A file descriptor for the new io_uring instance on success, or a negative error code on failure. #### Response Example ``` // Success: returns a file descriptor (e.g., 3) // Failure: returns a negative error code (e.g., -EINVAL) ``` ### Flags The `flags` field within `struct io_uring_params` can be set using the following values: - **IORING_SETUP_IOPOLL**: Perform busy-waiting for I/O completion. Requires `O_DIRECT` and may consume more CPU. - **IORING_SETUP_SQPOLL**: Create a kernel thread for submission queue polling, enabling I/O submission without context switches. Requires `IORING_REGISTER_FILES`. - **IORING_SETUP_SQ_AFF**: Bind the poll thread to a specific CPU (meaningful only when `IORING_SETUP_SQPOLL` is set). - **IORING_SETUP_CQSIZE**: Create the completion queue with a specific number of entries (`cq_entries`), potentially rounded up. ### Features The `features` field is filled by the kernel and indicates supported features: - **IORING_FEAT_SINGLE_MMAP**: Allows mapping SQ and CQ rings with a single `mmap(2)` call. - **IORING_FEAT_NODROP**: Ensures completion events are not dropped when the CQ ring is full; overflowed events are stored internally. ``` -------------------------------- ### io_uring_queue_init Source: https://unixism.net/loti/_sources/ref-liburing/setup_teardown Initializes an io_uring instance for use in your programs. This function should be called before any other io_uring operations. ```APIDOC ## io_uring_queue_init ### Description Initializes ``io_uring`` for use in your programs. You'd want to call this function before you get to do anything else with ``io_uring``. ### Method C Function ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```c struct io_uring ring; unsigned entries = 32; unsigned flags = 0; int ret = io_uring_queue_init(entries, &ring, flags); ``` ### Response #### Success Response (0) Returns 0 on success. #### Error Response (-errno) Returns -errno on failure. Use `strerror(3)` to get a human-readable error message. #### Response Example ```c // On success: 0 // On failure: -1 // with errno set appropriately (e.g., -EINVAL) ``` ``` -------------------------------- ### io_uring Initialization Functions Source: https://unixism.net/loti/ref-liburing/setup_teardown Functions for initializing and setting up the io_uring structure for asynchronous I/O operations. ```APIDOC ## io_uring_queue_init ### Description Initializes `io_uring` for use in your programs. This function should be called before any other `io_uring` operations. ### Method int ### Endpoint N/A (Function Call) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c struct io_uring ring; unsigned entries = 128; unsigned flags = 0; int ret = io_uring_queue_init(entries, &ring, flags); if (ret < 0) { // Handle error } ``` ### Response #### Success Response (0) Returns 0 on success. #### Response Example ``` 0 ``` ### Error Response - **-errno** (int) - Negative error code indicating failure. Use `strerror(3)` for human-readable descriptions. ``` ```APIDOC ## io_uring_queue_init_params ### Description Functionally equivalent to `io_uring_queue_init()`, but additionally takes a pointer to `io_uring_params` structure, allowing you to specify your own `io_uring_params` structure for flags, CPU affinity, and idle time. ### Method int ### Endpoint N/A (Function Call) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c struct io_uring ring; struct io_uring_params params; unsigned entries = 128; // Initialize params fields like flags, sq_thread_cpu, etc. params.flags = ...; int ret = io_uring_queue_init_params(entries, &ring, ¶ms); if (ret < 0) { // Handle error } ``` ### Response #### Success Response (0) Returns 0 on success. #### Response Example ``` 0 ``` ### Error Response - **-errno** (int) - Negative error code indicating failure. Use `strerror(3)` for human-readable descriptions. ``` ```APIDOC ## io_uring_queue_mmap ### Description A low-level function to mmap(2) the rings for `io_uring` after `io_uring_setup()` has been called. Use this when you need fine-grained control over `io_uring` initialization. ### Method int ### Endpoint N/A (Function Call) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```c int fd = io_uring_setup(...); struct io_uring_params params; struct io_uring ring; // Populate params structure int ret = io_uring_queue_mmap(fd, ¶ms, &ring); if (ret < 0) { // Handle error } ``` ### Response #### Success Response (0) Returns 0 on success. #### Response Example ``` 0 ``` ### Error Response - **-errno** (int) - Negative error code indicating failure. Use `strerror(3)` for human-readable descriptions. ``` -------------------------------- ### SQ Polling Wakeup Sequence C Example Source: https://unixism.net/loti/_sources/ref-iouring/io_uring_setup This C code snippet demonstrates the sequence for checking and handling the IORING_SQ_NEED_WAKEUP flag when SQ polling is enabled. It ensures that the wakeup flag is read after the tail pointer is written, and calls io_uring_enter to wake the kernel thread if necessary. ```c /* * Ensure that the wakeup flag is read after the tail pointer has been * written. */ smp_mb(); if (*sq_ring->flags & IORING_SQ_NEED_WAKEUP) io_uring_enter(fd, 0, 0, IORING_ENTER_SQ_WAKEUP); ``` -------------------------------- ### Initialize io_uring with Parameters (io_uring_queue_init_params) Source: https://unixism.net/loti/_sources/ref-liburing/setup_teardown Functionally similar to io_uring_queue_init but allows specifying custom io_uring_params, including flags and CPU affinity for the submission queue thread. Kernel fills in other parameters. ```c int io_uring_queue_init_params(unsigned entries, struct io_uring *ring, struct io_uring_params *p); ``` -------------------------------- ### io_uring_queue_init_params Source: https://unixism.net/loti/_sources/ref-liburing/setup_teardown Functionally equivalent to `io_uring_queue_init`, but allows specifying io_uring parameters through a `struct io_uring_params` pointer. ```APIDOC ## io_uring_queue_init_params ### Description Functionally equivalent to :c:func:`io_uring_queue_init`, but additionally takes a pointer to :c:struct:`io_uring_params` structure, allowing you to specify your own :c:struct:`io_uring_params` structure. In the :c:struct:`io_uring_params` structure, you can only specify ``flags`` which can be used to set :ref:`various flags ` and ``sq_thread_cpu`` and ``sq_thread_idle`` fields, which are used to set the CPU affinity and submit queue idle time. Other fields of the structure are filled up by the kernel on return. When you use :c:func:`io_uring_queue_init`, you don't get to specify these values. This function's existence solves this problem for you. ### Method C Function ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```c struct io_uring ring; struct io_uring_params params; unsigned entries = 32; // Initialize params structure, e.g., set flags memset(¶ms, 0, sizeof(params)); params.flags = IORING_SETUP_SQPOLL; int ret = io_uring_queue_init_params(entries, &ring, ¶ms); ``` ### Response #### Success Response (0) Returns 0 on success. #### Error Response (-errno) Returns -errno on failure. Use `strerror(3)` to get a human-readable error message. #### Response Example ```c // On success: 0 // On failure: -1 // with errno set appropriately (e.g., -EINVAL) ``` ``` -------------------------------- ### Initialize io_uring Queue (io_uring_queue_init) Source: https://unixism.net/loti/_sources/ref-liburing/setup_teardown Initializes an io_uring instance. It sets up the submission queue with a specified number of entries and applies given flags. Returns 0 on success or a negative errno on failure. ```c int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags); ``` -------------------------------- ### io_uring_setup C Function Signature Source: https://unixism.net/loti/_sources/ref-iouring/io_uring_setup This C function signature defines the io_uring_setup system call, which initializes an io_uring instance. It takes the number of queue entries and a parameters struct, returning a file descriptor for the io_uring instance. ```c #include int io_uring_setup(u32 entries, struct io_uring_params *p); ``` -------------------------------- ### io_uring_queue_mmap Source: https://unixism.net/loti/_sources/ref-liburing/setup_teardown Low-level function to memory-map the io_uring rings after `io_uring_setup` has been called. ```APIDOC ## io_uring_queue_mmap ### Description This is a low-level function you'll only want to use when you want to control a lot of aspects of the ``io_uring`` initialization. Before calling this function, you should have already called the low-level :c:func:`io_uring_setup`. You can then use this function to :man:`mmap(2)` the rings for you. ### Method C Function ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```c struct io_uring_params params; struct io_uring ring; int fd; // Assume fd is obtained from a prior io_uring_setup call // Assume params is populated int ret = io_uring_queue_mmap(fd, ¶ms, &ring); ``` ### Response #### Success Response (0) Returns 0 on success. #### Error Response (-errno) Returns -errno on failure. Use `strerror(3)` to get a human-readable error message. #### Response Example ```c // On success: 0 // On failure: -1 // with errno set appropriately ``` ``` -------------------------------- ### io_uring Cat Utility Example in C Source: https://unixism.net/loti/_sources/low_level A C code example demonstrating a 'cat' utility using the low-level io_uring interface. It includes necessary headers, defines queue depth and block size, and sets up basic io_uring structures for file I/O operations. ```c #include #include #include #include #include #include #include #include #include #include #include /* If your compilation fails because the header file below is missing, * your kernel is probably too old to support io_uring. * */ #include #define QUEUE_DEPTH 1 #define BLOCK_SZ 1024 /* This is x86 specific */ #define read_barrier() __asm__ __volatile__("" ::: "memory") #define write_barrier() __asm__ __volatile__("" ::: "memory") struct app_io_sq_ring { unsigned *head; unsigned *tail; ``` -------------------------------- ### Wakeup Condition Check for IORING_SETUP_SQPOLL (C) Source: https://unixism.net/loti/ref-iouring/io_uring_setup Provides a C code snippet demonstrating how to check and handle the IORING_SQ_NEED_WAKEUP flag when using IORING_SETUP_SQPOLL. This ensures that the application correctly wakes the kernel thread when it becomes idle and I/O is being submitted. ```c /* * Ensure that the wakeup flag is read after the tail pointer has been * written. */ smp_mb(); if (*sq_ring->flags & IORING_SQ_NEED_WAKEUP) io_uring_enter(fd, 0, 0, IORING_ENTER_SQ_WAKEUP); ``` -------------------------------- ### Main Server Initialization - C Source: https://unixism.net/loti/_sources/tutorial/webserver_liburing The entry point of the HTTP server. It sets up the listening socket, registers the SIGINT handler for graceful shutdown, initializes the io_uring ring, and starts the main server event loop. ```c int main() { int server_socket = setup_listening_socket(DEFAULT_SERVER_PORT); signal(SIGINT, sigint_handler); io_uring_queue_init(QUEUE_DEPTH, &ring, 0); server_loop(server_socket); return 0; } ``` -------------------------------- ### io_uring_queue_exit Source: https://unixism.net/loti/_sources/ref-liburing/setup_teardown Tears down an io_uring instance, unmapping ring buffers and closing the file descriptor. ```APIDOC ## io_uring_queue_exit ### Description Tear down function for ``io_uring``. Unmaps all setup shared ring buffers and closes the low-level ``io_uring`` file descriptor returned by the kernel. ### Method C Function ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```c struct io_uring ring; // Assume ring is initialized io_uring_queue_exit(&ring); ``` ### Response #### Success Response This function has a void return type and does not return a value on success. #### Error Response N/A #### Response Example ```c // No return value on success ``` ``` -------------------------------- ### Completion Handling Overview Source: https://unixism.net/loti/_sources/ref-liburing/completion This section provides a general overview of how to wait for and process I/O completions in io_uring, using the io_uring_wait_cqe function as an example. ```APIDOC ## Completion Handling in io_uring ### Description This documentation covers the functions used to manage and retrieve I/O completions from the ``io_uring`` system. After submitting requests, you can wait for completions, process their results, and mark them as seen. ### Key Concepts * **Waiting for Completion**: Use ``io_uring_wait_cqe`` to block until at least one completion is available. * **Completion Queue Entry (CQE)**: The ``io_uring_cqe`` structure holds details about a completed I/O request. * **Result Field**: The ``cqe->res`` member contains the return value of the system call (e.g., number of bytes read or error code). * **User Data**: ``io_uring_cqe_get_data(cqe)`` retrieves user-defined data associated with the request, useful for identifying requests. * **Marking as Seen**: ``io_uring_cqe_seen(ring, cqe)`` must be called after processing a completion. ### Example Usage (Conceptual) ```c int get_completion_and_print(struct io_uring *ring) { struct io_uring_cqe *cqe; int ret = io_uring_wait_cqe(ring, &cqe); if (ret < 0) { perror("io_uring_wait_cqe"); return 1; } if (cqe->res < 0) { /* The system call invoked asynchonously failed */ return 1; } /* Retrieve user data from CQE */ struct file_info *fi = io_uring_cqe_get_data(cqe); /* process this request here */ /* Mark this completion as seen */ io_uring_cqe_seen(ring, cqe); return 0; } ``` ``` -------------------------------- ### Memory Map Submission Queue Entries (C) Source: https://unixism.net/loti/ref-iouring/io_uring_setup Maps the array of submission queue entries (SQEs) into the application's address space. This allows the application to populate SQEs for I/O operations. Requires a file descriptor from io_uring_setup. ```c sqentries = mmap(0, sq_entries * sizeof(struct io_uring_sqe), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, ring_fd, IORING_OFF_SQES); ``` -------------------------------- ### io_uring Main Function and File Processing Source: https://unixism.net/loti/_sources/low_level The main entry point for the io_uring example. It handles command-line arguments, sets up the io_uring interface, and iterates through provided filenames, submitting them for processing and reading completions. ```c int main(int argc, char *argv[]) { struct submitter *s; if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } s = malloc(sizeof(*s)); if (!s) { perror("malloc"); return 1; } memset(s, 0, sizeof(*s)); if(app_setup_uring(s)) { fprintf(stderr, "Unable to setup uring!\n"); return 1; } for (int i = 1; i < argc; i++) { if(submit_to_sq(argv[i], s)) { fprintf(stderr, "Error reading file\n"); return 1; } read_from_cq(s); } return 0; } ``` -------------------------------- ### Memory Map Completion Queue Ring in C Source: https://unixism.net/loti/_sources/ref-iouring/io_uring_setup Shows how to memory map the completion queue ring. Unlike the submission queue, the completion queue entries are mapped as part of the same region as the ring buffer metadata. ```c ptr = mmap(0, cq_off.cqes + cq_entries * sizeof(struct io_uring_cqe), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, ring_fd, IORING_OFF_CQ_RING); ``` -------------------------------- ### Memory Map Submission Queue Entries in C Source: https://unixism.net/loti/_sources/ref-iouring/io_uring_setup Demonstrates memory mapping the array of submission queue entries (SQEs). This mapping allows direct access to the structures that define the I/O operations to be submitted. ```c sqentries = mmap(0, sq_entries * sizeof(struct io_uring_sqe), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, ring_fd, IORING_OFF_SQES); ``` -------------------------------- ### C Web Server Setup with liburing Source: https://unixism.net/loti/tutorial/webserver_liburing This C code sets up a listening socket for a web server using liburing. It includes error handling, socket configuration for address reuse, and binding to a specified port. Dependencies include standard C libraries and liburing. ```c #include #include #include #include #include #include #include #include #include #include #define SERVER_STRING "Server: zerohttpd/0.1\r\n" #define DEFAULT_SERVER_PORT 8000 #define QUEUE_DEPTH 256 #define READ_SZ 8192 #define EVENT_TYPE_ACCEPT 0 #define EVENT_TYPE_READ 1 #define EVENT_TYPE_WRITE 2 struct request { int event_type; int iovec_count; int client_socket; struct iovec iov[]; }; struct io_uring ring; const char *unimplemented_content = \ "HTTP/1.0 400 Bad Request\r\n" "Content-type: text/html\r\n" "\r\n" "" "" "ZeroHTTPd: Unimplemented" "" "" "

Bad Request (Unimplemented)

" "

Your client sent a request ZeroHTTPd did not understand and it is probably not your fault.

" "" ""; const char *http_404_content = \ "HTTP/1.0 404 Not Found\r\n" "Content-type: text/html\r\n" "\r\n" "" "" "ZeroHTTPd: Not Found" "" "" "

Not Found (404)

" "

Your client is asking for an object that was not found on this server.

" "" ""; /* * Utility function to convert a string to lower case. * */ void strtolower(char *str) { for (; *str; ++str) *str = (char)tolower(*str); } /* One function that prints the system call and the error details and then exits with error code 1. Non-zero meaning things didn't go well. */ void fatal_error(const char *syscall) { perror(syscall); exit(1); } /* * Helper function for cleaner looking code. * */ void *zh_malloc(size_t size) { void *buf = malloc(size); if (!buf) { fprintf(stderr, "Fatal error: unable to allocate memory.\n"); exit(1); } return buf; } /* * This function is responsible for setting up the main listening socket used by the * web server. * */ int setup_listening_socket(int port) { int sock; struct sockaddr_in srv_addr; sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) fatal_error("socket()"); int enable = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) fatal_error("setsockopt(SO_REUSEADDR)"); memset(&srv_addr, 0, sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(port); srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* We bind to a port and turn this socket into a listening * socket. * */ if (bind(sock, (const struct sockaddr *)&srv_addr, ``` -------------------------------- ### Calculate Ring Buffer Index in C Source: https://unixism.net/loti/_sources/ref-iouring/io_uring_setup Illustrates the calculation of an index into the io_uring ring buffer using a bitmask. This method is used for both the submission and completion queues to wrap around the buffer correctly. ```c index = tail & ring_mask; ``` -------------------------------- ### io_uring Structure Definition Source: https://unixism.net/loti/_sources/ref-liburing/setup_teardown Defines the core io_uring structure, which holds submission and completion queue information, flags, and a ring file descriptor. ```c struct io_uring { struct io_uring_sq sq; struct io_uring_cq cq; unsigned flags; int ring_fd; }; ``` -------------------------------- ### Memory Map Submission Queue Ring in C Source: https://unixism.net/loti/_sources/ref-iouring/io_uring_setup Demonstrates how to memory map the submission queue ring buffer using the mmap system call. It utilizes the io_sqring_offsets structure to calculate the correct size and offset for mapping. ```c ptr = mmap(0, sq_off.array + sq_entries * sizeof(__u32), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, ring_fd, IORING_OFF_SQ_RING); ``` -------------------------------- ### Prepare and Submit Readv Operation with liburing Source: https://unixism.net/loti/tutorial/cat_liburing Prepares and submits a readv operation using liburing. This involves getting a Submission Queue Entry (SQE), configuring it for a readv operation with file descriptor, iovecs, blocks, and flags, setting user data, and finally submitting the request. ```c /* Get an SQE */ struct io_uring_sqe *sqe = io_uring_get_sqe(ring); /* Setup a readv operation */ io_uring_prep_readv(sqe, file_fd, fi->iovecs, blocks, 0); /* Set user data */ io_uring_sqe_set_data(sqe, fi); /* Finally, submit the request */ io_uring_submit(ring); ``` -------------------------------- ### C Web Server Setup with liburing Source: https://unixism.net/loti/_sources/tutorial/webserver_liburing This C code sets up a basic web server using the liburing library for asynchronous I/O. It includes definitions for request types, buffer sizes, and error handling. The core functionality involves preparing the io_uring interface and setting up a listening socket for incoming connections. ```c #include #include #include #include #include #include #include #include #include #include #define SERVER_STRING "Server: zerohttpd/0.1\r\n" #define DEFAULT_SERVER_PORT 8000 #define QUEUE_DEPTH 256 #define READ_SZ 8192 #define EVENT_TYPE_ACCEPT 0 #define EVENT_TYPE_READ 1 #define EVENT_TYPE_WRITE 2 struct request { int event_type; int iovec_count; int client_socket; struct iovec iov[]; }; struct io_uring ring; const char *unimplemented_content = \ "HTTP/1.0 400 Bad Request\r\n" "Content-type: text/html\r\n" "\r\n" "" "" "ZeroHTTPd: Unimplemented" "" "" "

Bad Request (Unimplemented)

" "

Your client sent a request ZeroHTTPd did not understand and it is probably not your fault.

" "" ""; const char *http_404_content = \ "HTTP/1.0 404 Not Found\r\n" "Content-type: text/html\r\n" "\r\n" "" "" "ZeroHTTPd: Not Found" "" "" "

Not Found (404)

" "

Your client is asking for an object that was not found on this server.

" "" ""; /* * Utility function to convert a string to lower case. * */ void strtolower(char *str) { for (; *str; ++str) *str = (char)tolower(*str); } /* One function that prints the system call and the error details and then exits with error code 1. Non-zero meaning things didn't go well. */ void fatal_error(const char *syscall) { perror(syscall); exit(1); } /* * Helper function for cleaner looking code. * */ void *zh_malloc(size_t size) { void *buf = malloc(size); if (!buf) { fprintf(stderr, "Fatal error: unable to allocate memory.\n"); exit(1); } return buf; } /* * This function is responsible for setting up the main listening socket used by the * web server. * */ int setup_listening_socket(int port) { int sock; struct sockaddr_in srv_addr; sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) fatal_error("socket()"); int enable = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) fatal_error("setsockopt(SO_REUSEADDR)"); memset(&srv_addr, 0, sizeof(srv_addr)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = htons(port); srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* We bind to a port and turn this socket into a listening * socket. * */ if (bind(sock, (const struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) fatal_error("bind"); if (listen(sock, 10) < 0) fatal_error("listen"); return (sock); } int add_accept_request(int server_socket, struct sockaddr_in *client_addr, socklen_t *client_addr_len) { struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_accept(sqe, server_socket, (struct sockaddr *) client_addr, client_addr_len, 0); struct request *req = malloc(sizeof(*req)); req->event_type = EVENT_TYPE_ACCEPT; io_uring_sqe_set_data(sqe, req); io_uring_submit(&ring); return 0; } int add_read_request(int client_socket) { struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); struct request *req = malloc(sizeof(*req) + sizeof(struct iovec)); req->iov[0].iov_base = malloc(READ_SZ); req->iov[0].iov_len = READ_SZ; req->event_type = EVENT_TYPE_READ; req->client_socket = client_socket; memset(req->iov[0].iov_base, 0, READ_SZ); ``` -------------------------------- ### Handle HTTP GET Method and File Path Resolution (C) Source: https://unixism.net/loti/tutorial/webserver_liburing This C code function, `handle_get_method`, processes an HTTP GET request. It constructs the final file path, appending 'public' and handling trailing slashes by adding 'index.html'. It uses the `stat` system call to get file information and checks if the path points to a regular file. If successful, it prepares a request structure and calls `send_headers` and `copy_file_contents`. Includes error handling for '404 Not Found'. ```c void handle_get_method(char *path, int client_socket) { char final_path[1024]; /* If a path ends in a trailing slash, the client probably wants the index file inside of that directory. */ if (path[strlen(path) - 1] == '/') { strcpy(final_path, "public"); strcat(final_path, path); strcat(final_path, "index.html"); } else { strcpy(final_path, "public"); strcat(final_path, path); } /* The stat() system call will give you information about the file * like type (regular file, directory, etc), size, etc. */ struct stat path_stat; if (stat(final_path, &path_stat) == -1) { printf("404 Not Found: %s (%s)\n", final_path, path); handle_http_404(client_socket); } else { /* Check if this is a normal/regular file and not a directory or something else */ if (S_ISREG(path_stat.st_mode)) { struct request *req = zh_malloc(sizeof(*req) + (sizeof(struct iovec) * 6)); req->iovec_count = 6; req->client_socket = client_socket; send_headers(final_path, path_stat.st_size, req->iov); copy_file_contents(final_path, path_stat.st_size, &req->iov[5]); printf("200 %s %ld bytes\n", final_path, path_stat.st_size); } } ``` -------------------------------- ### io_uring Ring Setup and Memory Mapping (C) Source: https://unixism.net/loti/_sources/low_level The `app_setup_uring` function initializes an io_uring instance and maps its submission and completion queues into user space. It handles differences in memory mapping strategies between newer and older kernels, using `mmap` with appropriate flags. Dependencies include `sys/mman.h` and `fcntl.h`. ```c int app_setup_uring(struct submitter *s) { struct app_io_sq_ring *sring = &s->sq_ring; struct app_io_cq_ring *cring = &s->cq_ring; struct io_uring_params p; void *sq_ptr, *cq_ptr; memset(&p, 0, sizeof(p)); s->ring_fd = io_uring_setup(QUEUE_DEPTH, &p); if (s->ring_fd < 0) { perror("io_uring_setup"); return 1; } int sring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned); int cring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe); if (p.features & IORING_FEAT_SINGLE_MMAP) { if (cring_sz > sring_sz) { sring_sz = cring_sz; } cring_sz = sring_sz; } sq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_SQ_RING); if (sq_ptr == MAP_FAILED) { perror("mmap"); return 1; } if (p.features & IORING_FEAT_SINGLE_MMAP) { cq_ptr = sq_ptr; } else { cq_ptr = mmap(0, cring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, s->ring_fd, IORING_OFF_CQ_RING); if (cq_ptr == MAP_FAILED) { perror("mmap"); return 1; } } return 0; } ``` -------------------------------- ### Handle HTTP GET Method - C Source: https://unixism.net/loti/_sources/tutorial/webserver_liburing Processes an HTTP GET request by determining the requested file path, checking its existence and type using stat(), and if it's a regular file, sends the appropriate HTTP headers and file contents. It handles 404 errors for non-existent or inaccessible files. ```c void handle_get_method(char *path, int client_socket) { char final_path[1024]; /* If a path ends in a trailing slash, the client probably wants the index file inside of that directory. */ if (path[strlen(path) - 1] == '/') { strcpy(final_path, "public"); strcat(final_path, path); strcat(final_path, "index.html"); } else { strcpy(final_path, "public"); strcat(final_path, path); } /* The stat() system call will give you information about the file * like type (regular file, directory, etc), size, etc. */ struct stat path_stat; if (stat(final_path, &path_stat) == -1) { printf("404 Not Found: %s (%s)\n", final_path, path); handle_http_404(client_socket); } else { /* Check if this is a normal/regular file and not a directory or something else */ if (S_ISREG(path_stat.st_mode)) { struct request *req = zh_malloc(sizeof(*req) + (sizeof(struct iovec) * 6)); req->iovec_count = 6; req->client_socket = client_socket; send_headers(final_path, path_stat.st_size, req->iov); copy_file_contents(final_path, path_stat.st_size, &req->iov[5]); printf("200 %s %ld bytes\n", final_path, path_stat.st_size); add_write_request( req); } else { handle_http_404(client_socket); printf("404 Not Found: %s\n", final_path); } } } ```