Linux 信号的使用

Linux 信号的使用,第1张

Linux 信号的使用

文章目录
  • 信号的使用
    • 1.信号的基本概念
      • 1.1图示信号发送和处理
      • 1.2常见的信号
      • 1.3信号的值在系统源码中的定义
    • 2.修改信号的响应方式signal()
      • 2.1signal()
      • 2.2信号的三种响应方式
      • 2.3在键盘上按下 Ctrl+c 时,会给当前终端前台执行的进程发送 SIGINT 信号,用 signal() 修改 SIGINT 信号的响应方式
    • 3. 发送信号kill()
      • 3.1 kill()
      • 3.2使用 kill()系统调用实现类似于系统 kill 命令(只传pid)
      • 3.3使用 kill()系统调用实现类似于系统 kill 命令(传pid和信号)
    • 4.处理僵尸进程

信号的使用 1.信号的基本概念

信号是系统响应某个条件而产生的事件,进程接收到信号会执行相应的 *** 作。与信号有关的系统调用在“signal.h”头文件中有声明。

1.1图示信号发送和处理

1.2常见的信号

1.3信号的值在系统源码中的定义
  1. #define SIGHUP 1
  2. #define SIGINT 2 //键盘按下 Ctrl+c 时,会产生该信号
  3. #define SIGQUIT 3
  4. #define SIGILL 4
  5. #define SIGTRAP 5
  6. #define SIGABRT 6
  7. #define SIGIOT 6
  8. #define SIGBUS 7
  9. #define SIGFPE 8
  10. #define SIGKILL 9 //该信号的响应方式不允许改变
  11. #define SIGUSR1 10
  12. #define SIGSEGV 11
  13. #define SIGUSR2 12
  14. #define SIGPIPE 13 //读端关闭的描述符,写端写入时产生,该信号会终止程序
  15. #define SIGALRM 14
  16. #define SIGTERM 15 //系统 kill 命令默认发送的信号
  17. #define SIGSTKFLT 16
  18. #define SIGCHLD 17 //子进程结束后,会默认给父进程发送该信号
  19. #define SIGCONT 18
  20. #define SIGSTOP 19
  21. #define SIGTSTP 20
  22. #define SIGTTIN 21
  23. #define SIGTTOU 22
  24. #define SIGURG 23
2.修改信号的响应方式signal() 2.1signal()

sighandler_t signal(int signum, sighandler_t handler);

signum 代表信号的值
handler代表信号的响应方式

2.2信号的三种响应方式

默认:SIG_DFL
忽略:SIG_IGN
自定义:自己写的信号处理函数

2.3在键盘上按下 Ctrl+c 时,会给当前终端前台执行的进程发送 SIGINT 信号,用 signal() 修改 SIGINT 信号的响应方式
  1. 写一个代码,在终端屏幕上一直打印"hello Linux",当在键盘上按下Ctrl + c时观察(Ctrl + c对应的信号是SIGINT,信号代号为2)
    代码如下:
#include
#include
#include

int main()
{
    while(1)
    {   
        printf("hello Linuxn");
        sleep(1);
    }   

    exit(0);
}

运行并在键盘上按Ctrl + c

程序终止
2.用signal()改变信号的响应方式,调用自己写的信号处理函数
代码如下:

#include
#include
#include
#include


void sig_fun(int sig)
{
    printf("sig = %dn",sig);
}
int main()
{
    signal(SIGINT,sig_fun);
    while(1)
    {   
        printf("hello Linuxn");
        sleep(1);
    }   

    exit(0);
}

运行并在键盘上按Ctrl + c

当按下Ctrl+c时,会给我们的进程发送一个SIGINT信号,signal(SIGINT,sin_fun)收到这个信号后,把信号的值传给sin_fun()函数并调用该函数
(我们发现Ctrl+c不能结束进程,此时可以用Ctrl+退出进程)
3.上述代码,我们按下Ctrl+c打印了信号的代号,那么要是当第一次按下Ctrl+c会结束进程,该如何改进呢?
代码如下:

#include
#include
#include
#include


void sig_fun(int sig)
{
    printf("sig = %dn",sig);
    signal(sig,SIG_DFL);
}
int main()
{
    signal(SIGINT,sig_fun);
    while(1)
    {   
        printf("hello Linuxn");
        sleep(1);
    }   

    exit(0);
}

运行并在键盘上连续两次按Ctrl + c

当第一次按下Ctrl+c时,打印SININT的代号,第二次按下Ctrl+c时,进程结束,这是因为在我们的sig_fun()函数中置了signal()函数

3. 发送信号kill() 3.1 kill()

kill() 可以向指定的进程发送指定的信号
int kill(pid_t pid,int sig);

