(50)LINUX应用编程和网络编程之五 Linux信号(进程间通信)

信号实现进程间的通信

3.5.1.什么是信号

3.5.1.1、信号是内容受限(只是一个int型的数字)的一种异步通信机制

(1)信号的目的:用来通信(进程与进程之间的通信)

(2)信号是异步的(对比硬件中断),信号好像就是一种软件中断。

(3)信号本质上是int型数字编号(事先定义好的)

3.5.1.2、信号由谁发出

(1)用户在终端按下按键

(2)硬件异常后由操作系统内核发出信号

(3)用户使用kill命令向其他进程发出信号

(4)某种软件条件满足后也会发出信号,如alarm闹钟时间到会产生SIGALARM信号,向一个读端已经关闭的管道write时会产生SIGPIPE信号

3.5.1.3、信号由谁处理、如何处理

(1)忽略信号

(2)捕获信号(信号绑定了一个函数)

(3)默认处理(当前进程没有明显的管这个信号,默认:忽略或终止进程)

3.5.2.常见信号介绍(信号的名字 )

信号宏                    num                                         信号对应的作用

(1)【SIGINT    】                2                                             Ctrl+C时OS送给【前台】进程组中【每个】进程

(2)SIGABRT                        6                                            调用abort函数,进程异常终止

(3)【SIGPOLL    /   SIGIO    】        8                                   指示一个异步IO事件,在高级IO中提及

(4)【SIGKILL】                            9                                杀死进程的终极办法

(5)SIGSEGV                       11                                            无效存储访问时OS发出该信号

(6)【SIGPIPE    】                   13                                    涉及(异步通信的)管道和socket

(7)【SIGALARM】                       14                                涉及alarm函数的实现

(8)SIGTERM                       15                                             kill命令发送的OS默认终止信号

(9)【SIGCHLD    】               17                                         子进程终止或停止时OS向其父进程发此信号等待父进程回收

以上所有信号的作用都是事先定义好的。

(10)

SIGUSR1                           10                                          用户自定义信号,作用和意义由应用自己定义

SIGUSR2                           12

这两个名称是预先已经定义好了的,但是作用是用户自己定义的。

3.5.3.进程对信号的处理

3.5.3.1、signal函数介绍

signal()函数理解

在<signal.h> 这个头文件中。

signal(参数1,参数2);

参数1:我们要进行处理的信号。系统的信号我们可以再终端键入 kill -l查看(共64个)。其实这些信号就是系统定义的宏。

[email protected]:/mnt/hgfs/Winshare/1.Linux应用编程和网络编程/7.thread# kill -l

1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP

6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1

11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM

16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP

21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ

26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR

31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3

38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8

43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13

48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12

53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7

58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2

63) SIGRTMAX-1    64) SIGRTMAX

参数2:我们处理这些信号的方式(是系统默认还是忽略还是捕获)。

一般有3中方式进行操作。

1)eg: signal(SIGINT ,SIG_IGN );          //忽略处理

//SIG_ING 代表忽略SIGINT信号,SIGINT信号代表由InterruptKey产生,通常是CTRL +C 或者是DELETE 。发送给所有ForeGround Group的进程。

2)eg: signal(SIGINT ,SIG_DFL );      //默认处理(当前进程没有明显的管这个信号,默认:忽略或终止进程)

//SIGINT信号代表由InterruptKey产生,通常是CTRL +C或者是DELETE。发送给所有ForeGroundGroup的进程。 SIG_DFL代表执行系统默认操作,其实对于大多数信号的系统默认动作时终止该进程。这与不写此处理函数是一样的。

3)捕获信号并处理(信号绑定了一个函数)

3.5.3.2、用signal函数处理SIGINT信号

(1)忽略处理

#include<stdio.h>

#include<signal.h>

int main(void)

{

signal(SIGINT ,SIG_IGN );

while(1)

{

printf("hello world!\n");

sleep(1);

}

return 0;

}

(2)默认处理

signal(SIGINT ,SIG_DFL )

#include<stdio.h>

#include<signal.h>

int main(void)

