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

linux高階程式設計day06 筆記(轉)

問題解答:
  1.exit(狀態碼)返回狀態碼有什麼意義?
   返回值被系統得到.系統根據狀態碼進行日誌記錄.
   返回值被呼叫者得到:system/wait.程式會根據返回狀態碼進行對應處理。
   exit(狀態碼)=main函式中的return 狀態碼; 
  2.狀態碼的第二個位元組才是exit()的返回值或者return值。

一.程序的基本控制
 1.程序的常見控制函式
   1.1.為什麼需要控制程序?
   1.2.pause/sleep/usleep
   1.3.atexit  on_exit 


#include <stdio.h>
#include <stdlib.h> 
void
 fun()
{
    printf("over\n");
}
main()
{
    atexit(fun); //註冊終止函式(即main執行結束後呼叫的函式)     printf("Process!\n");
}2.程序與檔案鎖
   在多程序下檔案讀寫是共享的
   問題:
     怎麼知道一個檔案正在被另外程序讀寫?
   解決方案:
     檔案鎖。(建議鎖)
   API:
     fcntl(檔案鎖受核心引數影響)  
   程式設計技巧:
     對檔案加鎖
     判定一個檔案是否存在鎖
   函式說明:
     int fcntl(
       int fd,//被加鎖的檔案描述符號
       int cmd,//鎖的操作方式:F_SETLK(已經加鎖,異常返回) F_UNLK F_SETLKW(已經加鎖,則阻塞等待)

       struct flock *lk);//鎖的描述

     返回值:
       0:加鎖成功
       -1:加鎖失敗
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
main()
{
    int fd;
    struct flock lk;
    int r;
    //開啟一個檔案    fd=open("a.txt",O_RDWR);
    if(fd==-1) printf(":%m\n"),exit(-1);
    //描述鎖    lk.l_type=F_WRLCK;
    lk.l_whence=SEEK_SET;
    lk.l_start=5;
    lk.l_len=10;
    //
加鎖    r=fcntl(fd,F_SETLK,&lk);
    if(r==0) printf("加鎖成功!\n");
    else    printf("加鎖失敗!\n");
    while(1);    
}案例:
   寫兩個程式:
      A:加鎖
      B:獲取鎖的資訊#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
main()
{
    int fd;
    struct flock lk;
    int r;
    //開啟一個檔案    fd=open("a.txt",O_RDWR);
    if(fd==-1) printf(":%m\n"),exit(-1);
    //描述鎖    lk.l_type=F_WRLCK;
    lk.l_whence=SEEK_SET;
    lk.l_start=5;
    lk.l_len=3;
    //加鎖    r=fcntl(fd,F_SETLK,&lk);
    if(r==0) printf("加鎖成功!\n");
    else    printf("加鎖失敗!\n");
    while(1);    
}#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

main()
{
    int fd;
    struct flock lk={0};
    int r;
    
    fd=open("a.txt",O_RDWR);
    if(fd==-1) printf("::%m\n"),exit(-1);
    
    r=fcntl(fd,F_GETLK,&lk);
    if(r==0) 
        printf("得到鎖成功!\n");
    if(lk.l_type==F_WRLCK)
    {
        printf("寫鎖!\n");
    }
    printf("start:%d,len:%d\n",lk.l_start,lk.l_len);
    
    printf("PID:%d\n",lk.l_pid);
    
}鎖也是一個程序可以共享的資訊。

二.訊號
 1.訊號的作用
   背景:
     程序之間通訊比較麻煩。
     但程序之間有必須通訊,比如父子程序之間。
   作用:
     通知其他程序響應。程序之間通訊機制.
     訊號:
      接受訊號的程序馬上停止.呼叫訊號處理函式.
     訊號處理函式:
      預設處理函式.
        列印訊號資訊,退出程序.
      使用者處理函式.       
   中斷:
     軟中斷.

案例:
  1.程序之中,預設訊號處理
  2.程序之中,使用者訊號處理
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void handle(int s)
{
    printf("我是訊號發生!\n");    
}

