1. 程式人生 > >linux高階程式設計day07 筆記 (轉)

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.思考:訊號中斷函式呼叫中是否被其他訊號中斷.          
訊號函式呼叫中只遮蔽本身訊號,不遮蔽其他訊號.(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();
    }
    
}