linux学习之信号篇(二)

信号(二)

1.信号捕捉设定

#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

struct sigaction 定义:
struct sigaction 
{
   void (*sa_handler)(int);//函数指针
   void (*sa_sigaction)(int, siginfo_t *, void *);//和上边的函数时互斥的,两个只能选一个
    sigset_t sa_mask;//调用捕捉函数时,临时使用的阻塞函数集,避免重复嵌套,调用完成后,又恢复为之前的 
    int sa_flags;//指定调用上边哪个函数
    void (*sa_restorer)(void);//保留,已经过时了
};
sa_handler : 早期的捕捉函数

  当执行捕捉函数时,默认执行的是,当前信号的屏蔽字自动置1,当执行完后,会自动恢复,响应之前在捕捉函数执行时,发生的信号。信号产生后并不一定是第一时间被响应。

信号捕捉

练习:

#include<stdio.h>
#include<signal.h>

void do_sig(int num)

{

  printf("I am do_sig
");

  printf("num=%d
",num);

}

int main(void)

{

   struct sigaction act;

   act.sa_handler=do_sig;
   //act.sa_handler=SIG_DFL;
   //act.sa_handler=SIG_IGN;
sigemptyset(&act.sa_mask); act.sa_flags=0; sigaction(SIGINT,&act,NULL); while(1) { printf("************ ") sleep(1); } return 0; } 运行结果: ************ ************ ************ ^c I am do_sig num=2//信号的编号

 2.利用SIGUSR1和SIGUSR2实现父子进程同步输出

注意:子进程继承了父进程的信号屏蔽字和信号处理动作

例子:设计一个父子进程,交替数数,并打印身份。

3.C标准库信号处理函数(在window和linux,unix中都可以用)

typedef void (*sighandler_t)(int)
sighandler_t signal(int signum, sighandler_t handler)
int system(const char *command)
集合fork,exec,wait一体,子进程执行通过exec执行command  

signal接口简单,可跨平台,缺点没有sigaction功能强大。

4.可重入函数(和同步异步有关,多线程有关)

不可重入函数

* 不含全局变量和静态变量是可重入函数的一个要素
* 可重入函数见man 7 signal
* 在信号捕捉函数里应使用可重入函数

* 在信号捕捉函数里禁止调用不可重入函数

例如:上述的链表的插入函数就是一个不可重入函数。

  strtok就是一个不可重入函数,因为strtok内部维护了一个内部静态指针,保存上一次切割到的位置,如果信号的捕捉函数中也去调用strtok函数,则会造成切割字符串混乱,应用strtok_r版本,r表示可重入。

#include<stdio.h>
#include<string.h>
int main(void)
{
     char buf[]="hello world itcast xwp";
     char *save=buf,*p;
    
      while((p=strtok_r(save," ",&save))!=NULL); 
             printf("%s
",p);
      return 0;
}

输出:
hello
world
itcast
xwp

5.信号引起的竞态和异步I/O

时序竞态

int pause(void)
使调用进程挂起,直到有信号递达,如果递达信号是忽略,则继续挂起
int sigsuspend(const sigset_t *mask)
1.以通过指定mask来临时解除对某个信号的屏蔽,
2.然后挂起等待,
3.当被信号唤醒sigsuspend返回时,进程的信号屏蔽字恢复为原来的值
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void sig_alrm(int signo)
{
/* nothing to do */
}
unsigned int mysleep(unsigned int nsecs)
{
    struct sigaction newact, oldact;
    unsigned int unslept;
    newact.sa_handler = sig_alrm;
    sigemptyset(&newact.sa_mask);
    newact.sa_flags = 0;
    sigaction(SIGALRM, &newact, &oldact);
    alarm(nsecs);
    pause();//若在某一个时刻,有一个进程使用了内核,内核使用的时间是alarm(nsecs)时间,会导致pause永远不会被执行,也称之为时序竞态。
    unslept = alarm(0);
    sigaction(SIGALRM, &oldact, NULL);
    return unslept;
}
int main(void)
{
    while(1)
    {
        mysleep(2);
        printf("Two seconds passed
");
     }
    return 0;
}               

mysleep的改进版

unsigned int mysleep(unsigned int nsecs)//返回值时未睡够的时间,正常返回0
{
    struct sigaction newact, oldact;//新的动作,旧的动作
    sigset_t newmask, oldmask, suspmask;//新的信号集和旧信号集,保留旧的以便恢复
    unsigned int unslept;
    /* set our handler, save previous information */
    newact.sa_handler = sig_alrm;//设置捕捉函数,必须设置
    sigemptyset(&newact.sa_mask);//清零
    newact.sa_flags = 0;
    sigaction(SIGALRM, &newact, &oldact);
    /* block SIGALRM and save current signal mask */ //阻塞SIGALRM
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGALRM);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    alarm(nsecs);//定时
   
   //解除阻塞,恢复
    suspmask = oldmask;
    sigdelset(&suspmask, SIGALRM); /* make sure SIGALRM isn't                          blocked */ 
     sigsuspend(&suspmask); /* wait for any signal to be caught挂起等待,当定时器到时,产生SIGALRM信号,唤起*/
/* some signal has been caught, SIGALRM is now blocked */
    unslept = alarm(0);//返回未睡够的时间
    sigaction(SIGALRM, &oldact, NULL); /* reset previous action */
/* reset signal mask, which unblocks SIGALRM */
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    return(unslept);
}        

避免异步I/O的类型

sig_atomic_t
平台下的原子类型,32位是一个int型,64位是一个long型
volatile
防止编译器开启优化选项时,优化对内存的读写,改进异步I/O的信号传递不便的情况

6.SIGCHLD信号处理

SIGCHLD的产生条件(向父进程发送,若子进程死的话父进程负责回收子进程资源)

子进程终止时
子进程接收到SIGSTOP信号停止时
子进程处在停止态,接受到SIGCONT后唤醒时

status处理方式

pid_t waitpid(pid_t pid, int *status, int options)

    options
        WNOHANG
            没有子进程结束,立即返回
        WUNTRACED
            如果子进程由于被停止产生的SIGCHLD, waitpid则立即返回
         WCONTINUED
            如果子进程由于被SIGCONT唤醒而产生的SIGCHLD, waitpid则立即返回
    获取status
        WIFEXITED(status)
               子进程正常exit终止,返回真
                WEXITSTATUS(status)返回子进程正常退出值
        WIFSIGNALED(status)
            子进程被信号终止,返回真
                WTERMSIG(status)返回终止子进程的信号值
        WIFSTOPPED(status)
               子进程被停止,返回真
                WSTOPSIG(status)返回停止子进程的信号值
        WIFCONTINUED(status)
                  子进程由停止态转为就绪态,返回真                                                       
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void sys_err(char *str)
{
    perror(str);
    exit(1);
}
void do_sig_child(int signo)
{
    int status;
    pid_t pid;
    //回收了子进程的资源
    while ((pid = waitpid(0, &status, WNOHANG)) > 0) 
    {

        if (WIFEXITED(status))
            printf("child %d exit %d
", pid, WEXITSTATUS(status));//打印进程的退出值时多少
        else if (WIFSIGNALED(status))//判断是否是被信号终止的
            printf("child %d cancel signal %d
", pid,         WTERMSIG(status));
    }
}

int main(void)
{
    pid_t pid;
    int i;
    //阻塞SIGCHLD
    for (i = 0; i < 10; i++) 
   {
        if ((pid = fork()) == 0)
            break;
        else if (pid < 0)
            sys_err("fork");
   }

      if (pid == 0) 
      {
         int n = 18;
         while (n--)
         {
                printf("child ID %d
", getpid());
                sleep(1);
          }
          return i;
     }
     else if (pid > 0)
     {
        //先设置捕捉
        //再解除对SIGCHLD的阻塞
        struct sigaction act;
        act.sa_handler = do_sig_child;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGCHLD, &act, NULL);
        while (1)
        {
           printf("Parent ID %d
", getpid());
           sleep(1);
        }
     }
        return 0;
}                                      

7.向信号捕捉函数传参

sigqueue

int sigqueue(pid_t pid, int sig, const union sigval value)
union sigval {
   int sival_int;
   void *sival_ptr;
};

sigaction

void (*sa_sigaction)(int, siginfo_t *, void *)
siginfo_t {
    int si_int; /* POSIX.1b signal */
    void *si_ptr; /* POSIX.1b signal */
    sigval_t si_value; /* Signal value */
    ...
}
sa_flags = SA_SIGINFO

8.信号中断系统调用

read阻塞时,信号中断系统调用(要习惯返回read和write的值):
1.返回部分读到的数据
2.read调用失败,errno设成EINTER



原文地址:https://www.cnblogs.com/rainbow1122/p/7834928.html