1、基本概念css
1.進程組:屬於同一個登陸shell的進程linux
2.內核和超級用戶能夠發送信號到全部進程shell
普通用戶:只能向那些有相同UID,GID的進程發送信號,或者是一個進程組的進程發送信號數組
3.管道:單向的,先入先出,固定大小的數據流。進程讀取空管道時,直到有數據輸入前,進程阻塞;管道已滿,進程寫入時,進程阻塞服務器
命名管道 FIFOS 經過mkfifo建立數據結構
4.System V中三種進程通訊機制:消息隊列、信號量、共享內存函數
5.臨界區是一段代碼,任意時刻只能由一個進程之星它ui
6.進程收到信號的處置方式:1)忽略信號2)執行處理信號的函數3)暫停進程的執行4)重啓剛纔暫停的進程5)採用系統默認的操做this
7.信號 SIGHUP 掛斷控制終端 SIGINT 控制終端中斷鍵按下SIGKILL 刪除一個或一組進程,信號不能別忽略 SIGSTOP 暫停進程spa
8.內核轉儲:終止進程時沒留下一個稱爲core的文件,存儲當時進程內存中的內容,用於過後查錯。
二.註冊信號調用函數
signal()
#include <signal.h>
void (*signal(int signum, void (*handler))(int)))(int);
第一個參數指定信號的值,第二個參數指定針對前面信號值的處理,能夠忽略該信號(參數設爲SIG_IGN);能夠採用系統默認方式處理信號(參數設爲SIG_DFL);也能夠本身實現處理方式(參數指定一個函數地址)。
若是signal()調用成功,返回最後一次爲安裝信號signum而調用signal()時的handler值;失敗則返回-1。
例如:
void sigcatcher(int signum);
int main()
{
char buffer1[100],buffer2[100];
int i;
f(signal(SIGTERM,&sigcatcher)==-1)
{
printf("Couldn't resister signal handleer!\n");
exit(1);
}
printf("pid if this process is :%d \n",getpid());
printf("Please input:\n");
for(;;)
{
fgets(buffer1,sizeof(buffer1),stdin);
for(i=0;i<100;i++)
{
if(buffer1[i]>=97 && buffer1[i]<=122)
buffer2[i]=buffer1[i]-32;
else
buffer2[i]=buffer1[i];
}
printf("Your inout is %s \n",buffer2);
}
exit(0);
}
void sigcatcher(int signum)
{
printf("catch signal SIZETERM.\n");//在終端另外一個窗口輸入kill 進程號就會結束進程
exit(0);
}
2.
#include <signal.h>
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
sigaction函數用於改變進程接收到特定信號後的行爲。該函數的第一個參數爲信號的值,能夠爲除SIGKILL及SIGSTOP外的任何一個特定有效的信號(爲這兩個信號定義本身的處理函數,將致使信號安裝錯誤)。第二個參數是指向結構sigaction的一個實例的指針,在結構sigaction的實例中,指定了對特定信號的處理,能夠爲空,進程會以缺省方式對信號處理;第三個參數oldact指向的對象用來保存返回的原來對相應信號的處理,可指定oldact爲NULL。若是把第2、第三個參數都設爲NULL,那麼該函數可用於檢查信號的有效性。
sigaction結構定義以下:
struct sigaction {
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int,struct siginfo *, void *);
sigset_t sa_mask;
unsigned long sa_flags;
}
例如:
struct sigaction act;
act.sa_handler=sighandler;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
if(sigaction(SIGTERM,&act,NULL)==-1)
{
printf("Couldn't resister signal handleer!\n");
exit(1);
}
實現和signal同樣的功能
3.信號集
信號集被定義爲一種數據類型:
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t
信號集用來描述信號的集合,每一個信號佔用一位。Linux所支持的全部信號能夠所有或部分的出如今信號集中,主要與信號阻塞相關函數配合使用。下面是爲信號集操做定義的相關函數:
#include <signal.h>
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);
sigemptyset(sigset_t *set)初始化由set指定的信號集,信號集裏面的全部信號被清空;
sigfillset(sigset_t *set)調用該函數後,set指向的信號集中將包含linux支持的64種信號;
sigaddset(sigset_t *set, int signum)在set指向的信號集中加入signum信號;
sigdelset(sigset_t *set, int signum)在set指向的信號集中刪除signum信號;
sigismember(const sigset_t *set, int signum)斷定信號signum是否在set指向的信號集中。
信號集操做
1. int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)); oldset非空返回當前信號掩碼;set非空,根據how設定掩碼;set爲空沒有意義 執行成功返回0,不然返回-1
SIG_BLOCK 在進程當前阻塞信號集中添加set指向信號集中的信號
SIG_UNBLOCK 若是進程阻塞信號集中包含set指向信號集中的信號,則解除對該信號的阻塞
SIG_SETMASK 更新進程阻塞信號集爲set指向的信號集
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
void pr_mask(int signum)
{
sigset_t sigset;
if(sigprocmask(0,NULL,&sigset)<0)//返回當前進程的信號掩碼,到sigset中
{
printf("sigprocmask error\n");
exit(0);
}
if(sigismember(&sigset,SIGINT))
printf("SIGINT\n");
if(sigismember(&sigset,SIGTERM))
{
printf("SIGTERM\n");
exit(0);
}
}
if(signal(SIGINT,&pr_mask)==-1)//pr_mask 收到信號後處理函數
{
printf("Couldn't resister signal handleer!\n");
exit(1);
}
if(signal(SIGTERM,&pr_mask)==-1)
{
printf("Couldn't resister signal handleer!\n");
exit(1);
}
2.sigpending(sigset_t *set))得到當前已遞送到進程,卻被阻塞的全部信號,在set指向的信號集中返回結果。
static void sig_quit(int);
int main()
{
sigset_t newmask,oldmask,pendmask;
if(signal(SIGQUIT,&sig_quit)==-1) //註冊信號SIGQUIT
{
printf("couldn't register signal handler for SIGQUIT.\n");
exit(1);
}
sigemptyset(&newmask);//清空信號集
sigaddset(&newmask,SIGQUIT);//添加SIGQUIT信號
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask)<0)//保存當前信號集
{
printf("SIG_BLOCK error.\n");
exit(2);
}
sleep(5);
if(sigpending(&pendmask)<0)//檢測是否有掛起信號
{
printf("sigpend error\n");
exit(3);
}
if(sigismember(&pendmask,SIGQUIT))//查看SIGQUIT是否在掛起信號集中
printf("SIGQUIT pending\n");
if(sigprocmask(SIG_SETMASK,&oldmask,NULL)<0)//消除信號的阻塞
{
printf("SIG_SETMASK error.\n");
exit(4);
}
printf("SIGQUIT unblocked.\n");
sleep(5);
exit(0);
}
static void sig_quit(int signum)
{
printf("catch SIGQUIT\n");
if(signal(SIGQUIT,SIG_DFL)==-1)
printf("Couldn't reset SIGQUIT!\n");
return;
}
3.sigsuspend(const sigset_t *mask))用於在接收到某個信號以前, 臨時用mask替換進程的信號掩碼, 並暫停進程執行,直到收到信號爲止。sigsuspend 返回後將恢復調用以前的信號掩碼。信號處理函數完成後,進程將繼續執行。該系統調用始終返回-1,並將errno設置爲EINTR。
while(quitflag==0)
sigsuspend(&zeromask); //直到quitflag變爲1時,進程甦醒,從新執行
三.發送信號
1.raise 函數
int raise(int signum) 想一個進程自身發送信號,成功返回0,不然返回-1
2.kill
int kill(pid_t pid,int signo) 成功返回0 錯誤返回1
該系統調用能夠用來向任何進程或進程組發送任何信號。參數pid的值爲信號的接收進程
pid>0 進程ID爲pid的進程
pid=0 同一個進程組的進程
pid<0 pid!=-1 進程組ID爲 -pid的全部進程
pid=-1 除發送進程自身外,全部進程ID大於1的進程
4、管道
else if(pid>0)
{
close(fd[0]);
write(fd[1],"How are you?\n",12);//對管道進行讀寫操做時,先關閉另一端
}
else
{
close(fd[1]);
n=read(fd[0],buffer,1024);
write(STDOUT_FILENO,buffer,n);
}
2.if(dup2(fd[0],STDIN_FILENO)!=STDIN_FILENO)//標準輸入設爲管道的讀數據一端
{
printf("dup2 error to stdin!\n");
exit(1);
}
close(fd[0]);
3.FILE* popen(char *comd,char * type)調用fork 和exec函數執行命令行命令
if(popen("upcase","w"))==null)
{
printf("popen error");
exit(1);
}
pcloese(FILE * fp)
if(pclose(fp)==-1)
{
printf("pclose error.\n");
exit(1);
}
二.命名管道
不一樣之處:1)可用於非相同祖先進程之間通訊
2)內容存儲在文件系統中,除非刪除,不然不會消失
操做:1)必須打開才能進行讀寫操做
2)沒有其餘進程打開一個命名管道式,就對其進行讀操做,會產生SIGPIPE信號;全部寫進程都關閉了,讀管道認爲到達文件末尾
3)多個進程寫操做進行的狀況下,寫交錯的狀況會發生。
應用:shell命令行傳遞命令
客戶服務器之間交換數據
int mkfifo(char * pathname,mode_t mode)
int mknod(chat *pathname,mode_t mode,dev_t dev);
服務器程序
讀取數據
/**ex6-12client.c**/
#include<stdio.h>
#include<stdlib.h>
#define FIFO_FILE "MYFIFO"
int main(int argc,char *argv[])
{
FILE *fp;
int i;
if(argc<=1)
{
printf("usage:%s<pathname>\n",argv[0]);
exit(1);
}
if((fp=fopen(FIFO_FILE,"w"))==NULL)
{
printf("open fifo failed.\n");
exit(1);
}
for(i=1;i<argc;i++)
{
if(fputs(argv[i],fp)==EOF)
{
printf("write fifo error.\n");
exit(1);
}
if(fputs("",fp)==EOF)
{
printf("write fifo error.\n");
exit(1);
}
}
fclose(fp);
return 0;
}
客戶端程序,輸入數據
/**ex6-12client.c**/
#include<stdio.h>
#include<stdlib.h>
#define FIFO_FILE "MYFIFO"
int main(int argc,char *argv[])
{
FILE *fp;
int i;
if(argc<=1)
{
printf("usage:%s<pathname>\n",argv[0]);
exit(1);
}
if((fp=fopen(FIFO_FILE,"w"))==NULL)
{
printf("open fifo failed.\n");
exit(1);
}
for(i=1;i<argc;i++)
{
if(fputs(argv[i],fp)==EOF)
{
printf("write fifo error.\n");
exit(1);
}
if(fputs("",fp)==EOF)
{
printf("write fifo error.\n");
exit(1);
}
}
fclose(fp);
return 0;
}
三。system V IPC 機制
1.關鍵字和標識符
標識符:每一個對象都和惟一一個引用標識符相連,惟一性侷限在同一IPC對象內
關鍵字:用於定位system v中IPC機制的對象的引用標識符,類型爲key_t 包含在頭文件<sys/types.h>中
#include<sys/ipc.h>
key_t ftok(const char *path,int id) 返回key_t pathname 路徑名 id只有低8位有效
2.ipc_perm 包含於頭文件<sys/ipc.h>中包含了uid,gid,cuid,cgid,mod,seq(應用序號),key關鍵字
3.ipcs 用於顯示IPC機制全部對象的狀態
一).消息隊列
每一個消息隊列都有一個msqid_ds結構與其關聯:
1.建立消息隊列
#include<sys/types.h>
#include<sys/ipe.h>
#include<sys/msq.h>
int msgget(key_t key,int flag);
1)key==IPC_PRIVATE flag 爲什麼值都建立消息隊列
2)key!=IPV_PRIVATE flag 置位IPC_CREATE 沒設置 IPC_EXCL 則既能打開又能建立;key 與內核存在關鍵字相同時,打開消息隊列,返回引用標誌符;不然執行建立,返回標識符
3))key!=IPV_PRIVATE flag 置位IPC_CREATE 置 IPC_EXCL 執行建立,若存在消息隊列,則返回錯誤
flag 設置權限
例如:
if((msqid=msgget(key,IPC_CREAT|0660))==-1)
{
printf("the message failed.\n");
exit(1);
}
printf("the message succed :msqid=%d\n",msqid);
2.發送接收消息
1)消息結構
struct mymsg
{
long mtype;
char mtext[512];
}
發送
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgsnd(int msqid,const void *ptr,size_t nbytes,int flags);
msqid:消息隊列的引用標識符,插入隊尾
ptr :指向長整數的指針
nbytes:消息的長度,字節
flag:0或者IPC_NOWAIT
函數成功返回0不然返回-1
int msgrcv(int msqid,const void *ptr,size_t nbytes,long type,int flags);
type=0 f返回消息隊列中第一個消息;〉返回類型域等於這個值得消息;<0 在類型域<=type絕對值的消息中,類型域最小的第一個消息
flag: IPC——NOWAIT當type 無效直接返回錯誤,不然阻塞發送消息進程,直至type變爲有效
MSG_NOERROR,當數據長度超過nbytes時,截斷數據,正確返回,不然函數錯誤返回,消息仍然存在隊列中
調用成功返回字節數,不然返回-1
接收消息
/**ex6-14**/
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
struct my_msg
{
long int my_msg_type;
char text[BUFSIZ];
}msgbuf;
int main()
{
int running=1;
int msgid;
msgid=msgget((key_t)1234,0666|IPC_CREAT);
if(msgid==-1)
{
printf("msgget failed!\n");
exit(1);
}
while(running)
{
printf("Enter somt text:");
fgets(msgbuf.text,BUFSIZ,stdin);
msgbuf.my_msg_type=1;
if(msgsnd(msgid,(void *) &msgbuf,BUFSIZ,0)==-1)
{
printf("msgsnd failed!\n");
exit(1);
}
if(strncmp(msgbuf.text,"end",3)==0)
running=0;
}
//接收信息
int main()
{
int running=1;
int msgid;
long int msg_to_receive=0;
msgid=msgget((key_t)1234,0666|IPC_CREAT);
if(msgid==-1)
{
printf("msgget failed!\n");
exit(1);
}
while(running)
{
if(msgrcv(msgid,(void *)& msgbuf,BUFSIZ,msg_to_receive,0)==-1)
{
printf("received failed\n");
exit(1);
}
printf("You wrote:%s ",msgbuf.text);
if(strncmp(msgbuf.text,"end",3)==0)
running=0;
}
if(msgctl(msgid,IPC_RMID,0)==-1)
{
printf("msgct(IPC_RMID) failed!\n");
exit(1);
}
return 0;
}
3.控制消息
int msgctl(int msqid,int cmd,struct msqid_ds *buf)成功返回0 不然返回-1
cmd:IPC_STAT 複製數據到buf
IPC_SET 利用buf設置msqid
IPC_RMID 刪除msqid 及數據結構
二)信號量
共享資源:互斥共享:同一時刻只容許一個進程對資源進行操做
同步共享:同一時刻若干資源進行操做
1.信號量結構體
內核爲每一個信號量集維護一個信號量結構體,可在<sys/sem.h>找到該定義:
struct semid_ds {
struct ipc_perm sem_perm; /* 信號量集的操做許可權限 */
struct sem *sem_base; /* 某個信號量sem結構數組的指針,當前信號量集
中的每一個信號量對應其中一個數組元素 */
ushort sem_nsems; /* sem_base 數組的個數 */
time_t sem_otime; /* 最後一次成功修改信號量數組的時間 */
time_t sem_ctime; /* 成功建立時間 */
};
struct sem {
ushort semval; /* 信號量的當前值 */
short sempid; /* 最後一次返回該信號量的進程ID 號 */
ushort semncnt; /* 等待semval大於當前值的進程個數 */
ushort semzcnt; /* 等待semval變成0的進程個數 */
};
2.建立打開信號量
int semget(key_t key, int nsems, int oflag)出錯返回-1
if((sid=semget(keyval,numsems,IPC_CREAT|0660))==-1)
return -1;
(1) nsems>0 : 建立一個信的信號量集,指定集合中信號量的數量,一旦建立就不能更改。
(2) nsems==0 : 訪問一個已存在的集合
(3) 返回的是一個稱爲信號量標識符的整數,semop和semctl函數將使用它。
(4) 建立成功後信號量結構被設置:
.sem_perm 的uid和gid成員被設置成的調用進程的有效用戶ID和有效組ID
.oflag 參數中的讀寫權限位存入sem_perm.mode
.sem_otime 被置爲0,sem_ctime被設置爲當前時間
.sem_nsems 被置爲nsems參數的值
該集合中的每一個信號量不初始化,這些結構是在semctl,用參數SET_VAL,SETALL
初始化的。
semget函數執行成功後,就產生了一個由內核維持的類型爲semid_ds結構體的信號量
集,返回semid就是指向該信號量集的引索。
2.操做
int semop(int semid, struct sembuf *opsptr, size_t nops);
(1) semid: 是semget返回的semid
(2)opsptr: 指向信號量操做結構數組
(3) nops : opsptr所指向的數組中的sembuf結構體的個數
struct sembuf {
short sem_num; // 要操做的信號量在信號量集裏的編號,
short sem_op; // 信號量操做
short sem_flg; // 操做表示符
};
3.控制
int semctl(int semid,int semnum,int cmd,[union semun arg]);
semctl(sem_id,0,SETVAL,1);
semid:信號量集合合法標識
semnum:信號量集合的特定信號量的編號
cmd 命令
arg 可選
特殊命令返回特殊值,其餘命令充公返回0,失敗返回-1
三)共享內存
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>