LINUX編程學習筆記(十五) 進程式控制制 檔案鎖 訊號處理與屏蔽

來源:互聯網
上載者:User
文章目錄
  • 1.1進程之中,預設的訊號處理
  • 1.2進程之中,使用者的訊號處理
  • 1 訊號屏蔽
  • 2 訊號屏蔽的切換
一 進程的基本控制1 進程的常見控制函數1.1 為什麼需要控制進程?1.2 休眠 pause/sleep/usleep1.3 on_exit atexit

int atexit(void (*function)(void)); 
int on_exit(void (*function)(int , void *), void *arg);
註冊一個函數,在調用exit或者main函數return前調用。
atexit的函數較簡單,無參數
on_exit的函數函數中int是exit或者rerurn的傳回值(1位元組的 不需要宏解析),void*是後面的arg

可以註冊多次,但只調用一次。
多進程中子進程會複製該註冊,但只要運行一次其他的都被取消。

#include <stdio.h>#include <stdlib.h>void func(){printf("before over\n");}int main(){printf("Process\n");atexit(func);return 0;}

#include <stdio.h>#include <stdlib.h>void func(int statue,void *args){printf("before over\n");printf("statue = %d\n",statue);printf("args = %s\n",(char *)args);}int main(){printf("Process\n");char *str = "hello";on_exit(func,(void *)str);return 99;}

zhao@ubuntu:~/unix/6$ ./a.out 
Process
before over
statue = 99
args = hello

    2 進程與檔案鎖

在多進程下檔案讀寫是共用的
問題:怎麼知道一個檔案正在被另外進程讀寫?
解決方案: 檔案鎖(預設是建議鎖 強制鎖需重編譯核心)
API:
fcntl (檔案鎖受核心參數影響)
編程技巧:
對檔案加鎖
判定一個檔案是否存在鎖
函數說明:
int fcntl(
int fd, //被加鎖的檔案描述符
int cmd, //鎖的操作方式: F_SETLK (若已加鎖,異常返回) F_SETLKW(若已加鎖則等待) F_UNLK(解鎖)
struct flock *fk //鎖的描述
);

  struct flock {
               ...
               short l_type;    /* Type of lock: F_RDLCK,
                                   F_WRLCK, F_UNLCK */
               short l_whence;  /* How to interpret l_start:
                                   SEEK_SET, SEEK_CUR, SEEK_END */
               off_t l_start;   /* Starting offset for lock */
               off_t l_len;     /* Number of bytes to lock */
               pid_t l_pid;     /* PID of process blocking our lock
                                   (F_GETLK only) */
               ...
           };

加鎖

#include <unistd.h>#include <fcntl.h>#include <stdlib.h>#include <stdio.h>int main(){int fd;struct flock lk={0};//開啟一個檔案fd = open("test",O_RDWR|O_CREAT,0666);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;//加鎖int r = fcntl(fd,F_SETLK,&lk);if(r == 0) printf("加鎖成功\n");else{printf("加鎖失敗\n");}while(1);//程式結束自動解鎖}

注意這裡只是對5 到 10加了鎖 當加鎖地區不一樣時 還可以成功加上另外的鎖
如果地區重疊 就會導致加鎖失敗

