网络编程之 信号捕捉器(函数指针与回调函数)(1)

简介: 接着我们的信号说下去 之前博主给大家分享到了信号的概念和初步介绍signal函数的形式后就没有继续往下介绍了,实在是因为时间不够,那个时候博主还要上课,现在博主放假了就好好给大家分享一下如何注册信号捕捉,以及信号捕捉器的妙用。

接着我们的信号说下去    

之前博主给大家分享到了信号的概念和初步介绍signal函数的形式后就没有继续往下介绍了,实在是因为时间不够,那个时候博主还要上课,现在博主放假了就好好给大家分享一下如何注册信号捕捉,以及信号捕捉器的妙用。

 

  利用信号来处理子进程的回收是非常方便和高效的,因为所有的工作你都可以交给内核,当子进程终止时,会发出一个信号,一旦你注册的信号捕捉器捕捉到了这个信号,那么就可以去回调自己的函数(处理处理子进程的函数),去回收子进程了。

在引入函数指针和回调函数之前我们先介绍一下alarm()闹钟函数!


alarm()闹钟函数

alarm:设置定时器(闹钟)。在指定秒数(seconds)后,内核会给当前进程发送14)SIGALRM信号(前面的博客有所介绍)。进程收到该信号,默认动作终止。

20191101165529436.png

#include<unistd.h>
unsigned int alarm(unsigned int seconds);
返回值: 返回0或剩余的秒数,这个函数调用不存在失败!!!!
每个进程有且只有一个定时器!

20191101170048255.png

用大白话来描述上述图片就是:你在里面传入的正整数参数就是你要定时的秒数,在此期间不管你的进程处于何种状态(就绪、运行、挂起(阻塞、暂停)、终止、僵尸.)alarm都会进行计时,当计时完成就会发送 14)SIGALRM信号------>进程收到该信号,默认动作终止(也就是终止当前进程不做任何处理。)

       但是如果你注册了信号捕捉器那么就会进行函数回调,在信号捕捉器接收到14号信号之后就回调你自己定义的函数,这样你的程序不但不会终止,而且还能进行子进程的回收。


取消定时器: alarm(0),返回旧闹钟余下秒数。


小补充:20191101171406179.png

       除了alarm()之外还有一个计时函数,setitimer()---->精确度更高,可代替alarm函数。精度微秒us,可以实现周期定时。各位朋友能掌握alarm()已经足够了,如果想要了解更多的可以自行百度,博主在这里就不在介绍了。

你说那么多都还没有讲到信号捕捉器啊!!!!

莫急,在正式开始信号捕捉起之前我们还需要了解一下函数指针和回调函数(这个是必须要学会的,不然后面的内容你是看不懂的,这可不是博主在乱说所以,骚年加油吧!!

20191101171329618.png

好啦,博主在这里举一个小例子来给大家讲一下(极速入门!)。

请看代码!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int Function_pointer(const void *a, const void *b) {
  printf("成功调用 函数指针啦,你好骚气哦! \n");
  return 1;
}
int main(void) {
  int x = 10;
  int y = 20;
  //函数指针的定义 把函数声明移过来,把函数名改成 (* 函数指针名)
  int(*fp)(const void *, const void *);
  /*贝尔实验室的C和UNIX的开发者采用第1种形式,而伯克利的UNIX推广者却采用第2
  种形式ANSI C 兼容了两种方式*/
  fp = &Function_pointer; //
  (*fp)(&x, &y); //第1种,按普通指针解引的放式进行调用,(*fp) 等同于compare_int
  fp(&x, &y); //第2种 直接调用
  system("pause");
  return 0;
}


2019110118364763.png

下面就让我们理一理函数指针吧。

首先大家要明白函数本身就是地址是一个指针。

那么肯定就可以有一个指针来指向我们的函数了,这个指针就是我们所说的函数指针(这个我们宿舍的学霸(刘老板)都有点懵比),那么我们看看函数指针有啥特别的。

在子函数的声明和正常的函数是一样的!!!

想要快速创建一个函数指针就最佳方法就是把函数声明ctrl+c、ctrl+v复制下来,然后把函数的名字改成 (* 自定义的函数指针名),就好了。里面的形参你想加变量名就加变量名,纯属个人喜好。

接下来只需要让你自定义的函数指针名指向你之前的函数声明即可(18行)。

调用有两种方法,见代码。这样就是我们的函数指针了

大家好好看我上面的实例看懂函数指针应该是不难了,接下来我们就来看看回调函数吧。(不要急,书上不是说了吗,这两个不懂的话是看不懂后面的内容的,这可不是博主不想马上讲解信号捕捉器哟

我们的函数指针是一个指针变量,那么它能不能作为一个函数的参数呢?答案时是肯定的。在我们的操作系统源码,以及在linux中许多系统调用中,有许多这样的函数我们等下要接触到的 信号捕捉器就会用到,但是我们先来一个引入吧!!


观看博客的小可爱:我去,博主,你咋一直在搞引入啊?,我要直接看回调!!

博主:我也是为了大家好呀.

观看博客的小可爱:那好吧最后一次了。

博主:好的

    好了,回到正式上来,该引入我们的回调函数了(需要把之前的函数小小的改造一下)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int Function_pointer(const void *a, const void *b) {
    printf("成功回调 Function_pointer 啦,你好骚气哦! \n");
    int *a1 = (int *)a;
    int *b1 = (int *)b;
    return *a1 - *b1;
}
int main(void) {
  int x = 10;
  int y = 20;
  //函数指针的定义 把函数声明移过来,把函数名改成 (* 函数指针名)
  int(*fp)(const void *x, const void *y);
  /*贝尔实验室的C和UNIX的开发者采用第1种形式,而伯克利的UNIX推广者却采用第2
  种形式ANSI C 兼容了两种方式*/
  fp = &Function_pointer; //
  //(*fp)(&x, &y); //第1种,按普通指针解引的放式进行调用,(*fp) 等同于compare_int
  //fp(&x, &y); //第2种 直接调用
  int arr[] = { 2, 10, 30, 1, 11, 8, 7, 111, 520 };
  qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), Function_pointer);
  for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
    printf(" %d", arr[i]);
  }
  system("pause");
  return 0;
}

