回顧: 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 <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.增強功能 : 傳參數 int sigaction(
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**//保留成員.
}
#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);
}
}
}
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();
}
}