【问题标题】:Problems sending/handling signals across two processes on same computer - C programming在同一台计算机上的两个进程之间发送/处理信号的问题 - C 编程
【发布时间】:2013-02-15 04:32:01
【问题描述】:

我在处理计算机上运行的两个进程之间的信号时遇到问题。 scheduler.c 正在发送信号,而 producer.c 正在接收它们。生产者应该打印“Printing n”,其中每次接收到 SIGUSR1 时 n 都会增加 1。我曾尝试同时使用信号和 sigaction 来处理信号,但它们都不适合我。

scheduler.c:

/*
 * scheduler.c
 */


#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int n = 1;


int main(int argc, char *argv[])
{
    int a = 0; // This variable will be used for the switch later

    // Check to ensure correct number of command line arguments
    if(argc != 2){
        printf("Usage error. Wrong number of arguments\n");
        return 1;
    }

    // Grab PID of producer.c
    int producer_pid = atoi(argv[1]);       

    while(1){
        printf("Choose an Option: \n");
        printf("1. Request_Production\n");
        printf("2. Stop_Producer\n");
        printf("3. Stop_Scheduler\n");
        scanf("%d", &a);

        switch( a ) 
        {
            case 1:
                kill(producer_pid, 16);     //Send SIGUSR1 to producer.c
                break;

            case 2:
                kill(producer_pid, 2);      //Send SIGINT to producer.c
                break;

            // Successfully exit program
            case 3:
                return 0;

            // Invalid Choice
            default :
                printf("Invalid choice\n");
        }
    }
}

生产者.c:

/*
 * producer.c
 */

#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int n = 1;

void sigusr1(int signo)
{

    printf("Producing %d", n);
    n++;
}


int main()
{


    struct sigaction act;
    sigset_t block_mask;
    sigfillset(&block_mask);
    act.sa_handler = sigusr1;
    act.sa_mask = block_mask;
    act.sa_flags = 0;

    if(sigaction(SIGUSR1, &act, NULL) == 0){
        printf("success");
    }


    while(1) {

        sleep(2);
        fflush(stdout);
    }

}

【问题讨论】:

    标签: c signals kill interprocess


    【解决方案1】:

    此代码适用于我(在 Mac OS X 10.7.5 上):

    生产者.c

    #include <signal.h>
    #include <stdio.h>
    #include <unistd.h>
    
    static volatile sig_atomic_t n = 0;
    
    static void sigusr1(int signo)
    {
        n += signo / SIGUSR1;
    }
    
    int main(void)
    {
        struct sigaction act;
        sigset_t block_mask;
        sigfillset(&block_mask);
        act.sa_handler = sigusr1;
        act.sa_mask = block_mask;
        act.sa_flags = 0;
    
        if (sigaction(SIGUSR1, &act, NULL) == 0)
        {
            printf("success %d\n", (int)getpid());
            while (1)
            {
                pause();
                printf("Producer: %d\n", n);
                fflush(stdout);
            }
        }
    }
    

    scheduler.c

    #include <signal.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        int a = 0; // This variable will be used for the switch later
    
        // Check to ensure correct number of command line arguments
        if (argc != 2)
        {
            fprintf(stderr, "Usage: %s pid\n", argv[0]);
            return 1;
        }
    
        // Grab PID of producer.c
        int producer_pid = atoi(argv[1]);       
    
        while(1)
        {
            printf("Choose an Option: \n");
            printf("1. Request Production\n");
            printf("2. Stop Producer\n");
            printf("3. Stop Scheduler\n");
            scanf("%d", &a);
    
            switch (a) 
            {
                case 1:
                    if (kill(producer_pid, SIGUSR1) != 0)
                        fprintf(stderr, "Failed to send signal %d to %d\n", SIGUSR1, producer_pid);
                    break;
    
                case 2:
                    if (kill(producer_pid, SIGTERM) != 0)
                        fprintf(stderr, "Failed to send signal %d to %d\n", SIGTERM, producer_pid);
                    break;
    
                // Successfully exit program
                case 3:
                    return 0;
    
                // Invalid Choice
                default :
                    fprintf(stderr, "Invalid choice (%d)\n", a);
                    break;
            }
        }
    }
    

    样本输出

    $ (./producer &)
    $ success 40119
    $ ./scheduler 40119
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    1
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    Producer: 1
    1
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    Producer: 2
    1
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    Producer: 3
    1
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    Producer: 4
    1
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    Producer: 5
    1
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    Producer: 6
    1
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    Producer: 7
    1
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    Producer: 8
    2
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    1
    Failed to send signal 30 to 40119
    Choose an Option: 
    1. Request Production
    2. Stop Producer
    3. Stop Scheduler
    3
    $
    

    发生了什么变化?

    各种变化,但关键是:

    1. 确保输出消息以换行符结尾。
    2. n 变成volatile sig_atomic_t 变量;这就是 C 标准所说的您可以在信号处理程序中访问的内容。
    3. 在生产者pause() 中有主循环,然后打印。 pause() 系统调用仅在被信号中断时返回。
    4. 在调度程序中也使用符号信号名称。
    5. 让调度程序发送SIGTERM 而不是SIGINT 来终止生产者。如果生产者在后台运行,它会忽略中断。
    6. 让调度程序识别kill() 调用何时失败。
    7. 让生产者识别其 PID。

    我已经从文件标题中删除了多余的标题。

    信号处理程序中有趣的signo / SIGUSR1 避免了未使用参数的警告;它没有其他用途。如图所示,程序编译干净:

    gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
        -Wold-style-definition scheduler.c -o scheduler  
    gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
        -Wold-style-definition producer.c -o producer
    

    这是使用 GCC 4.7.1。

    【讨论】:

      【解决方案2】:

      一句话:

      有些函数是安全的,有些函数是不安全的,不能从信号处理程序中调用。

      printf 不能从信号处理程序中调用。另一方面,write 是安全的。

      该列表由 POSIX-1 指定,但详细信息可能因操作系统而异。对于 Linux,您将在 signal(7) 中找到该列表:

      http://linux.die.net/man/7/signal

      【讨论】:

      • 如何将 write 与 int 变量 n 一起使用?函数被这样调用write(int fd, char *buf, int num_bytes) 我不知道在哪里可以放置变量n...
      • sprintf(buffer, "%d", n); write(fd, buffer, len); 应该可以。或者一些你自己的sprintf-like 处理单个整数。
      • 仍然没有做任何事情......我不认为 printf 一定是问题
      • @midma101 printf 不是这里的问题,但无论如何它是无效的
      • @vonbrand sprintf 也不允许在信号处理程序中使用。虽然我不明白为什么(某些实现可能会在内部 malloc ......?)
      猜你喜欢
      • 2019-06-09
      • 1970-01-01
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 2012-05-01
      • 1970-01-01
      • 2020-07-03
      • 2011-05-27
      相关资源
      最近更新 更多