c++ 学习笔记(高级linux编程) day7

来源:互联网 发布:淘宝自定义代码 编辑:程序博客网 时间:2024/06/01 09:40

linux高级编程day07 笔记

回顾:
  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.思考:信号中断函数调用中是否被其他信号中断.          
  信号函数调用中只屏蔽本身信号,不屏蔽其他信号.
2.怎么保证函数调用中屏蔽指定的信号呢?
  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();

    }

   

}       

0 0
原创粉丝点击