pid > 0 指定将信号发送个那个进程
pid == 0 信号被发送到和当前进程在同一个进程组的进程
pid == -1 将信号发送给系统上有权限发送的所有的进程
pid < -1 将信号发送给进程组 id 等于 pid 绝对值,并且有权限发送的所有的进程
sig 指定发送信号的类型

3.2使用 kill()系统调用实现类似于系统 kill 命令(只传pid)

前面我们完成了main程序,第一次按下Ctrl+c的时候会打印信号的代号,第二次按下Ctrl+c的时候会结束进程,我们通过编写mykill.c来给main进程发送SIGINT信号(相当于键盘输入Ctrl+c),因为通过kill()函数发送信号的时候参数需要进程号,所以打印hello Linux的同时也打印一下进程号,即把main.c中代码printf(“hello Linuxn”);改为printf(“hello Linux pid = %dn”,getpid());。
mykill.c代码如下:

#include
#include
#include
#include

int main(int argc,char* argv)
{
    //只传入进程号,因为第一个参数是./mytest 所以一共有两个参数
    if(argc != 2)
    {   
       printf("mykill argc errorn");
       exit(0);
    }   

    int pid = 0;
    sscanf("argv[1]","%d",&pid);

    if(kill(pid,SIGINT) == -1) 
    {   
      printf("kill errorn");
      exit(0);
    }   
    exit(0);
}

一个终端运行main程序,一个终端运行,mykill程序,执行mykill程序的时候输入main程序的进程号,即通过kill()给main程序发送SIGINT信号。
执行两个程序如下:

通过执行结果我们发现,执行两次mykill和在键盘按下两次Ctrl+c效果一样,第一次打印信号的代号,第二次结束进程。

3.3使用 kill()系统调用实现类似于系统 kill 命令(传pid和信号)

因为要传入进程号和信号,所以main程序的参数个数变为3
代码如下:

#include
#include
#include
#include

int main(int argc,char* argv[])
{

    if(argc != 3)
    {   
       printf("mykill argc errorn");
       exit(0);
    }   

    int pid = 0;
    int sig = 0;

    sscanf(argv[1],"%d",&pid);
    sscanf(argv[2],"%d",&sig);

    if(kill(pid,sig) == -1) 
    {   
      printf("kill errorn");
      exit(0);
    }   
    exit(0);
}

执行两个程序如下:

4.处理僵尸进程

编写test.c

#include
#include
#include
#include
#include
#include


int main()
{
    int n = 0;
    char* s = NULL;

    pid_t pid = fork();
    assert( pid != -1 );

    if( pid == 0 )
    {
        n = 3;
        s = "child";
    }
    else
    {
        n = 7;
        s = "parent";
    }

    for(int i = 0;i < n;i++)
    {
        printf("s = %sn",s);
        sleep(1);
    }

    exit(0);
}

在test.c中因为子进程先于父进程结束,父进程没有得到子进程的退出码,所以会产生僵尸进程,僵尸进程在僵尸进程博客中有详细介绍。子进程结束的时候,会给父进程发送一个信号:SIGCHLD

让父进程接受这个信号,并且打印SIGCHLD的代号,修改test.c如下:

#include
#include
#include
#include
#include
#include

void sig_fun(int sig)
{
   printf("sig = %dn",sig);

}
int main()
{
    signal(SIGCHLD,sig_fun);
    int n = 0;
    char* s = NULL;

    pid_t pid = fork();
    assert( pid != -1 );
    
    if( pid == 0 )
    {
        n = 3;
        s = "child";
    }
    else
    {
        n = 7;
        s = "parent";
    }

    for(int i = 0;i < n;i++)
    {
        printf("s = %sn",s);
        sleep(1);
    }
    
    exit(0);
}

编译test.c生成可执行文件test并运行test

分析运行结果,当子进程结束的时候,是给父进程发送了一个SIGCHLD,父进程做出响应和处理。
放到后台执行,发现产生僵尸进程

因此,我们让父进程接受到SIGCHLD信号后,调用一下wait(),因为不关心子进程的退出码,只是避免产生僵尸进程,所以将wait()参数置为NULL;修改test.c中的sig_fun函数如下:

   void sig_fun(int sig)
  {
   printf("sig = %dn",sig);
   wait(NULL);
  }

重新编译运行:

通过运行结果我们发现:避免了子进程变成了僵尸进程,并且父进程并没有产生阻塞!父子进程可以并发执行
当父进程把子进程结束时发送的SIGCHLD信号忽略时,也可以避免产生僵尸进程,即, signal(SIGCHLD,SIG_IGN);因为父进程不关注子进程,子进程结束后,进程消失,释放pcb。注:SIGDFL默认的话就是无作为。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5502393.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-12
下一篇 2022-12-12

发表评论

登录后才能评论

评论列表(0条)

保存