20191101190023621.png

讲解:

     回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当 这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调 用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

    我们C语言库函数中就有一个典型的案例:qsort 函数(就是我们的26行),
  void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
第一个参数: void *base, size_t num:
        --->泛型指针,这样就能接受任何值
第二个参数: size_t width:
        --->大小
第三个参数:int (__cdecl *compare )(const void *elem1, const void *elem2 )
    -->看到了吧 函数指针"__cdecl这个不用管,在你声明函数的时候系统给你处理好了"

希望大家通过上面的代码能明白回调函数。

---->其实就是,在函数里调用函数去执行你要执行的那个函数。(希望没把大家搞晕。)我还特意把我们之前的函数指针调用给注释了哟,哈哈哈!


     前戏已经做足相信大家也已经迫不及待了吧,哈哈,我们的信号捕捉器正式开!!!

我们已经了解到了 信号、alarm()闹钟函数、函数指针和回调函数了,再看之前的图片。

20191101191515908.png

是不是不在瑟瑟发抖啦。哈哈哈,其实只要你认真看了我之前的信号捕捉器开端篇,书上给你介绍的宏(有点没说清楚),理解也会更深刻!

再给出我们之前的signal()函数

#include<signal.h>
void (*signal)(int signo, void(*func)(int)))(int);
函数名: signal
参数: int signo, void(* func)(int)
返回类型:参数类型为 int 型, 返回 void 型函数指针。
--->为了在产生信号时调用, 返回之前注册的函数指针
现在看这个是不是有些感觉了,哈哈。

调用上述函数时,第一个参数的信息,就是博主前面的博客提到的那些信号类型。

第二个参数为特殊情况下要调用的函数的地址值(指针==函数名),当发生第一个参数的情况下,就会调用第二个参数所指向的函数了。


下面给出signal()函数中用到的一些信号(部分),更多的可以在我的信号的介绍博客看到详细的信号处理。


SIGALRM: 已到通过调用 alarm 函数注册的时间

SIGINT:输入 CTRL+C

SIGCHIL:子进程终止

了解到这些我们就能去看一看书本中的代码了!!!!

20191101194324830.png

下面是更加详细的解释,相信大家也能看懂了吧。

20191101194455578.png

博主主要是为了让大家能明白书中的信号捕捉器而讲了那么多,也相信大家已经明白了。所以博主还要告诉大家一件事,signal()函数用到的不多,更多的是另一个函数来处理信号那就是:sigaction()函数。


朋友们坚持不!博主保证,这是最后一个了

网络编程之 信号捕捉器(函数指针与回调函数)(2)https://developer.aliyun.com/article/1414739?spm=a2c6h.13148508.setting.14.4d4d4f0erzCTC6

目录
相关文章
|
4月前
|
Unix
网络编程之 信号捕捉器(函数指针与回调函数)(2)
sigaction()函数 前面我们讲到的内容已经足以用来防止僵尸进程生成的代码。之所以博主还要介绍sigaction()函数是因为它类似于signal()函数,而且完全可以代替后者,也更稳定(主要是书上介绍到了
31 1
|
4月前
|
Linux
网络编程之信号(处理僵尸进程的终极办法)之初识信号捕捉器
接着我们之前的管道所提出来的问题() 在创建子进程之后,子进程究竟何时终止????调用waitpid函数后还要无休止的等待子进程终止吗???”,这显然会是一个问题。因为父进程往往与子进程一样繁忙,因此我们不能只调用waitpid函数来等待子进程终止。那么我们应该怎么办呢??? 信号闪亮登场!!!!!
30 0
|
2月前
|
存储 C语言
c语言函数指针和指针函数的区别,以及回调函数的使用。
c语言函数指针和指针函数的区别,以及回调函数的使用。
9 0
|
2月前
|
存储 程序员 API
C函数指针与回调函数
C函数指针与回调函数
26 0
|
3月前
|
C语言 C++
C语言之指针进阶篇_回调函数(3)
C语言之指针进阶篇_回调函数(3)
23 0
【计算机网络】物理链路通信信号
【1月更文挑战第27天】【计算机网络】物理链路通信信号
|
4月前
|
编译器 C++
函数指针和回调函数对函数取地址和直接使用函数名的差距
函数指针和回调函数对函数取地址和直接使用函数名的差距
41 0
|
4月前
leetcode-1620:网络信号最好的坐标
leetcode-1620:网络信号最好的坐标
24 0
|
10天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。
|
3天前
|
网络协议 算法 Linux
【Linux】深入探索:Linux网络调试、追踪与优化
【Linux】深入探索:Linux网络调试、追踪与优化