1. 程式人生 > >程序通訊(命名管道,訊號集)

程序通訊(命名管道,訊號集)

命名管道:(由於匿名管道(沒有名字,沒有路徑)通訊只能具有親緣關係的程序之間能,大大限制了程序間通訊的能力)

命名管道的特點:

       1、可以使用不同程序(無論是否有親緣關係的程序)通訊

       2、是一個特殊檔案:檔名與路徑(檔案系統中),是一個FIFO的檔案(沒有lssek)

       3、普通在讀寫方面沒有阻塞,而FIFO檔案卻與普通不同在讀寫方面:

             程序讀:
                                1、若該FIFO管道是阻塞

開啟,且FIFO檔案中沒有內容則阻塞等待,直有到資訊寫入。
                                2、若該FIFO管道是非阻塞開啟,無論FIFO中是否有內容,都不會阻塞等待。

             程序寫:
                                1、非阻塞

開啟檔案,無論讀程序是否讀取了,都會立即執行
                                2、阻塞開啟檔案,如果讀程序不讀取完成,則寫程序一直等待

             注:fifo預設是阻塞檔案。

API:建立一個管道檔案:    
             int mkfifo(const char *pathname, mode_t mode);
             返回值:0成功  -1失敗  錯誤碼在errno中
             pathname:路徑
             mode:許可權

 

   阻塞模式建立命名管道檔案 ./info  對管道檔案進行寫入資料 .

#include<iostream>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
//系統錯誤
#include<errno.h>
using namespace std;
int main()//寫程序
{
//1第一步:建立管道named檔案
	if(mkfifo("./info",0644)<0)
	{
		if(EEXIST!=errno)
		{
			perror("mkfifo fail");	
			return -1;
		}
	}
//1開啟寫入:以只讀和非阻塞開啟
	int fd=open("./info",O_WRONLY);	
//2操作
	char msg[100]="I Love You!#Do you love me?";
	cout<<write(fd,msg,strlen(msg))<<endl;	
//3關閉	
	close(fd);	
}

   新建一個檔案 , 用於讀取命名管道里面的資料 ./info  .

#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>
using namespace std;
int main()
{
	//開啟
	int fd=open("./info",O_RDONLY);
	if(fd<0)
	{
		perror("open fail\n");
		return -1;
	}
	//讀取
	char ch;
	while(read(fd,&ch,1)>0)
	{
		if(ch=='#')
			cout<<endl;
		else
			cout<<ch;
	}
	cout<<endl;
	close(fd);
	return 0;
}

 

   非阻塞模式開啟檔案  O_NONBLOCK 方式開啟 .

#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>
using namespace std;
int main()
{
	//開啟
	int fd=open("./info",O_RDONLY|O_NONBLOCK);
	if(fd<0)
	{
		perror("open fail\n");
		return -1;
	}
	//讀取
	char ch;
	while(read(fd,&ch,1)>0)
	{
		if(ch=='#')
			cout<<endl;
		else
			cout<<ch;
	}
	cout<<endl;
	close(fd);
	return 0;
}

 

  訊號:(system IPC:UNIX)是比較古老的程序間通訊的一種方法。主要是向另外一個程序發出通知。

  1、訊號:系統給每一個訊號都取了一個編號:
                shell:    Kill -l  
                31號訊號之前代表不靠訊號
                34訊號之後可靠(鏈式結構)
                shell: man 7 signal (具體的說明)
               1)   SIGHUP   停止程序  終端退出時
               2)   SIGINT  停止程序  Ctrl+C組合鍵,發出
               9)   SIGKILL 停止程序  使用者退出程序
              14)  SIGALRM 時鐘鬧鐘
              10)  SIGUSER1 12)SIGUSER2  使用者自定義訊號
              17)  SIGCHLD 子程序退出,子程序傳送給資訊給父程序

    訊號的處理方式:
              1、系統預設處理SIG_DFL
              2、使用者自處理
              3、忽略;SIG_IGN

    產生一個訊號:
              1、硬體產生:  ctrl+c  硬體產生一個訊號    結束
                         ctrl+/  硬體產生一個訊號    結束(產生一個core檔案)
              2、系統產生:
                         子程序退出,給父程序傳送訊號
              3、使用者呼叫核心API來產生:
                         kill  raise   alarm    pause
                         int kill(pid_t pid, int sig);
                         pid:程序的ID  sig訊號標識
                         int raise(int sig);    
                         unsigned int alarm(unsigned int seconds);
                    注:只允許定義一個鬧鐘,多個會被最新的替換
                         pause()  等待任意一個訊號到來就喚醒。
                    注:訊號處理是非同步方式
        (同步與非同步)

              處理資訊的步驟:
                   1、註冊:訊號+處理方式
                   2、訊號的產生

         拓展:
                  前端程序:程序在終端上執行
                  後臺程序:脫離終端,在後參執行。    可執行檔案 &

        訊號集 :
                  使用訊號集函式處理一組訊號,這些訊號按照呼叫的先後次序分為如下幾類:
                   1、建立訊號集:
                   2、登記訊號:決定程序如何處理訊號
                   3、訊號的處理與檢測:

         建立訊號集
         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);//查詢訊號是否在該訊號集中

   

    建立訊號集 , signal(SIGINT,sigfun);             ---->使用者自處理

                         signal(SIGINT,SIG_DFL);        ---->系統預設處理

    設定訊號集的功能:
                         int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
                         how:指定訊號集的的
                         set:訊號集
                         olds:把回之前的訊號集  

     指定訊號集的動作:
                         int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
                         act:將程序的訊號集修改
                         old:返回修改前的訊號集

