进程间通信-信号

信号相关概念

        信号是进程间通信方式;信号是唯一的一种异步通信方式;是软件层次上中断模拟。

信号的特点

1、 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
2、 信号可以在进程与进程之间,内核与进程之间产生。
3、 信号可以被阻塞,可以延迟送达,但信号不会丢失。

信号生存周期

用户进程对信号的响应方式

1、 捕捉信号 ( 响应信号处理 )。
2、 忽略信号 ( 视而不见 )。
3、 执行默认处理 ( 缺省操作 )。
SIGKILL SIGSTOP 是保留给系统管理和调试用的,不能被捕获,也不能忽略 , 只能执行默认动作( SIGKILL 终止进程,SIGSTOP 暂停进程)。

信号处理流程

1、信号是内核发出;
2、信号处理很快;

常用信号说明

linux 信号是固定,内核已经定义好了。

kill -l 查看系统支持的信号列表,看到 linux 的信号。

一共64种,重点前面30多种。

标准 LINUX 是一个非实时的操作系统,它不用 SIGTMIN 34 )到 SIGTMAX 64 )之间的信号。

给前台进程中的每一个进程 , 信号默认处理就是终止。
常用信号说明:
1、SIGHUP 控制终端 退出,默认操作终止;
2、SIGINT,CTRL+C, 死循环结束;
3、SIGQUIT ctrl+\ , 死循环也终止,类似 SIGINT;

4、SIGILL 堆栈溢出;SIGFPE 5/0;

编译通过有警告 :
1、运行时出错;
2、当你除 0 内核将给你发一个信号, SIGFPG 默认处理是终止;
3、SIGKILL 自杀信息,不能阻塞,必死无疑,只执行默认动作,杀死病毒用它;
4、SIGALRM 时钟信号,定时器到时;
5、SIGSTOP 不能忽略,暂停进程;
6、SIGCHLD 父子进程之间;

  7、SIGABORT abort() 调用时异常退出进程,会发出该信号; 
  8、SIGSEGV 用户访问了非法内存内核给进程发出的;
  9、SIGUSR1/SIGUSR2 用户自定义;
10、SIGCONT、SIGSTOP 成对、功能相反;

信号相关函数

1、信号处理函数 signal

注意: SIGKILL SIGSTOP 不能安装信号处理函数。
handler 填写 SIG_IGN: 忽略相应的信号;
handler 填写 SIG_DFL: 执行默认处理。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>

//信号处理函数
void  sig_ctrl_c_handler (int signo)
{
	if (signo == SIGINT)
		printf("receive a SIGINT signal\n");

}



int main(int argc, char **argv)
{
	//注册信号处理函数
	signal(SIGINT, sig_ctrl_c_handler);

	while (1) {
		printf("sleep\n");
		sleep(1);
	}
	return 0;

}

2、产生信号的 kill 函数

 

sig:是信号前的数字;用 kill -l 查看;把 sig 信号发给 pid 进程。

给指定的进程发信号,有 3 种情况:
pid >  0: sig 信号发送给 pid 指定的那个进程;
pid =  0: 把信号发送给当前进程所在的进程组;
pid = -1: 给有权限发送信号的所有进程都发送 sig 信号 (pid =1 init 进程除外 );
pid < -1: 不用理它;

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>


int main(int argc, char **argv)
{
	kill(atoi(argv[1]), SIGKILL);
	return 0;

}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

int main(void)
{
	//同一个程序中执行不同的进程
	pid_t pid;
	if ((pid = fork()) < 0) {
		perror("fork");
		exit(1);
	} else if (pid > 0) {  //fork后父进程执行代码入口
		printf("parent pid=%d \n",getpid());
		printf("parent ppid=%d \n",getppid());
		sleep(30);
		kill(0, SIGKILL);

	} else { //fork后子进程执行代码入口
		printf("child pid=%d \n",getpid());
		printf("child ppid=%d \n",getppid());
		sleep(100);
	}

	
	return 0;

}


3、 产生信号的 raise 函数

 给自己发信号,在进程中等价于:kill(getpid(), sig); 在多线程中等价于: pthread_kill(pthread_self(), sig); 给自己发信号,只能给自己玩不跟别人玩。

4、 产生信号的 alarm 函数

 闹钟到时,内核会向进程发送SIGALRM信号。alarm 函数不阻塞,SIGALRM 采用默认方式自杀。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>

int main(int argc, char **argv)
{
	alarm(3); //不阻塞,默认动作终止进程
	printf("start ...\n");
	sleep(5);
	printf("end...\n");
	return 0;

}

5、 接收信号的 pause 函数

pause 函数使调用进程挂起直到有信号递达。 如果信号的处理动作是终止进程,则进程终止,pause 函数没有机会返回; 如果信号的处理动作是忽略,则进程继续处于挂起状态,pause 不返回;

如果信号的处理动作是捕捉,则调用了信号处理函数之后 pause 返回 -1 errno 设置为 EINTR ,所以 pause 只有出错的返回值,错误码EINTR 表示“被信号中断”。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>


