問題解答:
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文件
基于內存
無序內存
內存共享
有序內存
共享隊列