信号机制及其使用
概念:信号是UNIX和Linux系统响应某些条件而产生的事件,信号提供了一种处理异步事件的方法,信号是由于某些错误条件产生的,例如内存段错误、浮点处理器错误、非法指令等。
处理信号的三种方式:
忽略此信号。大多数信号都可以被忽略,但是SIGKILL和SIGSTOP这两种信号不可以被忽略,原因:它是超级用户终止进程的可靠方法,
捕获信号,在某种信号发生的时候需要调用用户函数,在用户函数中对这种事件的处理,例如:SIGCHLD信号标识是子进程结束,所以可以捕获此信号调用waitpid获取子进程的ID和终止状态。
默认,系统会对信号作出默认动作,大部分为终止进程。
信号的使用:
signal函数:
void (*signal (int sig, void (*func) (int))) (int);
singnal函数说明粗函数需要两个参数,返回一个函数指针,而该指针所指向的函数无返回值(void),第一个参数sig是一个整数,准备捕获或忽略的信号,第二个参数是一个函数指针,它指向的函数需要一个整形参数,无返回值,signal函数的返回值是一个函数地址,该函数有一个整形参数。
可重入函数:一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的(多线程)。
保证函数可重入方法:
函数中尽量使用局部变量(寄存器、堆、栈)
操作全局变量要加以保护(信号量、锁等),这样构成的函数是可重入的。
发送信号:
kill函数将信号发送给进程或者进程组,raise函数则允许进程想自身发送信号。
int kill(pid_t pid, int sig); 底层为sys_kill()
int raise(int sig);
两者之间的关系 raise(sig) == kill(getpid(), sig)
kill函数的pid参数四种情况
pid>0 发送信号到指定进程的pid
pid=0 将信号发送给当前进程组的所有进程
pid=-1 发送信号给除第一个进程外的所有进程(权限所能发送的进程,权限不足则无法发送)
pid<0 发送信号给进程组ID为|pid|的所有进程
进程间通讯
管道
概念:类似于现实中管道一样进程的数据从一端流向另一端,即一端进行写操作,另一端执行读操作,也导致管道是半双工通讯。若管道为空,read会阻塞,若管道满,则write会阻塞。
有名管道&无名管道:
有名管道:可以再任意进程之间进行通讯,而且是双向的,但同一时间仅能一端读一端写。
无名管道:只能在有亲缘关系的进程中使用并且是单向的,只能一端读,另一端写。
管道符合先进先出的原则。
编程实例:
有名管道
//write
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
int main()
{
int fd = open("./FIFO", O_WRONLY);
assert(fd != -1);
printf("FOFO open success\n");
while(1)
{
char buff[128] = {0};
printf("please input: ");
scanf("%s", buff);
write(fd, buff, strlen(buff));
if(strncmp(buff, "end", 3) == 0)
{
break;
}
}
close(fd);
exit(0);
}
//read
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<fcntl.h>
int main()
{
int fd = open("./FIFO", O_RDONLY);
assert(fd != -1);
printf("open FIFO success\n");
while(1)
{
char buff[128] = {0};
int n = read(fd, buff, 127);
if(n == 0 || (n == 3 &&