Applying C - Interrupts & Polling
Written by Harry Fairhead   
Monday, 26 December 2022
Article Index
Applying C - Interrupts & Polling
Event Driven Socket Server

An Event Driven Socket Server

As an example of using poll let’s return to the socket server described in the previous chapter. If this isn't the case we need to make the call to accept non-blocking. The simplest way of doing this is to OR SOCK_NONBLOCK in the call to socket: 

int sockfd = socket(server→ai_family,
                    server->ai_socktype| SOCK_NONBLOCK,
                    server->ai_protocol);

This change makes the attempt to read from the socket:

int client_fd = accept(sockfd,
      (struct sockaddr *) &client_addr, &addr_size);

return immediately but with -1 if there is no data to read. Our only solution is to repeatedly attempt to read the data using accept:

for(;;){
int client_fd = accept(sockfd,
     (struct sockaddr *) &client_addr, &addr_size);
if(client_fd>0){
 process the data;
}

You can see that this keeps the thread busy in a tight polling loop.

A much better solution is to use poll to suspend the thread until there is data to read:

struct pollfd fdset[1];
fdset[0].fd = sockfd;
fdset[0].events = POLLIN;
fdset[0].revents = 0;
poll(fdset, 1, 10000);

The call now waits until there is data read to read from the socket or timeout occurs after 10 seconds. This means we can follow the call with code to read the data, but only after checking that the event occurred and it wasn’t a timeout or a signal:

if (fdset[0].revents & POLLIN) {
  int client_fd = accept(sockfd,
(struct sockaddr *) &client_addr,
&addr_size); process data:

Of course this only allows one client to use the server. We still need to put the poll and the processing in a loop:

struct pollfd fdset[1];
for (;;) {
   fdset[0].fd = sockfd;
   fdset[0].events = POLLIN;
   fdset[0].revents = 0;
   poll(fdset, 1, 10000);
   if (fdset[0].revents & POLLIN) {
     int client_fd = accept(sockfd, 
(struct sockaddr *) &client_addr, &addr_size); int n = read(client_fd, buffer, 2048); printf("%s", buffer); n = write(client_fd, data, strlen(data)); close(client_fd); printf("client request"); fflush(stdout); } if (fdset[0].revents == 0) { printf("timeout"); fflush(stdout); } }

Notice that you have to initialize the struct for each call to poll and you can also add a check for a timeout.

You can make this much more sophisticated but this is the basic idea. It is common for example to pass the request on to a separate thread so that the loop can go back to waiting for the next client.

The complete program is:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <poll.h>
int main(int argc, char** argv) {
    struct addrinfo hints, *server;
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    getaddrinfo(NULL, "1024", &hints, &server);
    int sockfd = socket(server→ai_family,
server->ai_socktype| SOCK_NONBLOCK,
server->ai_protocol); bind(sockfd, server->ai_addr, server->ai_addrlen); listen(sockfd, 10); struct sockaddr_storage client_addr; socklen_t addr_size = sizeof client_addr; char buffer[2048]; char headers[] = "HTTP/1.0 200 OK\r\nServer:C\r\n
Content-type: text/html\r\n\r\n"; char html[] = "<html><head><title>Hello HTTP World
</title></head>
<body><p>Hello HTTP World</p></body></html>\r\n"; char data[2048] = {0}; snprintf(data, sizeof data, "%s %s", headers, html); struct pollfd fdset[1]; for (;;) { fdset[0].fd = sockfd; fdset[0].events = POLLIN; fdset[0].revents = 0; int rc=poll(fdset, 1, 10000); if (fdset[0].revents & POLLIN) { int client_fd = accept(sockfd,
(struct sockaddr *) &client_addr, &addr_size); int n = read(client_fd, buffer, 2048); printf("%s", buffer); n = write(client_fd, data, strlen(data)); close(client_fd); printf("client request"); fflush(stdout); } if (rc == 0) { printf("timeout"); fflush(stdout); } } return (EXIT_SUCCESS); }

In book but not included in this extract

  • GPIO Interrupts and Poll
  • Complete Listing 
  • Poll on Another Thread

Summary

  • Under Linux the closest you can get to an interrupt in user space is to use the poll or select function to wait on a file descriptor.

  • This might seem restrictive, but as most things in Linux are presented as files it usually provides a way of achieving the same result as a true interrupt.

  • If you use poll or select on a thread then the thread is suspended until the file descriptor is ready to be processed. This looks wasteful, but the system can run another thread while the original is waiting.

  • The closest you can get to an interrupt in user space is to poll or select on a new thread. The new thread waits for the event and then runs the equivalent of the interrupt handler.

 

Now available as a paperback or ebook from Amazon.

Applying C For The IoT With Linux

  1. C,IoT, POSIX & LINUX
  2. Kernel Mode, User Mode & Syscall
  3. Execution, Permissions & Systemd
    Extract Running Programs With Systemd
  4. Signals & Exceptions
    Extract  Signals
  5. Integer Arithmetic
    Extract: Basic Arithmetic As Bit Operations
    Extract: BCD Arithmetic  ***NEW
  6. Fixed Point
    Extract: Simple Fixed Point Arithmetic
  7. Floating Point 
  8. File Descriptors
    Extract: Simple File Descriptors 
    Extract: Pipes 
  9. The Pseudo-File System
    Extract: The Pseudo File System
    Extract: Memory Mapped Files 
  10. Graphics
    Extract: framebuffer
  11. Sockets
    Extract: Sockets The Client
    Extract: Socket Server
  12. Threading
    Extract:  Pthreads
    Extract:  Condition Variables
    Extract:  Deadline Scheduling
  13. Cores Atomics & Memory Management
    Extract: Applying C - Cores 
  14. Interupts & Polling
    Extract: Interrupts & Polling 
  15. Assembler
    Extract: Assembler

Also see the companion book: Fundamental C

<ASIN:1871962609>

<ASIN:1871962617>

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Lightbend Announces Akka 3
15/11/2024

Lightbend, the company that developed Akka, has announced Akka 3, and has changed its name to Akka. The company produces cloud-native microservices frameworks, and Akka is used for building distribute [ ... ]



Wasmer 5 Adds iOS Support
12/11/2024

The Wasmer team has released Wasmer 5.0. The WebAssembly runtime adds experimental support for more back ends including V8, Wasmi and WAMR. It also now has iOS support, and upgraded compilers includin [ ... ]


More News

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Wednesday, 28 December 2022 )