{

signal(SIGINT ,SIG_DFL );

while(1)

{

printf("hello world!\n");

sleep(1);

}

return 0;

}

-----------------------------------------------------------------------------------------------------------------------------------------------------------

(3)捕获处理

signal函数原型:

#include <signal.h>

typedef void (*sighandler_t)(int);    //typedef sighandler_t这个函数指针变量类型,重命名一种类型

sighandler_t signal(int signum, sighandler_t handler);//第二个参数是一个函数指针类型的变量,将来传进去一个函数名;返回一个函数指针。

细节:

(1)signal函数绑定一个捕获函数后信号发生后会自动执行绑定的捕获函数,并且把信号编号s数字作为传参传给捕获函数

(2)signal的返回值在出错时为SIG_ERR,绑定成功时返回指向我们自定义的捕获函数的函数指针。

(3)signal()函数(它自己是带两个参数,一个整型的信号编号,以及一个指向用户自定义的信号处理函数的指针。),而这个signal()函数的返回值也为一个函数指针,【这个函数指针指向一个带一个整型参数,并且返回值为void的一个函数.】(也就是说signal()函数的返回值和我们向它里面传递的第二个参数的类型是一样的,都是一个函数指针类型的,且这个函数指针指向的函数的返回值为void,参数为int)【相当于我们在signal函数的两个接收参数中第二个参数是向系统中【绑定】这个要执行的函数】,等到我们第一个参数中注册的信号真的发生了之后,我们signal函数的返回值就指向了我们用户自定义的函数去执行它。

(4)signal函数的返回值,它的返回值为一个函数指针,如果signal函数执行错误,则返回SIG_ERR,如果执行成功则返回一个指向自定义函数的函数指针。

(5)等于说我们在第二个参数中是向内核注册(绑定)我们的自定义函数,而当信号真的发生后,返回一个指向我们自定义函数的函数指针,是去执行我们的绑定的自定义函数。

代码示例:

#include <stdio.h>

#include <signal.h>

#include <stdlib.h>

typedef void (*sighandler_t)(int);            //重新声明一个函数指针sighandler_t

void func(int sig)

{

if (SIGINT == sig)

printf("func for signal: %d.\n", sig);

exit(-1) ;

}

int main(void)

{

sighandler_t ret ; //ret为一个函数指针变量,指向一个函数

ret=signal(SIGINT, func);          //相当于先向系统中注册这个函数,SIGINT是Ctrl+C时OS送给【前台】进程组中【每个】进程

//signal(SIGINT, SIG_DFL);        // 指定信号SIGINT为默认处理

//ret = signal(SIGINT, SIG_IGN);        // 指定信号SIGINT为忽略处理

if (SIG_ERR == ret)

{

perror("signal:");

exit(-1);

}

printf("before loop\n");

while(1)

{

printf("hello world!\n");

sleep(1);

}

printf("after loop\n");

return 0;

}

3.5.3.3、signal函数的优点和缺点

(1)优点:简单好用,捕获信号常用

(2)缺点:无法简单直接得知之前设置的对信号的处理方法

3.5.3.4、sigaction函数介绍

#include <signal.h>

int sigaction(int sig, const struct sigaction *act,struct sigaction *oact);

(1)2个都是API,但是sigaction比signal更具有可移植性

(2)用法关键是sigaction函数的两个指针参数

sigaction比signal好的一点:sigaction可以一次得到设置新捕获函数和获取旧的捕获函数(其实还可以单独设置新的捕获或者单独只获取旧的捕获函数),而signal函数不能单独获取旧的捕获函数而必须在设置新的捕获函数的同时才获取旧的捕获函数。

sigaction代码示例:

#include <stdio.h>

#include <signal.h>

#include <stdlib.h>

#include <unistd.h>

void func(int sig)

{

if(SIGINT!=sig)

return ;

printf("信号num是:%d\n",sig);

}

int main(void)

{

struct sigaction act;

act.sa_handler=func;

int res=-1;

res=sigaction(SIGINT,&act,NULL);

if(res==-1)

{

perror("sigaction");

_exit(-1);

}

printf("before loop:\n");

while(1)

{

printf("hello world\n");

sleep(1);

}

return 0;

}

