网站开发工作分解结构的树形图,徐州做公司网站多少钱,建设网站建议,如何进行电子商务网站推广目录
一、信号
1、基本概念
2、用户处理信号的方式
3、查看信号
4、可靠信号和不可靠信号
5、信号种类 6、终止进程信号的区别
二、进程对信号的处理
1、signal#xff08;#xff09;函数
2、sigaction#xff08;#xff09;函数
3、代码演示
4、运行结果…目录
一、信号
1、基本概念
2、用户处理信号的方式
3、查看信号
4、可靠信号和不可靠信号
5、信号种类 6、终止进程信号的区别
二、进程对信号的处理
1、signal函数
2、sigaction函数
3、代码演示
4、运行结果
三、实战演练 四、补充
1、alarm函数
2、wait函数
3、僵尸进程和孤儿进程 一、信号
1、基本概念 信号是Linux系统中用于进程之间通信或者操作的机制它给进程提供一种异步的软件中断信号可以在任何时候发送给某一进程而无须知道该进程的状态。如果该进程并未处于执行状态则该信号就由内核保存起来直到该进程恢复执行并传递给他为止。如果一个信号被进程设置为阻塞则该信号的传递被延迟直到其阻塞被取消时才被传递给进程。
2、用户处理信号的方式
进程接受到信号后有三种处理方式
1忽略忽略某个信号对该信号不做任何处理就像从未发生过。
2捕捉类似中断的处理程序对于需要处理的信号进程可以指定处理函数由该函数来进行处理。
3默认/缺省Linux对每种信号都规定了默认操作通常是终止该进程。 注意有两个信号比较特殊需要特殊记一下——SIGKILL 和 SIGSTOP这是两个不能捕捉的信号或忽略的信号。不能被忽略的原因是他们向超级用户提供了使进程终止或停止的可靠方法。大概讲一下两个信号的区别SIGKILL这是一个 “我不管您在做什么,立刻停止”的信号。假如您发送SIGKILL信号给进程Linux就将进程停止在那里。SIGSTOP 停止进程的执行但是该进程还未结束, 只是暂停执行.。
3、查看信号 我们可以使用如下的命令查看当前系统支持的信号需要注意的是不同的系统支持的信号是不一样的 查看信号的命令kill -l 给大家看一下我系统所支持的信号 注意信号本质上是 int 类型的数字编号。内核针对每个信号都给其定义了一个唯一的整数编号从数字 1 开始顺序展开。并且每一个信号都有其对应的名字其实就是一个宏 信号名字与信号编号乃是一一对应关系但是由于每个信号的实际编号随着系统的不同可能会不一样所 以在程序当中一般都使用信号的符号名也就是宏定义。这些信号在头文件中定义每个信号都是以 SIGxxx 为开头。
4、可靠信号和不可靠信号 Linux信号机制基本上是从UNIX系统中继承过来的。早期UNIX系统中的信号机制比较简单和原始信号值小于SIGRTMIN的信号都是不可靠信号。这就是“不可靠信号”的来源它的主要问题就是信号可能丢失。随着时间发展实践证明了有必要对信号的原始机制加以改进和扩充。由于原来定义的信号已经有许多应用不好再做改动最后只好又新增加了一些信号并在一开始就把它们定义为可靠信号这些信号支持排队不会丢失。 信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号可靠信号克服了信号可能丢失的问题。对于目前Linux的两个信号安装函数signal及sigaction来说它们都不能把SIGRTMIN以前的信号变成可靠信号都不支持排队仍有可能丢失仍然是不可靠信号而且对SIGRTMIN以后的信号都支持排队。这两个函数的最大区别在于经过sigaction安装的信号都能传递信息给信号处理函数而经过signal安装的信号不能向信号处理函数传递信息。对于信号发送函数来说也是一样的。
5、信号种类 经过上面的学习我们已经知道了如何查看信号那么每个信号都代表着什么意思呢该信号的默认动作又是什么呢让我们继续往下学习吧红色信号为常见信号其余信号了解即可
信号编号信号名信号说明默认动作1SIGHUP在终端的控制进程结束时发出程序终止2SIGINTCTRLc按键终止程序运行的信号程序终止3SIGQUITCTRL\按键输入时产生的信号程序终止4SIGILL非法的指令程序终止5SIGTRAP跟踪自陷由断点指令或其它trap指令产生建立CORE文件6SIGABRT当调用abort函数时会产生当前信号程序终止7SIGBUS运行非本CPU相关编译器编译的程序程序终止8SIGFPE算术异常时产生建立CORE文件9SIGKILL强制杀死程序序号任何程序都不可以捕捉该信号程序终止不可被捕捉10SIGUSR1用户自定义信号1不会自动产生只能使用kill函数或者命令给指定的进程发送当前信号程序终止11SIGSEGV段错误系统给程序发送的信号程序终止12SIGUSR2用户自定义信号2不会自动产生只能使用kill函数或者命令给指定的进程发送当前信号程序终止13SIGPIPE管道破裂信号程序终止14SIGALRM当alarm函数设置的时间到达时会产生当前信号程序终止15SIGTERMkill命令默认发送的信号默认动作是终止信号程序终止16SIGSTKFLT数学协处理器的栈异常程序终止17SIGCHLD子进程退出信号忽略该信号18SIGCONT当产生当前信号后当前停止的进程会恢复运行停止的进程恢复运行19SIGSTOP停止进程的执行停止进程20SIGTSTPCTRLz按键输入时产生的信号但该信号可以被处理和忽略停止进程21SIGTTIN后台进程读终端停止进程22SIGTTOU后台进程写终端停止进程23SIGURG有紧急数据或out-of-band数据到达socket时产生忽略该信号24SIGXCPU超出CPU限制程序终止25SIGXFSZ文件长度过长程序终止26SIGVTALRM虚拟定时器超时程序终止27SIGPROF统计分布图用计时器到时程序终止28SIGWINCH终端窗口尺寸发生变化忽略该信号29SIGIO异步IO程序终止30SIGPWR电力故障程序终止31SIGSYS无效系统调用程序终止 6、终止进程信号的区别 经过上面的学习我们知道终止进程的信号有三种即SIGINT、SIGKILL、SIGTERM三者都是结束/终止进程运行.但三者之间却有区别。让我们一起来看看吧
1SIGINT 产生方式: 键盘CtrlC 产生结果: 只对当前前台进程,和他的所在的进程组的每个进程都发送SIGINT信号,之后这些进程会执行信号处理程序再终止。
2SIGTERM 产生方式: 和任何控制字符无关,用kill函数发送 本质: 相当于 kill pid 产生结果: 当前进程会收到信号,而其子进程不会收到.如果当前进程被kill(即收到SIGTERM),则其子进程的父进程将为init,即pid为1的进程。与SIGKILL的不同SIGTERM可以被阻塞,忽略,捕获,也就是说可以进行信号处理程序,那么这样就可以让进程很好的终止,允许清理和关闭文件。
3SIGKILL 产生方式: 和任何控制字符无关,用kill函数发送 本质: 相当于kill -9 pid 产生结果: 当前进程收到该信号,注意该信号是无法被捕获的,也就是说进程无法执行信号处理程序,会直接发送默认行为,也就是直接退出。这也就是为什么kill -9 pid一定能杀死程序的原因。 故这也造成了进程被结束前无法清理或者关闭资源等行为。
二、进程对信号的处理 Linux下有signal和sigaction两种信号安装的函数让我们分别来看看
1、signal函数
函数原型如下 #include signal.h typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 函数描述 signal函数用来在进程中指定当一个信号到达进程后该做什么处理信号处理函数的handler有两个默认值分别是SIG_IGN表示忽略行为和SIG_DFL表示默认行为。而且signal函数是阻塞的比如当进程正在执行SIGUSR1信号的处理函数此时又来一个SIGUSR1信号signal会等到当前信号处理函数处理完后才继续处理后来的SIGUSR1。
2、sigaction函数
函数原型如下 #include signal.h int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) stuct sigaction { void (*)(int) sa_handle; //信号处理函数 sigset_t sa_mask; //信号屏蔽集 int sa_flags; } 参数说明
1第一个参数 signum信号值。
2第二个参数act信号的处理参数。
3第三个参数oldact保存信号上次安装时的处理参数。
补充
1信号阻塞和signal函数类似当正处于某个信号的处理函数中时这个信号再次到达会被阻塞待信号处理函数完成之后再处理。
2sa_mask信号屏蔽集所谓屏蔽并不是忽略屏蔽的时间段是在信号处理函数执行期间一旦处理函数执行完毕将会重新唤醒此信号。
3sa_flag通常取值为0则表示默认行为。
3、代码演示 上面已经讲解了signal和sigaction两个函数的用法接下来我们用一个代码来实际操作一下吧
代码如下
#include stdio.h
#include string.h
#include signal.h
#include unistd.h
#include stdlib.hint g_signal 0;void signal_stop(int signum)
{if( SIGTERM signum ){printf(SIGTERM signal detected\n);}else if( SIGALRM signum ){printf(SIGALRM signal detected\n);g_signal 1;}}void signal_user(int signum)
{if( SIGUSR1 signum ){printf(SIGUSR1 signal detected\n);}else if( SIGUSR2 signum ){printf(SIGUSR2 signal detected\n);}g_signal 1;
}void signal_code(int signum)
{if( SIGBUS signum ){printf(SIGBUS signal detected\n);}else if( SIGILL signum ){printf(SIGILL signal detected\n);}else if( SIGSEGV signum ){printf(SIGSEGV signal detected\n);}exit(-1);}int main(int argc,char *argv[])
{char *ptr NULL;struct sigaction sigact,sigign;/*Use signal() install signal*/signal(SIGTERM,signal_stop);signal(SIGALRM,signal_stop);signal(SIGBUS,signal_code);signal(SIGILL,signal_code);signal(SIGSEGV,signal_code);/*Use sigaction() install signal*/
/*Initialize the catch signal structure.*/sigemptyset( sigact.sa_mask );sigact.sa_flags 0;sigact.sa_handler signal_user;/*Setup the ignore signal*/sigemptyset( sigign.sa_mask );sigign.sa_flags 0;sigign.sa_handler SIG_IGN;sigaction(SIGINT,sigign,0); /*ignore SIGINT signal by CTRLC*/sigaction(SIGUSR1,sigact,0); /*catch SIGUSR1*/sigaction(SIGUSR2,sigact,0); /*catch SIGUSR2*/printf(Program start running for 20 seconds...\n);alarm(20);while( !g_signal ){;}printf(Program start stop running...\n);printf(Invalid pointer operator will raise SIGSEGV signal\n);*ptr h; return 0;}
4、运行结果 大家是否理解这个运行结果呢如图可以看出进程一共接受到了两种信号分别是SIGALRM和SIGSEGV。为什么呢接收到SIGALRM是因为我们在程序中调用了alarm函数接收到SIGSEGV是因为代码最下面我们用了*ptr h而这条语句是一个指针错误。 大家应该注意到代码中有一行是sigaction(SIGINT,sigign,0);并且sigign.sa_handler SIG_IGN;这就代表着键盘CTRLc输入的SIGINT信号会被忽略掉那我们实际操作一下看看是不是如我们所想呢 正如我们所想键盘CTRLc输入的SIGINT信号因为已经被忽略掉所以不能在终止进程了。
三、实战演练
题目 我们知道父进程在创建子进程之后究竟是父进程还是子进程先运行没有规定这由操作系统的进程调度策略决定而如果在某些情况下我们需要确保父子进程运行的先后顺序则可以使用信号来实现进程间的同步。 要求写一个程序实现父子进程之间使用信号进行同步。如果父进程先执行则进入到循环休眠等待状态直到子进程给他发送信号之后才能跳出循环继续运行确保子进程先执行它的任务。同样子进程在执行完毕之后就等待父进程给他发送信号之后才能退出而父进程则通过调用wait系统调用等待子进程退出后父进程再退出。
参考代码如下
#include stdio.h
#include string.h
#include signal.h
#include sys/types.h
#include unistd.h
#include sys/wait.h
#include errno.hint g_child_stop 0;
int g_parent_run 0;void sig_child(int signum)
{if(SIGUSR1 signum){g_child_stop 1;}
}void sig_parent(int signum)
{if(SIGUSR2 signum){g_parent_run 1;}
}int main(int argc,char *argv[])
{int pid;int status;signal(SIGUSR1,sig_child);signal(SIGUSR2,sig_parent);if((pidfork()) 0){printf(Create child process failure:%s\n,strerror(errno));return -1;}else if(pid 0){/*Child process can do something first here.*/printf(Child process start runing!\n);/*when child process have done,then tell parent process to start running*/printf(Child process send parent a signal to tell parent process to run!\n);kill(getppid(),SIGUSR2);/*Waiting the stopping signal sent by parent process*/while( !g_child_stop ){sleep(1);}/*Child process have received the stopping signal*/printf(child process receive signal from parent and exit now!\n);return 0;}/*Only parent process run the codes beneath*//*Parents hangs up until receive signal from child*/while( !g_parent_run ){sleep(1);}/*Parent process have received the running signal from child process*//*Parent process can do something here*/printf(Parent start running now!\n);/*Parent process send a signal to tell child process to exit*/kill(pid,SIGUSR1);/*parent wait child process exit*/wait(status);printf(Parent wait child process die and exit now!\n);return 0;}运行结果如下 四、补充
最后针对上文提到的一些知识进行一些简单的补充
1、alarm函数
函数原型如下 unsigned int alarm(unsigned int seconds) 1功能 在进程中设置一个定时器在seconds秒之后将会发送SIGALRM信号给当前的进程故而alarm函数也被称为闹钟函数。如果在seconds秒内再次调用了alarm函数设置了新的闹钟那么之前设置的秒数将会被新的闹钟时间所取代
2参数 定时时间单位为秒。
3返回值 如果该alarm函数是进程中第一次调用则返回0如果不是第一次调用则返回上一次调用alarm函数剩余的时间。
2、wait函数
函数原型如下 #include sys/types.h #include wait.h int wait(int * status) 1函数功能: 父进程一旦调用wait函数就立即开始阻塞然后wait会分析当前进程的某个子进程是否已经退出如果让它找到了这样一个退出的子进程wait就会收集这个子进程的信息并把它彻底销毁后返回如果没有找到就一直阻塞直至找到一个结束的子进程或接收到了一个指定的信号为止。
【注意 当父进程忘记调用wait()等待已终止的子进程子进程就会进入一种没有父进程的状态此时子进程就是zombie僵尸进程。】
2参数status 用来保存被收集进程退出时的状态它是一个指向int类型的指针如果我们对这个子进程如何死掉的不在意只想这把这个被僵尸进程消灭掉就把这个参数置为NULL。如果status的值不是NULLwait把子进程的退出状态取出并存入其中这是一个整数值int。
3、僵尸进程和孤儿进程
1孤儿进程 父进程先于子进程结束当父进程退出时系统会让pid为1的进程接管子进程。所以孤儿进程的pid都是1 。
2僵尸进程 子进程先于父进程结束子进程成了僵尸zombie进程并且子进程会一直保持这样的状态直至重启此时内核只会保留进程的一些必要信息以备父进程所需此时子进程始终占有资源同时也减少了系统可以创建的最大进程数。
本篇文章用到了许多进程的知识如果大家对进程不是很了解可以看一下这篇文章《APUE学习之多进程编程》。我会坚持使用博客来整理自己所学知识同时也希望能够帮助到大家如果有哪些错误或者疑问也欢迎大家在评论区一起讨论