#include<iostream>
#include<signal.h>
using namespace std;
//訊號使用者自處理
void sigfun(int sig)
{
	//使用者傳送SIGINT(Ctrl+C)
	if(SIGINT==sig)
	{
		cout<<"使用者想幹掉我"<<endl;
	}
}
int main()
{
//1註冊訊號: signal  使用者輸入CTRL+C 時,發出訊號
	//signal(SIGINT,sigfun);//使用者自處理
	//系統預設處理
	//signal(SIGINT,SIG_DFL);
	//系統收到此訊號,但要求忽略處理
	signal(SIGINT,SIG_IGN);
	while(1)
	{
		cout<<"I`m working"<<endl;
		sleep(1);
	}
	return 0;
}

 

   鬧鐘的訊號 .

#include<iostream>
#include<signal.h>
#include<stdlib.h>
using namespace std;
void sigfun(int sig)
{
	if(sig==SIGALRM)
	{
		cout<<"鬧鐘響啦,休息一下"<<endl;
	}
}
int main()
{
//1定義訊號
	signal(SIGALRM,sigfun);

//定時: 10到了,會給本程序傳送一個SIGALRM的訊號
	alarm(10);
	alarm(2);

	while(1)
	{
		cout<<"I`m working"<<endl;
		sleep(1);
	}
}

 

      鬧鐘訊號 .

#include<iostream>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>
using namespace std;
void sigfun(int sig)
{
	if(SIGALRM==sig)
	{
		cout<<"鬧鐘響了"<<endl;
	}
	else if(SIGINT==sig)
	{
		cout<<"ctrl+c被按下"<<endl;
	}
}
int main()
{
	//註冊訊息
	signal(SIGALRM,sigfun);
	signal(SIGINT,sigfun);//Ctrl+C 
	//定義一個鬧鐘
	//alarm(5);//定義5秒
	//阻塞等待訊號的到來
	//pause();
	sleep(100);
	while(1)
	{
		cout<<"哈哈,我在玩..."<<endl;
		sleep(1);
	}
	return 0;
}

 

   訊號集的實現 .

#include<iostream>
#include<stdio.h>
#include<signal.h>
using namespace std;
void sigfun(int sig)
{
	if(SIGINT==sig)
	{
		cout<<"如果你想退出程序,請按Ctrl+\\"<<endl;
	}
}
//SIGQUIT SIGINT SIGALARM加入訊號集,然後設定為阻塞狀態,當該等待5秒之後再恢復
int main()
{
	signal(SIGINT,sigfun);
	//1建立一個訊號集(替換程序的集號)
	sigset_t set;
	if(-1==sigemptyset(&set))//1先清空(set是隨機值,可能包含其他訊號)
	{
		perror("clear fail");
		return -1;
	}
	//1.1新增上述兩個訊號到訊號集中
	if(sigaddset(&set,SIGQUIT)<0)
	{
		perror("add fail");
		return -1;
	}
	if(sigaddset(&set,SIGINT)<0)
	{
		perror("add fail");
		return -1;
	}
	sigset_t oldset;
	//2.決定訊號集的處理---阻塞模式
	if(sigprocmask(SIG_BLOCK,&set,&oldset)<0)
	{
		perror("procmask fail");
		return -1;
	}
	//睡眠10s
	sleep(10);
	cout<<"10休息完成"<<endl;
	//恢復之前的狀態
	if(sigismember(&oldset,SIGINT)==1)
	{
		cout<<"SIGINT在該訊號集中"<<endl;
	}
	else
		cout<<"不在"<<endl;
	sigprocmask(SIG_UNBLOCK,&set,NULL);
	sleep(20);
	return 0;
}