讀鎖

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>int main(){int fd;struct flock lk={0}; //要是不初始化 就不能成功得到鎖fd = open("test",O_RDWR);if(fd <0){printf("::%m\n"),exit(-1);}int r = fcntl(fd,F_GETLK,&lk);  //F_GETLKif(r == 0){printf("得到鎖成功\n");}else{printf("得到鎖失敗\n");}if(lk.l_type == F_WRLCK){printf("寫鎖\n");}printf("start:%d,len %d\n",lk.l_start,lk.l_len);}

解鎖

#include <unistd.h>#include <fcntl.h>#include <stdlib.h>#include <stdio.h>int main(){int fd;struct flock lk={0};//開啟一個檔案fd = open("test",O_RDWR|O_CREAT,0666);if(fd == -1){printf("::%m\n"),exit(-1);}//描述鎖lk.l_type = F_WRLCK;lk.l_whence=SEEK_SET;lk.l_start=20;lk.l_len=10;//加鎖int r = fcntl(fd,F_SETLK,&lk);if(r == 0) printf("加鎖成功\n");else{printf("加鎖失敗\n");}sleep(3);/* 。。。對檔案進行操作。。。*/lk.l_type = F_UNLCK;r = fcntl(fd,F_SETLK,&lk);if(r == 0) printf("解鎖成功\n");else{printf("解鎖失敗\n");}}

注意 這些都只是建議鎖,即使你讀取到有鎖,或者加鎖失敗,你都還可以對檔案進行操作

二 訊號1 訊號的作用

通知其他進程相應。進程間的一種通訊機制。

接收訊號的進程會馬上停止,執行訊號處理函數。(非強制中斷)

1.1進程之中,預設的訊號處理
#include <unistd.h>#include <stdio.h>#include <signal.h>int main(){while(1){printf("進程在執行\n");sleep(1);}}

怎麼發訊號: kill -s 訊號 進程id  
 kill -s 1 7038 //向進程7038中 發送訊號1
 kill  -s SIGUP 7038  //使用訊號的宏
 kill -l 查看所有訊號

進程在執行
進程在執行
進程在執行
進程在執行
進程在執行
掛起

ctrl+c 相當與發送訊號2 SIGINT 中斷訊號

1.2進程之中,使用者的訊號處理

一個進程只能綁定一個函數
一個函數可以綁定在多個進程上

#include <unistd.h>#include <stdio.h>#include <signal.h>void handle(int s){printf("我是訊號%d\n",s);sleep(2);}//函數返回後 主進程才繼續int main(){signal(SIGINT,handle); //註冊訊號處理函數while(1){printf("進程在執行\n");sleep(1);}}

進程在執行
^C我是訊號2 
進程在執行
^C我是訊號2 
進程在執行
^C我是訊號2 
進程在執行
進程在執行
進程在執行

發現ctrl+c 發送的是訊號2
發現關不掉 kill -s 9

注意:訊號SIGKILL SIGSTOP 不能被處理

2 訊號的發送與安裝、

       int kill(pid_t pid, int sig);//向進程pid發送sig訊號
進程ID:
>0 發送訊號到指定進程
=0 發送訊號到該進程所在進程組的所有進程
-1  發送所有進程  不包含init
<-1 發送給指定的進程組 (組ID=絕對值)

#include <unistd.h>#include <stdio.h>#include <signal.h>#include <sys/types.h>int main(){while(1){kill(7323,SIGINT);sleep(5);}}
3 訊號的應用3.1 延時器

SIGALRM
訊號發出函數 alarm

3.2 定時器

int setitimer(int which, //計時方式 ITIMER_REAL  ITMER_VIRTUAL ITIMER_PRO 
const struct itimerval *val, // 定時器的時間參數
struct itimer *oldval); // 返回原來設定的定時器

ITIMER_REAL: 以系統真實的時間來計算,它送出SIGALRM訊號。(常用)
ITIMER_VIRTUAL: -以該進程在使用者態下花費的時間來計算,它送出SIGVTALRM訊號。
ITIMER_PROF: 以該進程在使用者態下和核心態下所費的時間來計算,它送出SIGPROF訊號。

    struct itimerval {
               struct timeval it_interval;  /* next value */  間隔時間
               struct timeval it_value;    /* current value */ 延時時間
           };

           struct timeval {
               long tv_sec;                /* seconds */
               long tv_usec;               /* microseconds */
           };

oldval 可以設定為null 不接收

#include <signal.h>#include <stdio.h>#include <unistd.h>#include <sys/time.h>void deal(int s){printf("起床!\n");}int main(){signal(SIGALRM,deal);struct itimerval v={0}; //創立一個結構體 結構體又有兩個結構體v.it_interval.tv_sec = 3;v.it_interval.tv_usec = 1;v.it_value.tv_sec =5;v.it_value.tv_usec = 0;//5秒後 每隔3秒1微秒 發出SIGALRM訊號setitimer(ITIMER_REAL,&v,NULL);while(1){}}
4 訊號的可靠與不可靠以及訊號的含義

訊號有丟失(訊號壓縮)
由於曆史的緣故:訊號有壓縮的需求
可靠訊號(即時訊號)與不可靠訊號(非即時訊號)

早期訊號 1-31 31個訊號都是不可靠(與系統有關,系統會產生)。
最早31個中留了SIGUSR1 SIGUSR2給使用者使用
後期訊號 34-64 31個訊號 ,可靠訊號(使用者訊號)
跟系統打交道,使用早期訊號
處理使用者產生的訊號,使用可靠訊號

5 訊號的操作1 訊號屏蔽

 int sigprocmask(int how, //操作方式:SIG_BLOCK屏蔽   SIG_UNBLOCK解除屏蔽  SIG_SETMASK修改屏蔽
const sigset_t *set,//  操作的訊號集合
sigset_t *oldset);   // 返回原來的被屏蔽的集合
編程步驟
1 聲明訊號集合
sigset_t sigs;
2 加入屏蔽訊號
訊號結合屏蔽函數
清空集合 sigemptyset
添加訊號到集合 sigaddset
從集合刪除訊號 sigdelset
將所有訊號加進去 sigfillset
判斷訊號是否在集合中 sigismember

       int sigemptyset(sigset_t *set);
       int sigfillset(sigset_t *set);
       int sigaddset(sigset_t *set, int signum);
       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);

