{C++系列} UNIX高级编程 day11 信号阻塞和信号未决,信号从产生到处理的整个过程,可重入函数
in C/C++ with 0 comment

{C++系列} UNIX高级编程 day11 信号阻塞和信号未决,信号从产生到处理的整个过程,可重入函数

in C/C++ with 0 comment

复习

一,管道

Write(2) read(2) 的阻塞

二,文件输入重定向

三,信号

信号的产生,信号的处理,未决信号,信号阻塞。

一,信号阻塞和信号未决
sigset_T
系统提供了对信号集类型的封装操作
Sigempityset(2)

#include <signal.h>

int     sigaddset(sigset_t *set, int signo);
功能:添加信号到信号集里
参数:
Set:指定信号集
Sogno:指定信号
返回值:
0     成功
-1    错误    errno被设置
int     sigdelset(sigset_t *set, int signo);
功能:从信号集里将指定的信号删除
参数:
Set:指定信号集
Signo:指定信号
返回值:
0     成功
-1    错误    errno被设置
int     sigemptyset(sigset_t *set);
功能:将信号集设置为空
参数:
0     成功
-1    错误    errno被设置
Set:指定要被初始化的信号集
返回值:
0     成功
-1    错误    errno被设置
int     sigfillset(sigset_t *set);
功能:将指定的信号集初始化为满
参数:
Set:指定要被初始化的信号集
返回值:
0     成功
-1    错误    errno被设置
int     sigismember(const sigset_t *set, int signo);
功能:测试信号是否是信号集的一个成员
参数:
Set:指定信号集
Signo:指定信号
返回值:
1     是成员   
0      不是成员     
-1    错误    errno被设置

我们通过设置进程信号屏蔽字,可以设置进程对信号的阻塞。
如何将一个segset_t类型的变量设置为进程的信号屏蔽字,需要使用到sigprocmask(2)

 #include <signal.h>
int     sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);

功能:检测或改变信号阻塞
参数:
How:
SIG_BLOCK 线程当前集合和set集合的并集设置为信号的阻塞屏蔽字
SIG_UNBLOCK 将set集合里的成员,从当前线程的信号屏蔽字移除。解除对信号的阻塞。
SIG_SETMASK 将set设置为当前线程的信号屏蔽字

Set:指定要操作的信号集
Oldest:如果不为NIULL,将原来的值保存袋oldset中。
返回值:

举例

设置进程对二号进程阻塞

  1 #include<stdio.h>
  2 #include<signal.h>
  3 int main(){
  4         sigset_t set;//将set是信号集合
  5         //初始化信号集set
  6         sigemptyset(&set);
  7         //将2号信号添加到集合中
  8         sigaddset(&set,SIGINT);
  9         //set设置为进程的信号屏蔽字
 10         int s = sigprocmask(SIG_BLOCK,&set,NULL);
 11         if(s == -2){
 12                 perror("sigprocmask");
 13                 return -1;
 14         }
 15         while(1);
 16         return 0;
 17 }
1 #include<stdio.h>
  2 #include<signal.h>
  3 void *doit(int n){
  4         printf("recived....%d\n",n);
  5 }
  6 int main(){
  7         sigset_t set,oset;//将set是信号集合
  8         signal(2,doit);
  9         //初始化信号集set
 10         sigemptyset(&set);
 11         //将2号信号添加到集合中
 12         sigaddset(&set,3);
 13         sigaddset(&set,2);
 14         //set设置为进程的信号屏蔽字
 15         int s = sigprocmask(SIG_BLOCK,&set,&oset);
 16         if(s == -1){
 17                 perror("sigprocmask");
 18                 return -1;
 19         }
 20         for(int i=0;i<500000;i++){
 21                 printf("i=%d\n",i);
 22         }
 23         int p = sigprocmask(SIG_SETMASK,&set,NULL);
 24         return 0;
 25 }
~

注意

注意信号阻塞和信号忽略的区别
信号阻塞是信号还没有抵达给进程,但是信号忽略是信号抵达到进程之后,进程对信号的处理方式

SIG_KILLSIG_STOP不能阻塞或者忽略

如果进程已经对某个信号设置为阻塞,当为进程多次发送信号的时候,进程解除对信号的阻塞的时候,只对信号做一次处理,造成了信号的丢失,这样的信号称为不可靠信号。1~31号信号是不可靠信号。

进程对某个信号阻塞的时候,给进程多次发送某个信号,在解除对信号的处理以后,发送了多少次,就对进程处理多少次。34 ~ 64

未决信号:

检测进程的未决信号
为了验证,需要设置进程对信号的阻塞。
当进程对信号阻塞的时候,给进程发送信号,这时候再去获取未决信号集,然后再根据未决信号集提取出未决信号。
如何提取信号未决集。

sigpending(2)
 #include <signal.h>
int     sigpending(sigset_t *set);
功能:检查未决信号集
参数:
Set:指定了未决信号集的存储地址
返回值:
0        成功
-1        错误

Pause(2)

#include <unistd.h>
int     pause(void);
功能:等待信号
参数:void
返回值:
-1        错误     errno被设置

