poll function
poll is a function in the character device driver in Linux. poll is essentially no different from select. It copies the array passed in by the user to the kernel space, and then queries the device status corresponding to each fd. If the device is ready, add an item to the device waiting queue and continue to traverse. If no ready device is found after traversing all fd, suspend the current process, Until the device is ready or active timeout, it will traverse fd again after being awakened. This process has gone through many unnecessary traversals.
int poll(struct pollfd *fds, nfds_t nfds, int timeout); parameter: pollfd:Point to a struct pollfd Array of types, each pollfd Structure specifies a monitored file descriptor, indicating poll()Monitor multiple file descriptors. Of each structure events Domain is the event mask that monitors the file descriptor. This domain is set by the user. revents Domain is the operation result event mask of the file descriptor. The kernel sets this domain when the call returns, events Any event requested in the domain may be revents Field. nfds Specifies the number of listening elements in the array. timeout Specifies the number of milliseconds to wait, regardless of I/O Are you ready, poll Will return. timeout Specify a negative value to indicate an infinite timeout so that poll() Hang until a specified event occurs; timeout 0 indicates poll Call returns immediately and lists ready I/O File descriptor, but does not wait for other events. struct pollfd { int fd; /* File descriptor */ short events; /* Waiting events */ short revents; /* What actually happened */ } ;
The following table lists some constant values of the specified events flag and the test events flag:
Timeout: when the function is called successfully, poll() returns the number of file descriptors whose events field in the structure is not 0; If no event occurs before the timeout, poll() returns 0; On failure, poll() returns - 1 and sets errno to one of the following values:
EBADF The file descriptor specified in one or more structures is invalid. EFAULTfds The address pointed to by the pointer exceeds the address space of the process. EINTR A signal is generated before the requested event, and the call can be restarted. EINVALnfds Parameter exceeded PLIMIT_NOFILE Value. ENOMEM There is not enough available memory to complete the request.
epoll function
epoll operates on file descriptors in two modes: LT (level trigger) and ET (edge trigger). LT mode is the default mode. The differences between LT mode and et mode are as follows:
Horizontal trigger LT mode:
- The main feature of horizontal trigger is that if the user is listening for epoll events, it will be copied when there are events in the kernel
Give user status events, but if the user only processes them once, the remaining unprocessed events will be processed next time
epoll_wait returns the event again.
In this way, if the user never handles this event, there will be a copy of the event from the kernel to the user every time
Bei, it consumes performance, but the horizontal trigger is relatively safe. At least, the event will not be lost unless the user handles it
Finish
Edge triggered ET mode:
- The edge trigger is opposite to the horizontal trigger. When an event arrives in the kernel, the user will be notified only once. As for the use of
Whether the user handles it or not will not be notified in the future. This reduces the copy process and increases performance, but
Relatively speaking, if the user forgets to handle it carelessly, the event will be lost.
The edge trigger is triggered only once, and the horizontal trigger will be triggered all the time.
int epoll_create(int size); function: establish epoll parameter: size Ignore not
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev); function:modify epoll Addition, deletion, modification and query of parameter: epfd yes epoll_create Return value op Is used to specify the operation to be performed fd:Indicates the setting of which file descriptor in the interest list to modify. This parameter can be representative of pipeline FIFO,Socket POSIX Message queuing inotify Instance, terminal, device, or even another epoll File descriptor for instance ev:Pointer structure of
op specifies the values of operations: add, delete, and modify listening to fd
EPOLL_CTL_ADD | Add fd to epoll |
---|---|
EPOLL_CTL_MOD | Modifying registered fd events |
EPOLL_CTL_DE | epfd delete an fd |
Structure epoll_ The pointer to event and the structure are defined as follows:
typedef union epoll_data { void *ptr; //Pointer to user-defined data int fd; //File descriptor uint32_t u32; //32-bit integer uint64_t u64; //64 bit integer } epoll_data_t; struct epoll_event { uint32_t events; //What properties are set epoll_data_t data; //user data };
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout); function: Wait for ready event parameter: epfd yes epoll_create()Return value of evlist The information about the ready state file descriptor is returned in the structure array pointed to evlist The caller is responsible for applying for the space; maxevents Designated Office evlist The number of elements contained in the array; timeout Used to determine epoll_wait()There are several blocking behaviors: If timeout be equal to-1,The call will block until an event is generated on the file descriptor in the interest list or until a signal is captured. If timeout Equal to 0, perform a non blocking check to see which event is generated on the descriptor in the interest list. If timeout Greater than 0, the call will block at most timeout Milliseconds until an event occurs on the file descriptor or until a signal is captured
The values in events are as follows:
constant | explain | As epoll_ Input of ctl() | As epoll_ Return of wait() |
---|---|---|---|
EPOLLIN | Non high priority data can be read | can | can |
EPOLLPRI | High priority data can be read | can | can |
EPOLLRDHUP | socket end-to-end shutdown | can | can |
EPOLLOUT | Common data writable | can | can |
EPOLLET | Use edge triggered event notification | can | |
EPOLLONESHOT | Disable checking after event notification | can | |
EPOLLERR | An error occurred | can | |
POLLHUP | Hang up occurred | can |
Advantages of epoll
- Support a process to open a large number of socket descriptors (fd)
- IO efficiency does not decrease linearly with the increase of fd number
- Using mmap to speed up message passing between kernel and user space
- Kernel tuning
poll code implementation
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <getopt.h> #include <stdlib.h> #include <sys/select.h> #include <ctype.h> #include <libgen.h> #include <poll.h> #define ARRAY_ Size (x) (sizeof (x) / sizeof (x [0]) / / find the number of array elements int socket_Server_init(char *listen_ip,int listen_port); void sqlite_tem(char *buf); void print_usage(char *progname) { printf("%s usage: \n", progname); printf("-p(--port): sepcify server listen port.\n"); printf("-h(--Help): print this help information.\n"); printf("-d(--daemon):set program running on background\n"); return ; } int main (int argc, char **argv) { int listenfd = -1; int clifd; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; socklen_t len; int serv_port = 0; int ch; int rv ; int on = 1; char buf[1024]; int i,j; int found; int max; int daemon_run = 0; char *progname = NULL; struct pollfd fds_arry[1024]; struct option opts[] = { {"daemon",no_argument,NULL,'b'}, {"port", required_argument, NULL, 'p'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; progname = basename(argv[0]); while( (ch=getopt_long(argc, argv, "bp:h", opts, NULL)) != -1 ) { switch(ch) { case 'p': serv_port=atoi(optarg); break; case 'b': daemon_run=1; break; case 'h': print_usage(argv[0]); return 0; } } if( !serv_port ) { print_usage(argv[0]); return 0; } if( (listenfd = socket_Server_init(NULL, serv_port)) <0) { printf("ERROR %s server listen on port %d failure\n",argv[0],serv_port); return -1; } printf("%s server start to listen on port %d\n",argv[0],serv_port); if(daemon_run)//Set process background running { daemon(0,0); } for(i=0; i<ARRAY_SIZE(fds_arry); i++)//Traversal array { fds_arry[i].fd=-1;//Initialize the entire array to - 1. Why - 1; Because this array stores file descriptors, the system will generate three file descriptors 0, 1 and 2 } fds_arry[0].fd = listenfd;//Assign the first array to listenfd //A tcp network link contains a quadruple: source ip, destination ip, source port, destination port fds_arry[0].events = POLLIN; max = 0; for( ; ; ) { rv = poll(fds_arry, max+1, -1); if(rv < 0) { printf("POLL failure:%s\n",strerror(errno)); break; } else if( rv ==0 ) { printf("poll get timeout\n"); continue; } if( fds_arry[0].revents & POLLIN )//Determines whether the specified descriptor is in the collection { if( (clifd=accept(listenfd,(struct sockaddr *)NULL,NULL)) < 0) { printf("accept new client failure:%s\n",strerror(errno)); continue; } found = 0; for(i=0; i<ARRAY_SIZE(fds_arry);i++) { if( fds_arry[i].fd< 0 ) { printf("accept new client [%d] and add it into array\n",clifd); fds_arry[i].fd = clifd; fds_arry[i].events = POLLIN; found = 1; break; } } if(!found) { printf("accept new client [%d] but full, so refuse it\n",clifd); close(clifd); continue; } max = i>max ? i:max; if( rv <=0 ) continue; } else { for ( i=1; i<ARRAY_SIZE(fds_arry);i++) { if(fds_arry[i].fd < 0) continue; if( (rv=read(fds_arry[i].fd,buf,sizeof(buf))) <=0) { printf("socket [%d] read failure or get disconnected\n",fds_arry[i].fd); close(fds_arry[i].fd); fds_arry[i].fd=-1; } else { printf("%s\n",buf); sqlite_tem(buf); printf("Database inserted successfully!\n"); } } } } cleanup: close(listenfd); return 0; } int socket_Server_init(char *listen_ip, int listen_port) { struct sockaddr_in servaddr; int rv = 0; int on = 1; int listenfd; if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("use socket()to create a TCP socket failure:%s\n",strerror(errno)); return -1; } setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family=AF_INET; servaddr.sin_port = htons(listen_port); if( !listen_ip ) { servaddr.sin_addr.s_addr = htonl(INADDR_ANY); } else { if(inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <=0) { printf("inet_pton set listen IP address failure\n"); rv = -2; goto cleanup; } } if( bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0) { printf("socket[%d] bind to port failure:%s\n",listenfd,strerror(errno)); rv = -3; goto cleanup; } if( listen(listenfd,13) < 0) { printf("use bind to bind tcp socket failure:%s\n",strerror(errno)); rv = -4; goto cleanup; } cleanup: if(rv < 0) close(listenfd); else rv = listenfd; return rv; } void sqlite_tem(char *buf) { int nrow=0; int ncolumn = 0; char **azResult=NULL; int rv; sqlite3 *db=NULL; char *zErrMsg = 0; char sql1[100]; char *ipaddr=NULL; char *datetime=NULL; char *temper=NULL; char *sql = "create table if not exists temperature(ipaddr char(30), datetime char(50), temper char(30))"; ipaddr = strtok(buf,"/"); datetime = strtok(NULL, "/"); temper = strtok(NULL, "/"); rv = sqlite3_open("tempreture.db", &db); if(rv) { printf("Can't open database:%s\n", sqlite3_errmsg(db)); sqlite3_close(db); return; } printf("opened a sqlite3 database named tempreture.db successfully!\n"); int ret = sqlite3_exec(db,sql, NULL, NULL, &zErrMsg); if(ret != SQLITE_OK) { printf("create table fail: %s\n",zErrMsg); } if(snprintf(sql1,sizeof(sql1), "insert into temper values('%s','%s','%s')", ipaddr, datetime, temper) < 0) { printf("Failed to write data\n"); } //printf("Write data successfully!\n"); sqlite3_exec(db, sql1, 0, 0, &zErrMsg); sqlite3_free(zErrMsg); sqlite3_close(db); return; }
epoll code implementation
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #include <string.h> #include <errno.h> #include <getopt.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/epoll.h> #include "sqlite3.h" #define BACKLOG 13 #define MAX_EVENTS 512 void print_usage(char* progname); void sig_stop(int signum); void sqlite_tem(char *buf); int socket_listen(char *listen_ip, int port); static int g_stop = 0; int main(int argc, char* argv[]) { int rv; int ret; int opt; int idx; int port; int log_fd; int ch = 1; int daemon_run = 0; int ser_fd = -1; int cli_fd = -1; struct sockaddr_in cli_addr; socklen_t cliaddr_len = 20; int epollfd; struct epoll_event event; struct epoll_event event_array[MAX_EVENTS]; int events; int found; int a; int i; char *zErrMsg; sqlite3 *db; char buf[1024]; struct option opts[] = { {"daemon", no_argument, NULL, 'd'}, {"port", required_argument, NULL, 'p'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; while ((opt = getopt_long(argc, argv, "dp:h", opts, &idx)) != -1) { switch (opt) { case 'd': daemon_run = 1; break; case 'p': port = atoi(optarg); break; case 'h': print_usage(argv[0]); return 0; } } if (!port) { print_usage(argv[0]); return 0; } /*Create log*/ if (daemon_run) { printf("Program %s is running at the background now\n", argv[0]); log_fd = open("receive_temper.log", O_CREAT | O_RDWR, 0666); if (log_fd < 0) { printf("Open the logfile failure : %s\n", strerror(errno)); return 0; } dup2(log_fd, STDOUT_FILENO); dup2(log_fd, STDERR_FILENO); if ((daemon(1, 1)) < 0) { printf("Deamon failure : %s\n", strerror(errno)); return 0; } } /*Installation signal*/ signal(SIGUSR1, sig_stop); /*Call socket*/ if( (ser_fd = socket_listen(NULL, port)) < 0 ) { printf("ERROR: %s server listen on serv_port %d failure\n", argv[0], port); return -2; } printf("server start to listen on serv_port %d\n", port); /*Create opll*/ if ((epollfd = epoll_create(MAX_EVENTS)) < 0) { printf("epoll_create failure:%s\n", strerror(errno)); return 0; } event.events = EPOLLIN; event.data.fd = ser_fd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ser_fd, &event) < 0) { printf("epoll add ser_fd failure:%s\n", strerror(errno)); return 0; } while (!g_stop) { events = epoll_wait(epollfd, event_array, MAX_EVENTS, -1); if (events < 0) { printf("epoll failure:%s\n", strerror(errno)); break; } else if (events == 0) { printf("epoll get timeout\n"); continue; } for (i = 0;i < events;i++) { if ((event_array[i].events & EPOLLERR) || (event_array[i].events & EPOLLHUP)) { printf("epoll_wait get error on fd[%d]:%s\n", event_array[i].data.fd, strerror(errno)); epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL); close(event_array[i].data.fd); } if (event_array[i].data.fd == ser_fd) { cli_fd = accept(ser_fd, (struct sockaddr*) & cli_addr, &cliaddr_len); if (cli_fd < 0) { printf("Accept the request from client failure:%s\n", strerror(errno)); continue; } event.data.fd = cli_fd; event.events = EPOLLIN; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, cli_fd, &event) < 0) { printf("epoll add client socket failure:%s\n", strerror(errno)); close(cli_fd); continue; } } else { memset(buf, 0, sizeof(buf)); a = read(cli_fd, buf, sizeof(buf)); if (a < 0) { printf("Read information from client failure:%s\n", strerror(errno)); close(cli_fd); exit(0); } else if (a == 0) { printf("The connection with client has broken!\n"); close(cli_fd); exit(0); } else { printf("%s\n",buf); sqlite_tem(buf); printf("Database inserted successfully!\n"); } } } } close(ser_fd); return 0; } /*Help information*/ void print_usage(char* progname) { printf("-d(--daemon):let program run in the background.\n"); printf("-p(--port):enter server port.\n"); printf("-h(--help):print this help information.\n"); return; } /*Signal function*/ void sig_stop(int signum) { if (SIGUSR1 == signum) { g_stop = 1; } return; } /*socket function*/ int socket_listen(char *listen_ip, int port) { int rv = 0; int on = 1; int ser_fd; struct sockaddr_in servaddr; if ( (ser_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno)); return -1; } setsockopt(ser_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(port); if( !listen_ip ) { servaddr.sin_addr.s_addr = htonl(INADDR_ANY); } else { if( inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0 ) { printf("Inet_pton() set listen IP address failure\n"); rv = -2; goto cleanup; } } if( bind(ser_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) { printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno)); rv = -3; goto cleanup; } if( listen(ser_fd, 64) < 0 ) { printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno)); rv = -4; goto cleanup; } cleanup: if( rv < 0 ) close(ser_fd); else rv = ser_fd; return rv; } /*database*/ void sqlite_tem(char *buf) { int nrow=0; int ncolumn = 0; char **azResult=NULL; int rv; sqlite3 *db=NULL; char *zErrMsg = 0; char sql1[100]; char *ipaddr=NULL; char *datetime=NULL; char *temper=NULL; char *sql = "create table if not exists temperature(ipaddr char(30), datetime char(50), temper char(30))"; ipaddr = strtok(buf,"/"); datetime = strtok(NULL, "/"); temper = strtok(NULL, "/"); rv = sqlite3_open("tempreture.db", &db); if(rv) { printf("Can't open database:%s\n", sqlite3_errmsg(db)); sqlite3_close(db); return; } printf("opened a sqlite3 database named tempreture.db successfully!\n"); int ret = sqlite3_exec(db,sql, NULL, NULL, &zErrMsg); if(ret != SQLITE_OK) { printf("create table fail: %s\n",zErrMsg); } if(snprintf(sql1,sizeof(sql1), "insert into temper values('%s','%s','%s')", ipaddr, datetime, temper) < 0) { printf("Failed to write data\n"); } //printf("Write data successfully!\n"); sqlite3_exec(db, sql1, 0, 0, &zErrMsg); sqlite3_free(zErrMsg); sqlite3_close(db); return; }