简体   繁体   English

C程序守护程序使用100%的CPU使用率

[英]C Program daemon uses 100% cpu usage

I'm initializing a daemon in C in a Debian: 我正在Debian中用C初始化一个守护进程:

/**
 * 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%. 但是CPU使用率始终为100%。 Why is that? 这是为什么? What can I do? 我能做什么?

EDIT 编辑

I commented out the entire while loop and made the main function as simple as: 我注释掉了整个while循环,并使主要功能变得简单:

int main(int argc, char *argv[])
{
    init_daemon();
    while(1) {
        // All commented out
    }
    return 0;
}

And I'm still getting 100% cpu usage 而且我仍然获得100%的CPU使用率

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, ie select() is only blocking the very first time. 您需要在每次迭代中将timeout设置为所需的值,在Linux上对该结构进行修改,因此我认为您的循环除了第一次外就不会暂停,即select()仅在第一次时阻塞。

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. 尝试在select()之后打印tv_sectv_usec ,然后看一下,修改它以反映在select()返回之前还剩下多少时间。

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. select()调用之前在循环内进行操作,它应该可以按预期的方式工作,您也可以在循环内移动许多代币,这将使您的代码更易于维护,例如,可以将循环内容移动到函数中未来,这可能会有所帮助。

This is from the linux manual page select(2) 这是从linux手册页中的select(2)

On Linux, select() modifies timeout to reflect the amount of time not slept; 在Linux上, select()修改超时以反映未休眠的时间; 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 . (POSIX.1-2001允许任何一种行为。)当将读取超时的Linux代码移植到其他操作系统时,以及将代码重用struct timeval循环用于多个select() Linux移植到Linux时这都会引起问题。 重新初始化它 Consider timeout to be undefined after select() returns. select()返回之后,将超时视为未定义。

I think the bold part in the qoute is the important one. 我认为qoute中的大胆部分是重要的部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM