两个或多个进程间进行数据传送,参与通信的这些进程可能位于同一台机器上,也可能位于不同的机器上。
常用 IPC 方式:
Linux 系统上古老经典的 IPC方式,主要用于实现异步通知,不能传输数据。
信号是一种软件中断(Software Break)
void (*signal(int sig, void (*func)(int)))(int)
宏 | 信号 |
---|---|
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) 发送给本程序的终止请求信号。 |
SIG_DFL | 默认的信号处理程序。 |
---|---|
SIG_IGN | 忽视信号。 |
同步(Sync)
异步(Async)效率高
查看 Linux 系统支持的所有信号:kill -l
命令
从-34号信号开始,信号没有固定意义,可以手动设置,并且是可靠的(有信号的处理时,来了信号,则进入队列,取队头进行处理)、实时的,前面的信号是不可靠(同上,没有队列,直接执行新的信号,原信号消失)、不实时的
信号的处理方式:
信号产生的来源:
//当除数为0时,系统会发SIGFPE信号
#include
#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("hello\n");
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(getpid(), 信号编号)
避免僵尸进程产生的另外一种常用办法:忽略 SIGCHLD 信号或者在 SIGCHLD 信号的处理函数中调用 wait/waitpid 函数。
SIGCHLD 信号:在一个进程终止或者停止时,将SIGCHLD信号发送给其父进程。按系统默认将忽略此信号。如果父进程希望被告知其子进程的这种状态,则应捕捉此信号。信号的捕捉函数中通常调用wait函数以取得进程ID和其终止状态。
//手动实现kill命令
#include
#include
#include
#include
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命令
#include
#include
#include
#include
#include
// void signal_handler(int signum)
// {
// printf("SIGINT\n");
// }
void my_sleep(int seconds);
void signal_handler(int signum)
{
// printf("hello\n");
// 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命令
#include
#include
#include
#include
#include
void mt_sleep(int seconds)
{
alarm(seconds);
pause();
}
int main()
{
signal(SIGALRM, SIG_IGM);
printf("start...");
my_sleep(5);
printf("end...");
return 0;
}
#include
#include
#include
#include
#include
#include
#include
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("欢迎使用本拷贝工具!\n\n");
printf("使用帮助:\n");
printf("开始或继续拷贝请输入 1\n");
printf("暂停拷贝请输入 2\n");
printf("查看拷贝状态请输入 3\n");
printf("退出程序请输入 0\n");
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");
}
}
}
}