I am trying handle 4 different named unix pipes in a single listener process.
I tried to use the select to handle the file descriptor of the pipes.I opened all the named pipes in a non blocking mode
I am having a issue, select is not at all sleeping. Continuously running in a loop.
I dont know where is the problem in my code. I pasted my code below.
Always retaining the last file descriptor in select call eventhough it doesnt have a content in pipe.
Please suggest what is wrong in code?
Code
Pipe Open call(Invoked in Constructor)
diag_fd = open(DIAG_PIPE , O_RDONLY | O_NONBLOCK );
Main Loop
while(1)
{
FD_ZERO(&fds);
FD_SET(cp_fd, &fds);
FD_SET(diag_fd, &fds);
FD_SET(err_fd, &fds);
FD_SET(perf_fd, &fds);
if (select(cp_fd+1, &fds, NULL, NULL, &tv) < 0)
{
perror("select");
return ;
}
for (int fd = diag_fd; fd <= cp_fd; fd++)
{
if (FD_ISSET(fd, &fds))
{
if (fd == diag_fd)
{
ProcessDiagLogs();
}
else if (fd == err_fd)
{
ProcessErrLogs();
}
else if (fd == perf_fd)
{
ProcessPerfLogs();
}
else if (fd == cp_fd)
{
ProcessCPLogs();
}
}
}
Read call in One File Descriptor:
ProcessDiagLogs()
do
{
if ((num = read(diag_fd, s, BUF_LENGTH)) == -1)
perror("read");
else {
s[num] = '\0';
fputs(s, filed);
fflush(filed);
}
} while (num > 0);
select() allows you to monitor one or more file descriptors, waiting until one of them becomes "ready", i.e. data is available for.
The const struct timespec *timeout, tv, in your case specifies the timeout period, or how log he select waits for data until it returns ( this behaves like a sleep ). if the timeout is 0, the select return immediately, if it's NULL it can block indefinitely.
You don't show in your code how you initialized tv, but I'm going to guess it's zero, hence the behavior you are seeing.
Try initializing the timeout before you call select and see if that helps:
tv.tv_sec = 1;//or any other value you see fit
tv.tv_usec= 0;
Related
I'm initializing a daemon in C in a Debian:
/**
* Initializes the daemon so that mcu.serial would listen in the background
*/
void init_daemon()
{
pid_t process_id = 0;
pid_t sid = 0;
// Create child process
process_id = fork();
// Indication of fork() failure
if (process_id < 0) {
printf("Fork failed!\n");
logger("Fork failed", LOG_LEVEL_ERROR);
exit(1);
}
// PARENT PROCESS. Need to kill it.
if (process_id > 0) {
printf("process_id of child process %i\n", process_id);
exit(0);
}
//unmask the file mode
umask(0);
//set new session
sid = setsid();
if(sid < 0) {
printf("could not set new session");
logger("could not set new session", LOG_LEVEL_ERROR);
exit(1);
}
// Close stdin. stdout and stderr
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
The main daemon runs in the background and monitors a serial port to communicate with a microcontroller - it reads peripherals (such as button presses) and passes information to it. The main functional loop is
int main(int argc, char *argv[])
{
// We need the port to listen to commands writing
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
logger("ERROR, no port provided", LOG_LEVEL_ERROR);
exit(1);
}
int portno = atoi(argv[1]);
// Initialize serial port
init_serial();
// Initialize server for listening to socket
init_server(portno);
// Initialize daemon and run the process in the background
init_daemon();
// Timeout for reading socket
fd_set setSerial, setSocket;
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
char bufferWrite[BUFFER_WRITE_SIZE];
char bufferRead[BUFFER_READ_SIZE];
int n;
int sleep;
int newsockfd;
while (1)
{
// Reset parameters
bzero(bufferWrite, BUFFER_WRITE_SIZE);
bzero(bufferRead, BUFFER_WRITE_SIZE);
FD_ZERO(&setSerial);
FD_SET(fserial, &setSerial);
FD_ZERO(&setSocket);
FD_SET(sockfd, &setSocket);
// Start listening to socket for commands
listen(sockfd,5);
clilen = sizeof(cli_addr);
// Wait for command but timeout
n = select(sockfd + 1, &setSocket, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
}
// This is for READING button
else if (n == 0) {
// This timeout is okay
// This allows us to read the button press as well
// Now read the response, but timeout if nothing returned
n = select(fserial + 1, &setSerial, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
} else if (n == 0) {
// timeout
// This is an okay tiemout; i.e. nothing has happened
} else {
n = read(fserial, bufferRead, sizeof bufferRead);
if (n > 0) {
logger(bufferRead, LOG_LEVEL_INFO);
if (strcmp(stripNewLine(bufferRead), "ev b2") == 0) {
//logger("Shutting down now", LOG_LEVEL_INFO);
system("shutdown -h now");
}
} else {
logger("Could not read button press", LOG_LEVEL_WARN);
}
}
}
// This is for WRITING COMMANDS
else {
// Now read the command
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0 || n < 0) logger("Could not accept socket port", LOG_LEVEL_ERROR);
// Now read the command
n = read(newsockfd, bufferWrite, BUFFER_WRITE_SIZE);
if (n < 0) {
logger("Could not read command from socket port", LOG_LEVEL_ERROR);
} else {
//logger(bufferWrite, LOG_LEVEL_INFO);
}
// Write the command to the serial
write(fserial, bufferWrite, strlen(bufferWrite));
sleep = 200 * strlen(bufferWrite) - timeout.tv_usec; // Sleep 200uS/byte
if (sleep > 0) usleep(sleep);
// Now read the response, but timeout if nothing returned
n = select(fserial + 1, &setSerial, NULL, NULL, &timeout);
if (n == -1) {
// Error. Handled below
} else if (n == 0) {
// timeout
sprintf(bufferRead, "err\r\n");
logger("Did not receive response from MCU", LOG_LEVEL_WARN);
} else {
n = read(fserial, bufferRead, sizeof bufferRead);
}
// Error reading from the socket
if (n < 0) {
logger("Could not read response from serial port", LOG_LEVEL_ERROR);
} else {
//logger(bufferRead, LOG_LEVEL_INFO);
}
// Send MCU response to client
n = write(newsockfd, bufferRead, strlen(bufferRead));
if (n < 0) logger("Could not write confirmation to socket port", LOG_LEVEL_ERROR);
}
close(newsockfd);
}
close(sockfd);
return 0;
}
But the CPU usages is always at 100%. Why is that? What can I do?
EDIT
I commented out the entire while loop and made the main function as simple as:
int main(int argc, char *argv[])
{
init_daemon();
while(1) {
// All commented out
}
return 0;
}
And I'm still getting 100% cpu usage
You need to set timeout to the wanted value on every iteration, the struct gets modified on Linux so I think your loop is not pausing except for the first time, i.e. select() is only blocking the very first time.
Try to print tv_sec and tv_usec after select() and see, it's modified to reflect how much time was left before select() returned.
Move this part
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
inside the loop before the select() call and it should work as you expect it to, you can move many delcarations inside the loop too, that would make your code easier to maintan, you could for example move the loop content to a function in the future and that might help.
This is from the linux manual page select(2)
On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1-2001 permits either behavior.) This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple select()s in a loop without reinitializing it. Consider timeout to be undefined after select() returns.
I think the bold part in the qoute is the important one.
I am trying to do a non blocking read but the function never returns. Can someone suggest something? Here is my code to set nonblocking fd.
from_ap = open(FFS_GBEMU_OUT, O_RDWR|O_NONBLOCK);
if (from_ap < 0)
return from_ap;
I have also tried this with similar results
from_ap = open(FFS_GBEMU_OUT, O_RDWR);
int status = fcntl(from_ap, F_SETFL, fcntl(from_ap, F_GETFL, 0) | O_NONBLOCK);
if (status == -1){
perror("calling fcntl");
Here is where I call my read function:
rsize = read(from_ap, cport_rbuf, ES1_MSG_SIZE);
if (rsize < 0) {
printf("error %zd receiving from AP\n", rsize);
return NULL;
}
I have also tried this with similar results:
fd_set readset;
struct timeval tv;
FD_ZERO(&readset);
FD_SET(from_ap, &readset);
tv.tv_sec = 0;
tv.tv_usec = 100;
result = select(from_ap+1, &readset, NULL, NULL, &tv);
if (result > 0 && FD_ISSET(from_ap, &readset)){
printf("there was something to read\n");
rsize=read(from_ap,cport_rbuf,ES1_MSG_SIZE);
}
The last message received is "there was something to read" and code does not progress further. What I am doing wrong? This is not a multithreaded program so no one can change flags but I have anyways confirmed them with printing back the flags before reading.
Does the device support O_NONBLOCK? This appears to be code from GitHub for gbsim. Read up on gbsim, it's entirely possible the driver does not support non-blocking calls.
Here is the code i am using. Whenever i write something to the Stdin, it works, but it is not working for socket. It's not able to enter the loop for Socket. I am new to socket programming.
void HandleConnection(int socket)
{
fd_set rfd;
struct timeval tv;
int retval;
printf("%d",socket);
MakeNonBlocking(socket);
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfd);
while(1)
{
FD_SET(STDIN, &rfd);
FD_SET(socket, &rfd);
/* Wait up to five seconds. */
tv.tv_sec = 50;
tv.tv_usec = 0;
retval = select(2, &rfd,NULL, NULL, &tv);
if(retval == 0)
{
printf("No data within fifty seconds.\n");
exit(1);
}
if(FD_ISSET(socket,&rfd))
{
printf("socket wala\n");
recieve_message(&socket);
send_message(&socket);
}
if(FD_ISSET(STDIN,&rfd))
{
printf("stdin wala\n");
recieve_message(&socket);
send_message(&socket);
}
}
}
FDZERO must go before FDSET inside the loop
select(2, ...) should be select(highest filedescriptor +1, ...).
when select returns you should check for negative value in case of errors
you should consider using pselect instead of select.
Clear tv before you reinitialize it.
It appears that you don't understand how the nfds argument to select() is used. The man page addresses this explicitly:
The first nfds
descriptors are checked in each set; i.e., the descriptors from 0 through nfds-1 in the descriptor sets are examined. (Example:
If you have set two file descriptors "4" and "17", nfds should not be "2", but rather "17 + 1" or "18".)
So here is how you should rewrite your code.
int maxfd = (socket > STDIN ? socket : STDIN) + 1; /* select() requires the number of FDs to scan, which is max(fds)+1 */
while(1){
FD_ZERO(&rfd); /* This needs to be done each time through the loop */
/* Watch stdin (fd 0) to see when it has input. */
FD_SET(STDIN, &rfd);
FD_SET(socket, &rfd);
/* Wait up to five seconds. */
tv.tv_sec = 50;
tv.tv_usec = 0;
retval = select(maxfd, &rfd,NULL, NULL, &tv);
if(retval == 0)
{
printf("No data within fifty seconds.\n");
exit(1);
}
if(retval == -1) /* Check for error */
{
perror("Error from select");
exit(2);
}
if(FD_ISSET(socket,&rfd))
{
printf("socket wala\n");
recieve_message(&socket);
send_message(&socket);
}
if(FD_ISSET(STDIN,&rfd))
{
printf("stdin wala\n");
recieve_message(&socket);
send_message(&socket);
}
}
I create one executable file named as "readmsg". Its source code is as below. The select() works if I only perform readmsg in shell (I can see the output of timeout).
But if I create a FIFO file via command: mknod /tmp/message p, and perform readmsg < /tmp/message in shell. In result, the select() can't return if I don't write something in /tmp/message. My question is: Why I can't get the timeout output?
the source code of "readmsg":
#define STDIN 0
fd_set fds;
struct timeval tv;
while (1) {
FD_ZERO(&fds);
FD_SET(STDIN, &fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
ret = select(STDIN + 1, &fds, NULL, NULL, &tv);
if (ret > 0) {
printf("works\n");
if (FD_ISSET(STDIN, &fds)) {
// read ...
}
} else if (ret == 0) {
printf("timeout!!\n");
} else {
printf("interrupt\n");
}
}
Thanks #Mat. After adding printf() close to main(), there is not output either. Even there is not the process id of readmsg when perform ps.
So it proves the process of readmsg < /tmp/message is blocked before the FIFO is ready to be writen.
There isn't any error. In fact, the readmsg works well when reading messages from redirected FIFO file.
I am reading/writing to a pipe created by pipe(pipe_fds). So basically with following code, I am reading from that pipe:
fp = fdopen(pipe_fds[0], "r");
And when ever I get something, I print it out by:
while (fgets(buf, 200, fp)) {
printf("%s", buf);
}
What I want is, when for certain amount of time nothing appears on the pipe to read from, I want to know about it and do:
printf("dummy");
Can this be achieved by select() ? Any pointers on how to do that will be great.
Let's say you wanted to wait 5 seconds and then if nothing was written to the pipe, you print out "dummy."
fd_set set;
struct timeval timeout;
/* Initialize the file descriptor set. */
FD_ZERO(&set);
FD_SET(pipe_fds[0], &set);
/* Initialize the timeout data structure. */
timeout.tv_sec = 5;
timeout.tv_usec = 0;
/* In the interest of brevity, I'm using the constant FD_SETSIZE, but a more
efficient implementation would use the highest fd + 1 instead. In your case
since you only have a single fd, you can replace FD_SETSIZE with
pipe_fds[0] + 1 thereby limiting the number of fds the system has to
iterate over. */
int ret = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
// a return value of 0 means that the time expired
// without any acitivity on the file descriptor
if (ret == 0)
{
printf("dummy");
}
else if (ret < 0)
{
// error occurred
}
else
{
// there was activity on the file descripor
}
IIRC, select has a timeout that you then check with FD_ISSET to tell if it was I/O or not that returned.