3.5.4.alarm和pause函数

3.5.4.1、alarm函数:主要是在设定秒数后向signal发送信号,然后执行绑定函数;alarm()函数用于在系统中设置一个定时器,在定时器到时后会向进程发送SIGALRM信号。SIGALRM信号的 默认处理方式是终止进程。

(1)内核以API形式提供的闹钟(内核帮一个进程只维护一个alarm时钟)

(2)编程实践

所需头文件

  #include<unistd.h>

函数原型

  unsigned int alarm(unsigned int seconds)

函数参数

  seconds:指定秒数

函数返回值

  成功:如果调用此alarm()前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。

  出错:-1

注意:alarm只设定一个闹钟,时间到达并执行其注册函数之后,闹钟便失效。【如果想循环设置闹钟,需在其注册函数(hander函数中)再调用alarm函数。】

代码示例:

#include<unistd.h>

#include<signal.h>

#include<stdio.h>

void handler()

{

printf("Hello。。。。。\n");

}

void main()

{

int i;

signal(SIGALRM, handler); //让内核做好准备,一旦接受到SIGALARM信号,就执行 handler

alarm(5);//在5秒过后执行handler函数

for(i=1;i<21;i++)

{

printf("sleep %d ...\n",i);

sleep(1);

}

}

3.5.4.2、pause函数

(1)内核挂起

(2)代码实践

pause函数的作用就是让当前进程暂停运行,交出CPU给其他进程去执行。当当前进程进入pause状态后当前进程会表现为“卡住、阻塞住”,要退出pause状态当前进程需要被信号唤醒。

3.5.4.3、使用alarm和pause来模拟sleep

pause函数代码示例:

#include <unistd.h>

#include <stdio.h>

#include <signal.h>

void sigroutine(int unused)

{

printf("Catch a signal SIGINT \n");

}

int main()

{

signal(SIGINT, sigroutine);//SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出

pause();//在没有收到任何的信号的时候,pause相当于一个死循环,当收到任何一个信号的时候,就执行下一句

sleep(3);

printf("receive a signal \n");

}

使用alarm和pause来模拟sleep代码示例:

#include <stdio.h>

#include <unistd.h>            // unix standand

#include <signal.h>

void func(int sig)

{

/*

if (sig == SIGALRM)

{

printf("alarm happened.\n");

}

*/

}

void mysleep(unsigned int seconds);

int main(void)

{

printf("before mysleep.\n");

mysleep(3);

printf("after mysleep.\n");

/*    unsigned int ret = -1;

struct sigaction act = {0};

act.sa_handler = func;

sigaction(SIGALRM, &act, NULL);

//signal(SIGALRM, func);

ret = alarm(5);

printf("1st, ret = %d.\n", ret);

sleep(3);

ret = alarm(5);        // 返回值是2但是本次alarm会重新定5s

printf("2st, ret = %d.\n", ret);

sleep(1);

ret = alarm(5);

printf("3st, ret = %d.\n", ret);

//while (1);

pause();

*/

return 0;

}

void mysleep(unsigned int seconds)

{

struct sigaction act = {0};

act.sa_handler = func;

sigaction(SIGALRM, &act, NULL);

alarm(seconds);

pause();

}

时间: 2024-10-12 05:18:02

(50)LINUX应用编程和网络编程之五 Linux信号(进程间通信)的相关文章

(46)LINUX应用编程和网络编程之一Linux应用编程框架

3.1.1.应用编程框架介绍 3.1.1.1.什么是应用编程 (1)整个嵌入式linux核心课程包括5个点,按照学习顺序依次是:裸机.C高级.uboot和系统移植.linux应用编程和网络编程.驱动. (2)典型的嵌入式产品就是基于嵌入式linux操作系统来工作的.典型的嵌入式产品的研发过程就是:第一步让linux系统在硬件上跑起来(系统移植工作),第二步基于linux系统来开发应用程序实现产品功能. (3)基于linux去做应用编程,其实就是通过调用linux的[系统API]来实现应用需要完成

