概述
进程间通信(IPC,Inter-Process Communication)
两个或多个进程间进行数据传送,参与通信的这些进程可能位于同一台机器上,也可能位于不同的机器上。
常用 IPC 方式:
- 信号(Signal)
- 管道(Pipe)
- 共享内存(Shared Memory)
- 消息队列(Message Queue)
- 套接字(Socket)
信号(Signal)
Linux 系统上古老经典的 IPC方式,主要用于实现异步通知,不能传输数据。
信号是一种软件中断(Software Break)
void (*signal(int sig, void (*func)(int)))(int)
- sig – 在信号处理程序中作为变量使用的信号码。下面是一些重要的标准信号常量:
宏 | 信号 |
---|---|
SIGABRT | (Signal Abort) 程序异常终止。 |
SIGFPE | (Signal Floating-Point Exception) 算术运算出错,如除数为 0 或溢出(不一定是浮点运算)。 |
SIGILL | (Signal Illegal Instruction) 非法函数映象,如非法指令,通常是由于代码中的某个变体或者尝试执行数据导致的。 |
SIGINT | (Signal Interrupt) 中断信号,如 ctrl-C,通常由用户生成。 |
SIGSEGV | (Signal Segmentation Violation) 非法访问存储器,如访问不存在的内存单元。 |
SIGTERM | (Signal Terminate) 发送给本程序的终止请求信号。 |
- func – 一个指向函数的指针。它可以是一个由程序定义的函数,也可以是下面预定义函数之一:
SIG_DFL | 默认的信号处理程序。 |
---|---|
SIG_IGN | 忽视信号。 |
同步(Sync)
异步(Async)效率高
查看 Linux 系统支持的所有信号:kill -l
命令
从-34号信号开始,信号没有固定意义,可以手动设置,并且是可靠的(有信号的处理时,来了信号,则进入队列,取队头进行处理)、实时的,前面的信号是不可靠(同上,没有队列,直接执行新的信号,原信号消失)、不实时的
信号的处理方式:
- 默认处理(通常就是结束进程)
- 忽略
- 自定义处理(即给定某个信号指定一个信号处理函数,如果在某个时刻该进程接收到这个信号,进程就会暂停主程序的执行,转去调用信号处理函数,信号处理函数返回后再继续执行主程序)
信号产生的来源:
- 自己或其他进程(raise 函数、kill 函数)
- 系统内核(当用户按键或发生某些事件时,系统内核可能也会给对应的进程发送信号)
//当除数为0时,系统会发SIGFPE信号
#include <stdio.h>
#include <>
int main()
{
//signal(SIGINT, SIG_IGN);//忽略SIGINT信号
//signal(SIGHUP, SIG_IGN);//忽略SIGHUP信号
//忽略全部信号,除9号,9号信息无法被屏蔽,默认处理
//-9 为SIGKILL 信号不能被忽略,也不能自定义处理,任何进程接收到该信号后默认行就是强制结束。该信号给系统管理员提高了一种结束进程的终极解决方案。(-15信号使进程正常退出)
for(int i = 1; i < n)
{
signal(i,SIG_IGN);
}
while(1)
{
printf("hellon");
sleep(1);
}return 0;
}
向此程序法信号,首先得知道此程序的PID,利用kill()//向某个进程发送一个信号。
命令行-》kill -2 PID (结束)// -2为-SIGINT,也就是ctrl+c ctrl+z 为SIGTSTP
命令行-》kill -1 PID (挂起)// -1为-SIGHUP
// -19 -SIGSTOP (暂停) 不能被忽略,也不能自定义处理
//若要继续运行则使用 -18 -SIGCONT (继续)
//从-34号信号开始,信号没有固定意义,可以手动设置
//SIGSEGV为段错误处理
命令行-》kill -3 `ps -e | grep signal_demo | awk `{print $1}``
//后面表示利用ps命令获取此进程的信息,再利用awk获取第一列——PID
相关的系统调用:
- kill:向指定进程发送某个信号
- raise:向当前进程发送某个信号,等效于:
kill(getpid(), 信号编号)
- alarm:设置闹钟,注意:每个进程最多只能拥有一个闹钟。
- pause:挂起当前进程,直到接收到一个信号。
- abort:终止当前进程。(raise(SIGABRT))
- sleep:挂起当前进程,直到超时或接收到一个信号。返回值为剩余时间,若执行完则返回 0,中途终止则返回剩余的时间。
避免僵尸进程产生的另外一种常用办法:忽略 SIGCHLD 信号或者在 SIGCHLD 信号的处理函数中调用 wait/waitpid 函数。
SIGCHLD 信号:在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程。按系统默认将忽略此信号。如果父进程希望被告知其子进程的这种状态,则应捕捉此信号。信号的捕捉函数中通常调用wait函数以取得进程ID和其终止状态。
手动实现kill命令
//手动实现kill命令
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc,char** argv)
{
int signum;
int pid;
if(argv[1][0] == '-')
//取信号的数值
signum = atoi(argv[1] + 1);//atoi将字符串转换为整形,不能强转,直接强转就是将字符地址强转成了整形
else
signum = atoi(argv[1]);
//取进程编号
pid = atoi(argv[2]);
return kill(pid, signum);//成功返回0,失败返回-1
}
执行完将可执行文件移动到/user/bin/下则可直接当命令执行
手动实现raise命令
//手动实现raise命令
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
// void signal_handler(int signum)
// {
// printf("SIGINTn");
// }
void my_sleep(int seconds);
void signal_handler(int signum)
{
// printf("hellon");
// alarm(3);
}
int main()
{
// signal(SIGINT, signal_handler);
// raise(SIGINT);
// signal(SIGALRM, signal_handler);
signal(SIGABRT, signal_handler);
// alarm(3);
// while(1) pause();
// printf("start...n");
// my_sleep(5);
printf("start...n");
abort(); // raise(SIGABRT);
printf("end...n");
return 0;
}
void my_sleep(int seconds)
{
alarm(seconds);
pause();
}
手动实现alarm命令
//手动实现alarm命令
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
void mt_sleep(int seconds)
{
alarm(seconds);
pause();
}
int main()
{
signal(SIGALRM, SIG_IGM);
printf("start...");
my_sleep(5);
printf("end...");
return 0;
}
文件拷贝程序(多进程控制)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
int is_start = 0;
int is_end = 0;
int copy_result = 0;
void signal_handler(int signum);
void copy_file(const char* src_file, const char* dst_file);
void ui(pid_t child);
int main(int argc, char** argv)
{
if(argc != 3)
{
fprintf(stderr, "命令用法错误!n");
return 1;
}
if(-1 == access(argv[1], R_OK))
{
fprintf(stderr, "待拷贝的文件不存在或不可读!n");
return 1;
}
if(0 == access(argv[2], F_OK))
{
printf("目标文件 %s 已存在,是否覆盖?(Y/n)", argv[2]);
char choice = getchar();
if(choice == 'Y' || choice == 'y')
{
while(getchar() != 'n');
}
else
return 1;
}
pid_t pid = fork();
if(pid == -1)
{
perror("fork");
return 1;
}
if(pid == 0)
{
// 子进程负责拷贝文件任务
signal(SIGTERM, signal_handler);
signal(SIGRTMIN + 1, signal_handler);
signal(SIGRTMIN + 2, signal_handler);
copy_file(argv[1], argv[2]);
}
else
{
// 父进程负责 UI 处理任务
signal(SIGCHLD, signal_handler);
signal(SIGRTMIN + 3, signal_handler);
ui(pid);
}
return 0;
}
void signal_handler(int signum)
{
if(signum == SIGTERM)
{
is_end = 1;
}
else if(signum == SIGCHLD)
{
is_end = 1;
}
else if(signum == SIGRTMIN + 1)
{
is_start = 1;
}
else if(signum == SIGRTMIN + 2)
{
is_start = 0;
}
else if(signum == SIGRTMIN + 3)
{
copy_result = 1;
}
}
void copy_file(const char* src_file, const char* dst_file)
{
int fd_src, fd_dst;
unsigned int ret, write_cnt = 0;
char buf[1024];
struct stat st;
fd_src = open(src_file, O_RDONLY);
fd_dst = open(dst_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
fstat(fd_src, &st);
while((ret = read(fd_src, buf, sizeof(buf))) > 0)
{
while(!is_end && !is_start) pause();
if(is_end)
{
unlink(dst_file);
break;
}
ret = write(fd_dst, buf, ret);
if(ret == -1) break;
write_cnt += ret;
}
close(fd_src);
close(fd_dst);
if(!is_end)
{
if(write_cnt == st.st_size)
kill(getppid(), SIGRTMIN + 3);
}
}
void ui(pid_t child)
{
int op;
printf("欢迎使用本拷贝工具!nn");
printf("使用帮助:n");
printf("开始或继续拷贝请输入 1n");
printf("暂停拷贝请输入 2n");
printf("查看拷贝状态请输入 3n");
printf("退出程序请输入 0n");
while(1)
{
printf("n:> ");
if(1 != scanf("%d", &op))
{
op = -1;
while(getchar() != 'n');
}
switch(op)
{
case 0:
{
kill(child, SIGTERM);
printf("n感谢您的使用,下次再见!n");
exit(0);
}
break;
case 1:
{
kill(child, SIGRTMIN + 1);
is_start = 1;
}
break;
case 2:
{
kill(child, SIGRTMIN + 2);
is_start = 0;
}
break;
case 3:
{
if(is_end)
{
if(copy_result)
printf("拷贝已经成功完成!n");
else
printf("拷贝失败!n");
}
else
{
if(is_start)
printf("拷贝正在进行,可以输入 2 暂停拷贝!n");
else
printf("拷贝已经暂停,可以输入 1 继续拷贝!n");
}
}
break;
default:
{
printf("n输入错误,请重新输入!n");
}
}
}
}
最后
以上就是懦弱板凳为你收集整理的Linux高级编程——进程通信(信号signal)的全部内容,希望文章能够帮你解决Linux高级编程——进程通信(信号signal)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复