回顾:
1.信号的作用
2.理解信号:
软中断
可靠与不可靠信号kill -l
3.信号发送与注册kill/raise alarm setitimer signal
4.信号的屏蔽sigprocmask sigemptyset sigfillset ...
5.信号屏蔽的切换
sigpending
sigsuspend
=pause+
指定屏蔽信号
pause与sigsuspend都回被信号中断.
中断的是pause与sigsuspen,不是进程中其他代码
sigsuspend放在sigprocmask环境中思考:
5.1.sigsuspend是否影响sigprocmask屏蔽的信号呢?
影响.使原来的屏蔽信号全部失效.
当sigsuspend返回,恢复原来的屏蔽信号.
5.2.sigsuspend什么时候使用?
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void handle( int s)
{
printf("外部用户中断处理 !\n");
sleep(3);
printf("外部用户中断处理完毕!\n");
}
main()
{
int sum=0;
int i;
sigset_t sigs,sigt,sigu;
sigemptyset(&sigs);
sigemptyset(&sigt);
sigemptyset(&sigu);
sigaddset(&sigs,SIGINT);
// sigfillset(&sigs);
signal(SIGINT,handle);
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=0;i<10;i++)
{
printf("正在拷贝电影<%d>!\n",i);
sleep(5); // 模拟业务处理时间比较长
printf("正在拷贝电影<%d>完毕!\n",i);
sigpending(&sigu);
if(sigismember(&sigu,SIGINT))
{
sigsuspend(&sigt);
}
}
printf("所有电影拷贝完毕\n",sum);
sigprocmask(SIG_UNBLOCK,&sigs,0);
printf("over!\n");
}
一.最新版本的信号发送与处理
sigqueue/sigaction
1.思考:信号中断函数调用中是否被其他信号中断.
信号函数调用中只屏蔽本身信号,不屏蔽其他信号.(signal)
因为屏蔽本身,所以这过程中的信号打来依然会排队。(不可靠会压缩)
2.怎么保证函数调用中屏蔽指定的信号呢?
sigaction可以指定处理函数调用的屏蔽信号,所以signal是用sigaction来实现屏蔽本身信号的。(sigaction不会屏蔽本身除非你主动设置)
sigaction在处理信号的时候,接受数据.
sigqueue发送信号的时候,可以发送数据.
sigaction/sigqueue是signal/kill的增强版本
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
main()
{
union sigval val;
val.sival_int=8888;
sigqueue(3972,SIGUSR1,val);
}
3.函数说明
使用sigaction/sigqueue有两个理由.
3.1.稳定
3.2.增强功能 : 传参数
0:成功
-1:失败
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
/*
void handle(int s)
{
printf("OOOK!\n");
sleep(5);
printf("K000!\n");
} */
void handle( int s,siginfo_t* info, void *d)
{
printf("OOOK:%d\n",info->si_int);
sleep(5);
printf("K000!\n");
}
main()
{
struct sigaction act={0};
// act.sa_handler=handle;
act.sa_sigaction=handle;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGINT);
act.sa_flags=SA_SIGINFO;
sigaction(SIGUSR1,&act,0);
while(1);
}
案例:
1.使用sigaction处理信号,使用kill发送信号
2.使用sigaction处理信号,使用sigqueue发送信号
3.发送信号的同时处理数据
二.IPC
1.基于文件
1.1.无序文件
1.1.有序文件
1.1.1.管道
1.1.1.1.有名
1.1.1.2.匿名
1.1.2.socket
2.基于内存
2.1.无序内存
2.1.1.匿名内存
2.1.2.共享内存
2.2.有序内存
2.2.1.共享队列
3.同步:基于内存IPC应用(共享内存数组)
信号量/信号灯
三.基于普通文件的IPC
IPC的技术提出的应用背景.
进程之间需要同步处理:
同步需要通信.
普通文件就是最基本的通信手段.
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
main()
{
int *p;
int fd;
int i;
fd=open("tmp",O_RDWR|O_CREAT,0666);
ftruncate(fd,4);
p=mmap(0,4,PROT_READ|PROT_WRITE,
MAP_SHARED,fd,0);
i=0;
while(1)
{
sleep(1);
*p=i;
i++;
}
close(fd);
}
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
main()
{
int *p;
int fd;
fd=open("tmp",O_RDWR);
p=mmap(0,4,PROT_READ|PROT_WRITE,
MAP_SHARED,fd,0);
while(1)
{
sleep(1);
printf("%d\n",*p);
}
close(fd);
}
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
int fd;
int i;
void end( int s)
{
// 关闭管道
close(fd);
// 删除管道
unlink("my.pipe");
exit(-1);
}
main()
{
signal(SIGINT,end);
// 建立管道
mkfifo("my.pipe",0666);
// 打开管道
fd=open("my.pipe",O_RDWR);
// shutdown(fd,SHUT_RD);
i=0;
while(1)
{
// 每隔1秒写数据
sleep(1);
write(fd,&i,4);
i++;
}
}
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
int fd;
void end( int s)
{
// 关闭管道
close(fd);
exit(-1);
}
main()
{
int i;
// 打开管道
signal(SIGINT,end);
fd=open("my.pipe",O_RDWR);
// shutdown(fd,SHUT_WR);
while(1)
{
read(fd,&i,4);
printf("%d\n",i);
}
}
总结:
1.read没有数据read阻塞,而且read后数据是被删除
2.数据有序
3.打开的描述符号可以读写(two-way双工)
4.管道文件关闭后,数据不持久.
5.管道的数据存储在内核缓冲中.
五.匿名管道
发现有名的管道的名字仅仅是内核识别是否返回同一个fd的标示.
所以当管道名失去表示作用的时候,实际可以不要名字.
在父子进程之间:打开文件描述后创建进程.
父子进程都有描述符号. 管道文件没有价值.
所以在父子进程中引入一个没有名字的管道:匿名管道.
结论:
匿名管道只能使用在父子进程.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
main()
{
int fd[2];
pipe(fd);
if(fork())
{ // parent
close(fd[0]); // 只负责写
while(1)
{
write(fd[1],"Hello",5);
sleep(1);
}
}
else
{ // child
char buf[20];
int r;
close(fd[1]); // 只负责读
while(1)
{
r=read(fd[0],buf,20);
buf[r]=0;
printf("::%s\n",buf);
}
}
}
1.创建匿名管道
2.使用匿名管道
案例:
匿名管道的创建
体会匿名管道的特点
int pipe(int fd[2]);//创建管道.打开管道.拷贝管道.关闭读写
fd[0]:只读(不能写)
fd[1]:只写(不能读)
注意:数据无边界.
综合:
建立两个子进程:
一个负责计算1-5000的素数
另外一个负责计算5001-10000
父进程负责存储
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include < string.h>
#include <sched.h>
int idx=0;
int fddata;
void handle( int s)
{
int status;
if(s==SIGCHLD)
{
wait(&status);
idx++;
if(idx==2)
{
close(fddata);
printf("任务完成\n");
exit(-1);
}
}
}
int isprimer( int ta)
{
int i=2;
for(;i<ta;i++)
{
if(ta%i==0)
{
return 0;
}
}
return 1;
}
main()
{
int a,b;
int id=1;
int fd[2];
signal(SIGCHLD,handle);
pipe(fd);
while(1)
{
if(id==1){
a=2;b=50000;
}
if(id==2){
a=50001;b=100000;
}
if(fork()){
id++;
if(id>2){
break;
}
continue;
}
else{
// 子进程
int i;
close(fd[0]);
for(i=a;i<=b;i++)
{
if(isprimer(i))
{
write(fd[1],&i, sizeof( int));
}
sched_yield();
}
printf("%d任务完成!\n",getpid());
exit(0);
}
}
int re;
char buf[20];
// 打开文件,准备存储
close(fd[1]);
fddata=open("result.txt",
O_RDWR|O_CREAT,0666);
while(1)
{
read(fd[0],&re, sizeof( int));
sprintf(buf,"%d\n",re);
write(fddata,buf,strlen(buf));
sched_yield();
}
}
1.信号的作用
2.理解信号:
软中断
可靠与不可靠信号kill -l
3.信号发送与注册kill/raise alarm setitimer signal
4.信号的屏蔽sigprocmask sigemptyset sigfillset ...
5.信号屏蔽的切换
sigpending
sigsuspend
=pause+
指定屏蔽信号
pause与sigsuspend都回被信号中断.
中断的是pause与sigsuspen,不是进程中其他代码
sigsuspend放在sigprocmask环境中思考:
5.1.sigsuspend是否影响sigprocmask屏蔽的信号呢?
影响.使原来的屏蔽信号全部失效.
当sigsuspend返回,恢复原来的屏蔽信号.
5.2.sigsuspend什么时候使用?
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void handle( int s)
{
printf("信号干扰!\n");
}
main()
{
int sum=0;
int i;
sigset_t sigs,sigt;
sigemptyset(&sigs);
sigemptyset(&sigt);
sigaddset(&sigs,SIGINT);
// sigfillset(&sigs);
signal(SIGINT,handle);
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=0;i<10;i++)
{
sum+=i;
sleep(5); // 模拟业务处理时间比较长
sigsuspend(&sigt);
sleep(5);
}
printf("%d\n",sum);
sigprocmask(SIG_UNBLOCK,&sigs,0);
printf("over!\n");
}
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void handle( int s)
{
printf("信号干扰!\n");
}
main()
{
int sum=0;
int i;
sigset_t sigs,sigt;
sigemptyset(&sigs);
sigemptyset(&sigt);
sigaddset(&sigs,SIGINT);
// sigfillset(&sigs);
signal(SIGINT,handle);
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=0;i<10;i++)
{
sum+=i;
sleep(5); // 模拟业务处理时间比较长
sigsuspend(&sigt);
sleep(5);
}
printf("%d\n",sum);
sigprocmask(SIG_UNBLOCK,&sigs,0);
printf("over!\n");
}
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void handle( int s)
{
printf("外部用户中断处理 !\n");
sleep(3);
printf("外部用户中断处理完毕!\n");
}
main()
{
int sum=0;
int i;
sigset_t sigs,sigt,sigu;
sigemptyset(&sigs);
sigemptyset(&sigt);
sigemptyset(&sigu);
sigaddset(&sigs,SIGINT);
// sigfillset(&sigs);
signal(SIGINT,handle);
sigprocmask(SIG_BLOCK,&sigs,0);
for(i=0;i<10;i++)
{
printf("正在拷贝电影<%d>!\n",i);
sleep(5); // 模拟业务处理时间比较长
printf("正在拷贝电影<%d>完毕!\n",i);
sigpending(&sigu);
if(sigismember(&sigu,SIGINT))
{
sigsuspend(&sigt);
}
}
printf("所有电影拷贝完毕\n",sum);
sigprocmask(SIG_UNBLOCK,&sigs,0);
printf("over!\n");
}
sigqueue/sigaction
1.思考:信号中断函数调用中是否被其他信号中断.
信号函数调用中只屏蔽本身信号,不屏蔽其他信号.(signal)
因为屏蔽本身,所以这过程中的信号打来依然会排队。(不可靠会压缩)
2.怎么保证函数调用中屏蔽指定的信号呢?
sigaction可以指定处理函数调用的屏蔽信号,所以signal是用sigaction来实现屏蔽本身信号的。(sigaction不会屏蔽本身除非你主动设置)
sigaction在处理信号的时候,接受数据.
sigqueue发送信号的时候,可以发送数据.
sigaction/sigqueue是signal/kill的增强版本
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
main()
{
union sigval val;
val.sival_int=8888;
sigqueue(3972,SIGUSR1,val);
}
使用sigaction/sigqueue有两个理由.
3.1.稳定
3.2.增强功能 : 传参数
int sigaction(
int sig, // 被处理信号
const struct sigaction*action, // 处理函数及其参数
struct sigaction*oldact // 返回原来的处理函数结构体
)
返回:
int sig, // 被处理信号
const struct sigaction*action, // 处理函数及其参数
struct sigaction*oldact // 返回原来的处理函数结构体
)
0:成功
-1:失败
struct sigaction
{
void (*sa_handle)( int);
void (*sa_sigaction)( int,siginfo_t*, void*);
sigset_t *mask; // 屏蔽信号
int flags; // SA_SIGINFO
void** // 保留成员.
}
{
void (*sa_handle)( int);
void (*sa_sigaction)( int,siginfo_t*, void*);
sigset_t *mask; // 屏蔽信号
int flags; // SA_SIGINFO
void** // 保留成员.
}
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
/*
void handle(int s)
{
printf("OOOK!\n");
sleep(5);
printf("K000!\n");
} */
void handle( int s,siginfo_t* info, void *d)
{
printf("OOOK:%d\n",info->si_int);
sleep(5);
printf("K000!\n");
}
main()
{
struct sigaction act={0};
// act.sa_handler=handle;
act.sa_sigaction=handle;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGINT);
act.sa_flags=SA_SIGINFO;
sigaction(SIGUSR1,&act,0);
while(1);
}
1.使用sigaction处理信号,使用kill发送信号
2.使用sigaction处理信号,使用sigqueue发送信号
3.发送信号的同时处理数据
二.IPC
1.基于文件
1.1.无序文件
1.1.有序文件
1.1.1.管道
1.1.1.1.有名
1.1.1.2.匿名
1.1.2.socket
2.基于内存
2.1.无序内存
2.1.1.匿名内存
2.1.2.共享内存
2.2.有序内存
2.2.1.共享队列
3.同步:基于内存IPC应用(共享内存数组)
信号量/信号灯
三.基于普通文件的IPC
IPC的技术提出的应用背景.
进程之间需要同步处理:
同步需要通信.
普通文件就是最基本的通信手段.
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
main()
{
int *p;
int fd;
int i;
fd=open("tmp",O_RDWR|O_CREAT,0666);
ftruncate(fd,4);
p=mmap(0,4,PROT_READ|PROT_WRITE,
MAP_SHARED,fd,0);
i=0;
while(1)
{
sleep(1);
*p=i;
i++;
}
close(fd);
}
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
main()
{
int *p;
int fd;
fd=open("tmp",O_RDWR);
p=mmap(0,4,PROT_READ|PROT_WRITE,
MAP_SHARED,fd,0);
while(1)
{
sleep(1);
printf("%d\n",*p);
}
close(fd);
}
普通文件IPC技术的问题:
一个进程改变文件,另外一个进程无法感知.
解决方案:
一个特殊的文件:管道文件
四.管道文件
1.创建管道mkfifo
2.体会管道文件特点
案例:
fifoA fifoB
建立管道
打开管道 打开管道
写数据 读数据
关闭管道 关闭管道
删除管道
建立管道文件:
使用linux的指令mkfifo
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
int fd;
int i;
void end( int s)
{
// 关闭管道
close(fd);
// 删除管道
unlink("my.pipe");
exit(-1);
}
main()
{
signal(SIGINT,end);
// 建立管道
mkfifo("my.pipe",0666);
// 打开管道
fd=open("my.pipe",O_RDWR);
// shutdown(fd,SHUT_RD);
i=0;
while(1)
{
// 每隔1秒写数据
sleep(1);
write(fd,&i,4);
i++;
}
}
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
int fd;
void end( int s)
{
// 关闭管道
close(fd);
exit(-1);
}
main()
{
int i;
// 打开管道
signal(SIGINT,end);
fd=open("my.pipe",O_RDWR);
// shutdown(fd,SHUT_WR);
while(1)
{
read(fd,&i,4);
printf("%d\n",i);
}
}
1.read没有数据read阻塞,而且read后数据是被删除
2.数据有序
3.打开的描述符号可以读写(two-way双工)
4.管道文件关闭后,数据不持久.
5.管道的数据存储在内核缓冲中.
五.匿名管道
发现有名的管道的名字仅仅是内核识别是否返回同一个fd的标示.
所以当管道名失去表示作用的时候,实际可以不要名字.
在父子进程之间:打开文件描述后创建进程.
父子进程都有描述符号. 管道文件没有价值.
所以在父子进程中引入一个没有名字的管道:匿名管道.
结论:
匿名管道只能使用在父子进程.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
main()
{
int fd[2];
pipe(fd);
if(fork())
{ // parent
close(fd[0]); // 只负责写
while(1)
{
write(fd[1],"Hello",5);
sleep(1);
}
}
else
{ // child
char buf[20];
int r;
close(fd[1]); // 只负责读
while(1)
{
r=read(fd[0],buf,20);
buf[r]=0;
printf("::%s\n",buf);
}
}
}
2.使用匿名管道
案例:
匿名管道的创建
体会匿名管道的特点
int pipe(int fd[2]);//创建管道.打开管道.拷贝管道.关闭读写
fd[0]:只读(不能写)
fd[1]:只写(不能读)
注意:数据无边界.
综合:
建立两个子进程:
一个负责计算1-5000的素数
另外一个负责计算5001-10000
父进程负责存储
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include < string.h>
#include <sched.h>
int idx=0;
int fddata;
void handle( int s)
{
int status;
if(s==SIGCHLD)
{
wait(&status);
idx++;
if(idx==2)
{
close(fddata);
printf("任务完成\n");
exit(-1);
}
}
}
int isprimer( int ta)
{
int i=2;
for(;i<ta;i++)
{
if(ta%i==0)
{
return 0;
}
}
return 1;
}
main()
{
int a,b;
int id=1;
int fd[2];
signal(SIGCHLD,handle);
pipe(fd);
while(1)
{
if(id==1){
a=2;b=50000;
}
if(id==2){
a=50001;b=100000;
}
if(fork()){
id++;
if(id>2){
break;
}
continue;
}
else{
// 子进程
int i;
close(fd[0]);
for(i=a;i<=b;i++)
{
if(isprimer(i))
{
write(fd[1],&i, sizeof( int));
}
sched_yield();
}
printf("%d任务完成!\n",getpid());
exit(0);
}
}
int re;
char buf[20];
// 打开文件,准备存储
close(fd[1]);
fddata=open("result.txt",
O_RDWR|O_CREAT,0666);
while(1)
{
read(fd[0],&re, sizeof( int));
sprintf(buf,"%d\n",re);
write(fddata,buf,strlen(buf));
sched_yield();
}
}