回顧:
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();
}
}