举例

pausealarm函数配合使用完成sleep的功能

注意:只要有信号传送过来,pause就会被击穿。

1 #include<stdio.h>
2 #include<signal.h>
3 #include<unistd.h>
4 unsigned int mysleep(unsigned int seconds){
5         int ar = alarm(seconds);
6         pause();
7         return ar;
8 }
9 void doit(int n){
 10         return;
 11 }
 12 int main(){
 13         //改变进程对SIGALRM信号的处理
 14         signal(SIGALRM,doit);
 15         while(1){
 16                 mysleep(3);
 17                 printf("drops.org.cn\n");
 18         }
 19         return 0;
 20 }
~

二,信号从产生到处理的整个过程

信号的递达和信号的处理不是一个环节。

信号处理函数有自己的栈帧。信号处理函数和进程是异步的。

三,可重入函数

只能访问函数自己栈帧里的数据,这样的函数称为可重入函数
函数不能访问全局变量,静态局部变量,malloc堆里的数据。这样的函数是可重入函数。

当异步访问一个资源时,会造成竞争。
如何避免竞争资源?

一个函数的私有资源。
就是函数的栈帧
进程的代码段 堆 为公共资源。
这个函数只访问栈帧里的数据, 这个函数称为可重入函数

举例

为了演示异步访问资源,举例

  1 #include<stdio.h>
  2 #include<signal.h>
  3 int count=0;//模拟了公共资源
  4 void doit(int n){
  5         int val = count;
  6         val++;
  7         usleep(5000);
  8         printf("val=%d\n",val);
  9         count=val;
 10         return;
 11 }
 12 int main(){
 13         signal(2,doit);
 14         while(1)
 15           doit(3);
 16         return 0;
 17 }

四,前台函数和后台函数

在linux操作系统下,总是有一个前台作业和一个或多个后台作业。

什么是作业?

什么是进程组?

作业也是进程组的一种。
一般情况下,子进程和父进程通属于一个进程组。子进程和子进程也属于这个进程组。
但是属于同一个作业。父进程和子进程同属于一个作业,但是和孙子不是同一个作业。

当使用快捷键发送信号的时候,信号只能发送给前台作业,不能发送给后台作业

举例

将一个作业切换到后台。
在前台启动一个作业。
如何将前台正在运行的作业切换到后台。ctl+z
使用jobs查看后台作业。

fg %作业号        将后台作业切换到前台
bg %作业号        在后台运行作业

子进程结束的时候,给父进程发送SIGCHLD信号。父进程收到信号,立即回收子进程的资源。

五,setitimmer设置计时器

系统时间:
运行一个进程的时候,所消耗的时间包括三个部分。

系统内核为系统中的每个进程维护三个计时器。

三个系统计时器除了统计进程的时间之外,还可以按照各自的规则以定时器的方式工作。向进程周期性地发送不同的信号。

如何使用计时器完成计时器的工作。

setitimmer(2)
#include <sys/time.h>
#define ITIMER_REAL      0
#define ITIMER_VIRTUAL   1
#define ITIMER_PROF      2
int     getitimer(int which, struct itimerval *value);
Int     setitimer(int which, const struct itimerval *restrict value,struct itimerval *restrict ovalue);

功能:设置定时器的间隔值
参数:

which:
New_value:设置为定时器的新值
old_value:设置定时器的旧值。

返回值:

ITIMER_REAL      真实计时器                SIGALRM
ITIMER_VIRTUAL     虚拟计时器            SIGVTALARM
ITIMER_PROF        实用计时器                SIGPROF
struct itimerval {
                   struct  timeval it_interval;    /* timer interval */
                   struct  timeval it_value;       /* current value */
           };
it_value减到0(从进程启动到产生第一次信号的时间),设置it_interval(间隔值)
It_interval 间隔值
struct    timeval{
                    long    tv_sec;        /* second */
                    long    tv_usec;        /* microseconds */
}

举例

使用setitimer设置一个间隔定时器。其实间隔时间为3s,间隔时间为0.5.产生SIG_ALARM

1 #include<stdio.h>
 2 #include<sys/time.h>
 3 #include<signal.h>
 4 void doit(int n){
 5         printf("recv  SIGALRM...\n");
 6         return;
 7 }
 8
 9 int main(){
10         struct itimerval new,old;
11         signal(SIGALRM,doit);
12         new.it_value.tv_sec=3;
13         new.it_value.tv_usec=0;
14
15         new.it_interval.tv_sec=0;
16         new.it_interval.tv_usec=500000;
17         int tm = setitimer(TIMER_REAL,&new,&old);
18         if(tm==-1){
19                 perror("setitimer");
20                 return -1;
21         }
22         while(1);
23         return 0;
24 }
~

信号是进程间的通讯,异步的。

信号的产生,阻塞,未决,信号的处理
信号的抵达和信号的处理 是两个阶段。

六,进程间的通信. System V. IPC

进程间通讯 : 管道 信号 环境变量
每个对象都有自己的id。 需要使用键值获取对象的id。获取了对象的id,就可以使用对象的id

总结

Responses