//信号处理函数
void  sig_alarm (int signo)
{
	if (signo == SIGALRM)
		printf("receive a SIGINT signal\n");

}


int main(int argc, char **argv)
{
	int ret;

	//注册SIGALM信号处理函数
	//signal(SIGALRM, sig_alarm);//捕捉信号
	signal(SIGALRM, SIG_IGN);//忽略信号
	//signal(SIGALRM, SIG_DFL); //默认
	alarm(3); //不阻塞,默认动作终止进程
	ret = pause(); //让当前进程暂停
	if (ret==-1 && errno==EINTR) {
		printf("pause() return!\n");
	}
	printf("bye\n");
	
	return 0;

}

示例:一个进程W不停的打印时间,遇到进程R发送信号则执行回调函数写入文件一条时间记录;另一个进程R在死循环,遇到ctrl+c,向进程W发送一个信号。

/*
实现一个往管道写pid,输出的功能
*/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <time.h>


//信号处理函数
void  sig_foo (int signo)
{
	if (signo == SIGUSR1){
		printf("receive a SIGUSR1 signal\n");
		FILE *fp;
		time_t tm;
		if((fp = fopen("data.txt", "a+"))==NULL) {
			perror("fopen");
			exit(1);
		}
		time(&tm);
		fputs(ctime(&tm), fp);
		//fflush(fp)
		fclose(fp);
	}
}

int main(int argc, char **argv)
{
	int fd;
	pid_t pid;
	time_t tm;
	pid = getpid();
	signal(SIGUSR1, sig_foo);
	if (argc != 2) {
		printf("usage: %s <filename> \n",argv[0]);
		exit(1);
	}

	//打开有名管道
	if ((fd = open(argv[1], O_RDWR))<0) {
		perror("open");
		exit(1);
	}
	write(fd, &pid, sizeof(pid));
	//write(fd, &pid, sizeof(pid));
	while(1) {
		time(&tm);
		printf("%s", ctime(&tm));
		sleep(1);
	}
	close(fd);
    return 0;
}



/*
实现一个进程读管道,输入的功能
*/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>


pid_t pid;


//信号处理函数
void  sig_int (int signo)
{
	if (signo == SIGINT){
		printf("receive a SIGINT signal\n");
		kill(pid, SIGUSR1);
	}
}

int main(int argc, char **argv)
{
	int fd;
	

	signal(SIGINT, sig_int);

	
	if (argc != 2) {
		printf("usage: %s <filename> \n",argv[0]);
		exit(1);
	}

	//打开有名管道
	if ((fd = open(argv[1], O_RDWR))<0) {
		perror("open");
		exit(1);
	}
	read(fd, &pid, sizeof(pid));
	close(fd);
	while (1) 
		sleep(1);
	
    return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
进程间通信中,信号是一种非常重要的机制。它可以让一个进程向另一个进程发送一个异步通知,让接收进程执行特定的操作。 信号的基本概念是:当一个进程需要通知另一个进程发生了某个事件时,它可以向另一个进程发送一个信号。接收进程在接收到信号后,可以选择忽略该信号或者执行相应的处理程序。 信号可以由如下几种方式发送: 1. kill命令:可以使用kill命令向指定进程发送信号。 2. 信号函数:可以使用信号函数向指定进程发送信号。 3. 软件异常:当一个进程执行某些非法操作时,操作系统会向该进程发送一个信号。 常见的信号有: 1. SIGINT:中断进程。通常由Ctrl+C键触发。 2. SIGTERM:请求进程终止。通常由kill命令发送。 3. SIGKILL:强制进程终止。通常由kill命令发送。 4. SIGSTOP:暂停进程。通常由kill命令发送。 5. SIGCONT:恢复进程。通常由kill命令发送。 6. SIGUSR1和SIGUSR2:用户自定义信号。 在Linux系统中,可以使用kill命令向指定进程发送信号。例如: ``` kill -SIGINT pid ``` 这个命令会向指定pid的进程发送SIGINT信号,让该进程接收到信号后执行相应的处理程序。 在编写程序时,可以使用信号函数向指定进程发送信号。例如,下面的代码向指定进程发送SIGUSR1信号: ``` #include <signal.h> #include <unistd.h> int main() { int pid = 12345; // 指定进程的pid kill(pid, SIGUSR1); // 向指定进程发送SIGUSR1信号 return 0; } ``` 当接收进程接收到信号后,可以执行相应的处理程序。例如,下面的代码定义了一个信号处理程序,当接收到SIGUSR1信号时会执行该处理程序: ``` #include <signal.h> void handle_signal(int signum) { // 处理SIGUSR1信号 } int main() { signal(SIGUSR1, handle_signal); // 注册信号处理程序 while(1) {} // 循环等待信号 return 0; } ``` 上面的代码中,signal函数用于注册信号处理程序,当接收到SIGUSR1信号时会执行handle_signal函数。循环部分是为了等待信号的到来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁静的海2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值