查詢正在排隊的訊號(已收到 但被sigprocmask屏蔽的訊號)

  int sigpending(sigset_t *set);

sigpending()  returns  the set of signals that are pending for delivery
       to the calling thread (i.e., the signals which have been  raised  while
       blocked).  The mask of pending signals is returned in set.

#include <stdio.h>#include <signal.h>int main(){sigset_t sigs; //屏蔽集合sigset_t sig;sigemptyset(&sigs);//清空集合sigemptyset(&sig);//清空集合sigaddset(&sigs,SIGINT);//sigprocmask(SIG_BLOCK,&sigs,0); //開始屏蔽int sum=0;int i;for(i=1;i<=10;i++){sum+=i;sleep(1);sigpending(&sig);//查詢正在排隊(被屏蔽)的訊號if(sigismember(&sig,SIGINT)){ printf("SIGINT正在排隊!\n");}}printf("sum=%d\n",sum);sigprocmask(SIG_UNBLOCK,&sigs,0); //解除屏蔽printf("over!\n");}
2 訊號屏蔽的切換

sigsuspend 訊號屏蔽切換
int sigsuspend(const sigset_t *mask); 
sigsuspend 暫時取代目前的屏蔽訊號,然後掛起進程,直到一個訊號,調用一個訊號處理常式或終止該進程。

1如果該訊號終止該過程sigsuspend不會返回。
2 如果訊號被捕獲,處理完訊號處理函數首,sigsuspend()會返回,且恢複以前的屏蔽訊號。

       SIGKILL或SIGSTOP無法屏蔽

這個函數的主要作用是將中斷限制在一定的範圍內。比如signal產生的中斷可能發生在任何地方

#include <stdio.h>#include <signal.h>void handle(int s){printf("SIGINT處理中\n");}int main(){signal(SIGINT,handle);sigset_t sigp,sigq,sigs;sigemptyset(&sigp);sigemptyset(&sigq);sigemptyset(&sigs);sigaddset(&sigp,SIGINT);sigprocmask(SIG_BLOCK,&sigp,0);printf("屏蔽SIGINT開始\n");int i = 0,sum=0;for(;i<10;i++){sum += i;sleep(1);sigpending(&sigq);//得到排隊中的訊號if(sigismember(&sigq,SIGINT)){printf("SIGINT正在排隊\n");sigsuspend(&sigs);//只能在這裡產生SIGINT的中斷printf("SIGINT處理結束\n");}}}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.