posts - 200, comments - 8, trackbacks - 0, articles - 0
回顾:
  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();
    }
    
}

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理