Linux程序设计学习笔记----网络编程之网络数据包拆封包与字节顺序大小端

网络数据包的封包与拆包 过程如下: 将数据从一台计算机通过一定的路径发送到另一台计算机.应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装(Encapsulation),如下图所示: 不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据包(packet),在链路层叫做帧(frame).数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理. 上图对应两台计算机在同一网段中的情况,

XMPP-05Socket编程之网络编程篇

要学习XMPP,就要先了解Socket编程,在学习Socket之前,还要先了解一下网络编程 一.网络编程基本概念 通过使用套接字来达到进程间通信目的的编程就是网络编程. 网络编程从大的方面说就是对信息的发送到接收,中间传输为物理线路的作用,编程人员可以不用考虑…… 网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的!中间最主要的就是数据包的组装,数据包的过滤,数据包的捕获,数据包的分析,当然最后再做一些处理

66.JAVA编程思想——网络编程

66.JAVA编程思想--网络编程 历史上的网络编程都倾向于困难.复杂,而且极易出错. 程序员必须掌握与网络有关的大量细节,有时甚至要对硬件有深刻的认识.一般地,我们需要理解连网协议中不同的"层"(Layer).而且对于每个连网库,一般都包含了数量众多的函数,分别涉及信息块的连接.打包和拆包:这些块的来回运输:以及握手等等.这是一项令人痛苦的工作.但是,连网本身的概念并不是很难.我们想获得位于其他地方某台机器上的信息,并把它们移到这儿:或者相反.这与读写文件非常相似,只是文件存在于远程

iOS网络编程开发—网络编程基础

iOS网络编程开发—网络编程基础 一.网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过网络跟外界进行数据交互.数据更新,应用才能保持新鲜.活力 (3)如果没有了网络,也就缺少了数据变化,无论外观多么华丽,终将变成一潭死水 移动网络应用 = 良好的UI + 良好的用户体验 + 实时更新的数据 新闻:网易新闻.新浪新闻.搜狐新闻.腾讯新闻 视频:优酷.百度视频.搜狐视频.爱奇艺视频 音乐:QQ音乐

linux服务端的网络编程

常见的Linux服务端的开发模型有多进程.多线程和IO复用,即select.poll和epoll三种方式,其中现在广泛使用的IO模型主要epoll,关于该模型的性能相较于select和poll要好不少,本文也主要讨论该模型而忽略另外两种IO复用模型. 多线程相较于多进程开销比较小,但是要主要主线程往子线程传递数据的时候要注意变量互斥访问来保证线程安全. epoll模型在Linux2.6内核中引入的,改进了select中的一些明显设计上的缺点,具有更高的效率.主要体现在以下几个方面: 1. epo

Linux下的socket网络编程

linux 网络编程是通过socket(套接字)接口实现,Socket是一种文件描述符,socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开-读/写-关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件. socket 类型 常见的socket有3种类型如下.     (1)流式socket(SOCK_STREAM )     流式套接字提供可靠

linux下简单socket网络编程

在进行socket网络编程时, 我们需要了解一些必备的知识,例如什么是socket,ipv4的地址结构,套接字类型等等,不然上来直接看代码就会晕,当初学习网络编程时,看书上的例子,总有感觉书上讲的都很简要.再或者讲的原理太多把人绕晕.我这里只想让大家简单知道怎么使用socket进行网络编程并且给出的例子可以直接使用参考. 1. 什么是socket (1) socket 可以看成是用户进程与网络协议栈的编程接口.就是说应用层可以看成是用户进程,传输层网络层数据链路层看成网络协议栈,因为这三个层的传

第8章 面向对象高级编程与网络编程

面向对象的特征 一.多态 程序在运行的过程中,根据传递的参数的不同,执行不同的函数或者操作不同的代码,这种在运行过程中才确定调用的方式成为运行时多态 import abc #多态:同一种事物的多种形态 class Animal: #同一类事物:动物 def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之二:狗 def ta