main()
{
    //signal(SIGWINCH,handle);    signal(35,handle);
    while(1)
    {
        //printf("程序在執行:%d!\n",getpid());
        
//sleep(1);    }
}3.中斷

  kill -s 訊號  程序ID
  kill -訊號  程序ID     
  訊號:數字1-31  34-64
     巨集SIGINT=2
  ctrl+d 傳送訊號2 SIGINT
  kill -l察看所有訊號    

  訊號SIGKILL SIGSTOP不能被處理.


案例:
  傳送訊號
  int kill(pid_t pid,int s)
  程序ID:
    >0:傳送訊號到指定程序
    =0:傳送訊號到該程序所在程序組的所有程序
    -1:傳送給所有程序,除init外
    <0:傳送給指定的程序組(組ID=絕對值)
#include <stdio.h>
#include <signal.h>
main()
{
    int i;
    //while(1)    for(i=0;i<5;i++)
    {
        kill(4601,35);
        
    }
}2.訊號傳送與安裝
   signal
   kill
 3.訊號的應用
   3.1.延時器timeout
     SIGALRM
     訊號發出函式:alarm
   3.2.定時器
int setitimer(int which,//計時方式
                        
//ITIMER_REAL 牆上時間,時鐘時間
                       // ITIMER_VIRTUAL 系統空間時間+使用者空間時間
                       //ITIMER_PROF ITIMER_VIRTUAL+休眠時間
            const struct itimerval *val,//定時器的時間引數            struct itimer *oldval);//返回原來設定的定時器
                                            
//如果=NULL,則不返回struct itimerval
{
        struct timeval it_interval;//間隔時間:定時器週期        struct timeval it_value;//延時時間:過多久定時器開始生效}struct timeval
{
     long tv_sec;//秒
     long tv_usec;//微妙
}
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
void deal(int s)
{
    printf("起床!\n");
    
}
main()
{
    struct itimerval v={0};
    
    signal(SIGALRM,deal);
    
    //v.it_value.tv_sec=3; //程式啟動3秒後觸發,可以不設定tv_usec    v.it_value.tv_sec=0;
    v.it_value.tv_usec=1;  //讓程式一啟動就觸發。不能設為0    v.it_interval.tv_sec=1; //間隔1秒
    
//alarm(5);    setitimer(ITIMER_REAL,&v,0);
    while(1)
    {
        //..    }
}訊號應用:
     系統與應用程式之間
     應用於應用程式之間
     父子程序之間 
  案例1:
     使用定時器訊號,實現多工.
     實現:
       實現7位隨機數
       使用中斷實現時間的顯示
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <math.h>
#include <curses.h>
WINDOW *w;
int num;
int isstop=0;
void handle(int s)
{
    if(s==SIGUSR1)
    {
        if(isstop==1)
            isstop=0;
        else
            isstop=1;
    }
}

main()
{
    initscr();
    curs_set(0);//隱藏游標    noecho();//禁止輸入回顯
    
//keypad(stdscr,TRUE);
    
//keypad(w,TRUE);
    
//建立子窗體    w=derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2);
    box(w,0,0);//給子窗體加邊框    refresh();
    wrefresh(w);
    if(fork())
    {
        //顯示7位數的隨機數        signal(SIGUSR1,handle);
        while(1){
            if(isstop==1)
            {
                pause();//pause會被訊號中斷停止 ***            }
            num=rand()%10000000;//產生7位隨機數            mvwprintw(w,1,2,"%07d",num);//顯示隨機數            refresh();//重新整理螢幕。            wrefresh(w);
            usleep(10000);//暫停10毫秒        }
    }
    else
    {
        //處理按鍵        while(1)
        {
            getch();
            //if(ch==KEY_ENTER)            {
                kill(getppid(),SIGUSR1);
            }
        }
    }
    
    endwin();
} 案例2:
     實現父子程序之間通訊

     控制程序.

   sleep與pause函式被訊號影響後,sleep不再繼續sleep.
   pause不再pause.
練習:
   1.在curses顯示7位隨機數     

   其他訊號函式
     raise

 4.訊號的可靠與不可靠以及訊號的含義
   訊號有丟失.(訊號不排隊,壓縮成一個)
   由於歷史的緣故:訊號有壓縮的需求.
   可靠訊號(實時訊號)與不可靠訊號(非實時訊號).

   早期訊號 1-31 31個訊號, 不可靠(與系統有關).
   後期訊號34-64 31個訊號,可靠訊號(使用者訊號)  
   
   這就是"不可靠訊號"的來源。它的主要問題是:
  • 程序每次處理訊號後,就將對訊號的響應設定為預設動作。在某些情況下,將導致對訊號的錯誤處理;因此,使用者如果不希望這樣的操作,那麼就要在訊號處理函式結尾再一次呼叫signal(),重新安裝該訊號。
  • 訊號可能丟失,後面將對此詳細闡述。
    因此,早期unix下的不可靠訊號主要指的是程序可能對訊號做出錯誤的反應以及訊號可能丟失。

Linux支援不可靠訊號,但是對不可靠訊號機制做了改進:在呼叫完訊號處理函式後,不必重新呼叫該訊號的安裝函式(訊號安裝函式是在可靠機制上的實現)。因此,Linux下的不可靠訊號問題主要指的是訊號可能丟失。


 5.訊號的操作
   訊號導致的問題
   1.訊號遮蔽 
int sigprocmask(int how,//操作方式                SIG_BLOCK
                SIG_UNBLOCK
                SIG_SETMASK
    const sigset_t *sigs,//操作的訊號集合    sigset_t *oldsigs);//返回原來操作的訊號集合  1.宣告訊號集合
       sigset_t  sigs;
      2.加入遮蔽訊號
        一組訊號集合維護函式:
        2.1. 清空集合sigemptyset
        2.2. 新增訊號到集合sigaddset
        2.3. 從集合刪除訊號sigdelset
        2.4. 新增所有訊號到集合sigfillset
        2.5. 判定訊號是否在集合sigismember
     3.遮蔽訊號
     4.接觸遮蔽 

   2.訊號遮蔽的切換
     int sigsuspend(sigset_t *sigs);
     遮蔽新的訊號,原來的訊號失效.
     sigsuspend是阻塞函式.對引數訊號遮蔽.
     對引數沒有指定的訊號不遮蔽,但當沒有遮蔽訊號處理函式呼叫完畢
     sigsuspend返回條件:
        1.訊號發生,並且訊號是非遮蔽訊號

     sigsuspend設定新的遮蔽訊號,儲存舊的遮蔽訊號
     而且當sigsuspend返回的時候,恢復舊的遮蔽訊號.
#include <stdio.h>
#include <signal.h>
void h(int s)
{
    printf("非遮蔽訊號發生!\n");
}
main()
{
    sigset_t sigs;
    
    signal(SIGWINCH,h);
    
    sigemptyset(&sigs);
    sigaddset(&sigs,2);
    printf("遮蔽開始!\n");
    sigsuspend(&sigs);
    printf("遮蔽結束!\n");
    
}3.查詢被遮蔽而未達到程序的訊號(查詢正在排隊的訊號)
     int sigpending(sigset_t *sets);
#include <stdio.h>
#include <signal.h>
void h(int s)
{
    printf("抽空處理int訊號\n");
}
main()
{
    int sum=0;
    int i;
    //1.    signal(SIGINT,h);
    sigset_t sigs,sigp,sigq;
    //2.    sigemptyset(&sigs);
    sigemptyset(&sigp);
    sigemptyset(&sigq);
    
    sigaddset(&sigs,SIGINT);
    //3.    sigprocmask(SIG_BLOCK,&sigs,0);
    for(i=1;i<=10;i++)
    {
        sum+=i;
        sigpending(&sigp);
        if(sigismember(&sigp,SIGINT))
        {
            printf("SIGINT在排隊!\n");
            sigsuspend(&sigq);
            //使原來遮蔽訊號無效,開放原來訊號
            
//使新的訊號遮蔽,
            
//當某個訊號處理函式處理完畢
            
//sigsuspend恢復原來遮蔽訊號,返回         }
        sleep(1);
    }
    printf("sum=%d\n",sum);
    sigprocmask(SIG_UNBLOCK,&sigs,0);
    printf("Over!\n");
}

回顧:
  1.程序控制sleep pause
  2.理解訊號的中斷流程
  3.發射訊號(Shell/code),處理訊號
  4.alarm與setitimer
  5.訊號應用:實現簡單多工與程序之間通訊
  6.使用訊號+sleep/pause控制程序
  7.訊號的遮蔽
  8.瞭解sigpending與 sigsuspend的使用.

作業:
  
  1.寫一個程式,建立兩個子程序,分別計算1-5000與5001-1000素數,
     通過訊號讓,父程序專門完成資料儲存.
  
  2.完成課堂上的作業:
     顯示7位隨機數,同時使用定時器訊號顯示時間
     在使用鍵盤控制7位隨機數的停止與繼續顯示.
     (建議美化) 
  3.完成如下作業:
     在螢幕水平同時顯示移動兩個字元.
  
思考:
  訊號處理函式呼叫過程中,是否被其他訊號影響.        
   
明天:
 1.訊號與程序間資料傳遞
   sigqueue=kill與sigaction=signal
 2.IPC:
   基於檔案
     無序檔案:對映
     有序檔案:管道檔案:有名/匿名
          socket檔案
   基於記憶體
     無序記憶體
        記憶體共享
     有序記憶體  
        共享佇列