四種方式
- 管道(僅有血緣關(guān)系的使用)
- 信號 (開銷小)
- 套接字
- 共享映射區(qū)
一裂垦、管道
linux下文件類型共七種:
普通顺囊,目錄,軟鏈接
字符缸废,塊包蓝,管道,套接字-----偽文件企量,不占用磁盤测萎,僅占用內(nèi)存緩沖區(qū)。
管道需要保證單向流通届巩,不能有第三方連通管道
1.1 管道的特點
- 由兩個文件描述符引用硅瞧,一個表示讀,一個表示寫
- 規(guī)定數(shù)據(jù)從寫端流向讀端
- 由環(huán)形隊列實現(xiàn)恕汇,借助內(nèi)存緩沖區(qū)(大小4k)腕唧,因此讀取后就消失
- 無論匿名還是有名,讀取之后數(shù)據(jù)就會消失
弊端
- 不能自己讀自己寫
- 每個數(shù)據(jù)只能讀取一次
- 半雙工通信瘾英,單向流動
- 只能在有公公祖先的進(jìn)程使用管道
1.2 PIPE()
int pipe(int pipefd[2])
//參數(shù)fd[0]讀端 fd[1]寫端
//成功返回0枣接,失敗-1
例子
int fd[2];
pipe(fd);
pid_t pid=fork();
char buf[1024];
//每個進(jìn)程都有讀寫兩端連接管道
if(pid>0){
close(fd[0]);//關(guān)閉讀
write(fd[1],"hello",strlen("hello"));//父親寫
close(fd[1]);
}
else{
close(fd[1]);//關(guān)閉寫
ret=read(fd[0],buff,sizof(buff));//兒子讀
}
1.3 管道讀寫行為
讀
如果管道中無數(shù)據(jù)
- 若寫端全部關(guān)閉,read返回0缺谴;
- 若寫端未關(guān)閉但惶,則阻塞(讓出cpu)
寫
如果讀端關(guān)閉,則異常終止
如果讀端未關(guān)閉
- 管道已滿,write阻塞
- 未滿膀曾,正常讀寫
1.4 有名管道FIFO(創(chuàng)建管道文件)
//命令行
mkfifo filename
#include<sys/stat.h>
//庫函數(shù)县爬,man 3 mkfifo
int mkfifo(char * filename,mode_t mode);
1.4.1 實例,搭配sprintf使用
用sprintf將要寫的數(shù)據(jù)存儲
//1.創(chuàng)建fifo文件添谊,mkfifo myfifo
//2.編寫兩個.c文件财喳,相當(dāng)于兩個無血緣關(guān)系的進(jìn)程
//fifo_w:
char buf[4096];
int fd=open("myfifo",O_WRONLY);
while(1){
sprintf(buf,"hello fifo %d\n",i++);
write(fd,buf,strlen(buf));
sleep(1);
}
//fifo_r:
char buf[4096];
int fd=open("myfifo",O_RDONLY);
while(1){
len=read(fd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
sleep(1);
}
二、存儲映射mmap
將磁盤映射到內(nèi)存,即可以通過指針來操作文件斩狱,操作的目標(biāo)還是文件所以可以無血緣通信
/*
void*指任意返回類型耳高,用任何指針都可以
addr:一般填NULL,表示系統(tǒng)自己分配映射地址
length:<文件大小所踊,否則會出總線錯誤
prot:共享映射區(qū)讀寫屬性 PROT_READ祝高、PROT_WRITE、PROT_READ | PROT_WRITE
flags:標(biāo)注共享內(nèi)存屬性 MAP_SHARED污筷、MAP_RIVATE
offset:偏移位置,需要是4k的整數(shù)倍
return:映射的首地址
MAP_FAILED.errno
*/
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
案例
char * c=mmap(xxxxx);
strcpy(c,"hello");
2.1 munmap
int munmap(void *addr, size_t len);
//釋放映射區(qū)
2.2 mmap保險調(diào)用方式
- fd=open(文件名, O_RDWR);
- mmap(NULL,有效文件大小, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0);
2.3 mmap父子間通信
int fd=open("mmap.txt",O_CREAT|O_RDWR,0644);
ftruncate(fd,50);
int len=lseek(fd,0,SEEK_END);
char * p=mmap(NULL,len,PROT_READ|PROT_WRITE, MAP_SHARED, fd,0);
if(p==MAP_FAILED){
perror("mmap error");
exit(1);
}
pid=fork();
if(pid==0){
strcpy(p,"i am child");
}else{
sleep(1);
strcpy(p+strlen(p),"i am father");
printf("%s",p);
wait(NULL);
}
通過讀寫同一個指針乍赫,來進(jìn)行通信
2.3 mmap無血緣通信
//兩個文件瓣蛀,一個讀一個寫,mmap_w,mmap_r
//mmap_w同上
//mmap_r:
int fd=open("mmap.txt",O_RDONLY,0644);
int len=lseek(fd,0,SEEK_END);
char * p=mmap(NULL,len,PROT_READ|PROT_WRITE, MAP_SHARED, fd,0);
printf("%s",p);
mmap內(nèi)部數(shù)據(jù)可以多次讀取雷厂,fifo不行
三惋增、信號
信號產(chǎn)生和處理都是由內(nèi)核操作
阻塞信號集:對此集合信號設(shè)置屏蔽,當(dāng)屏蔽x信號之后改鲫,再收到該信號诈皿,就對該信號進(jìn)行處理推后()
未決信號集:未處理的信號
3.1 信號的處理方式
- 執(zhí)行默認(rèn)動作
- 忽略
- 捕捉(執(zhí)行用戶處理函數(shù))
3.2 信號四要素
- 名稱
- 編號
- 信號對應(yīng)事件
- 信號默認(rèn)處理動作
3.3 常規(guī)信號
- SIGHUP: 當(dāng)用戶退出shell時,由該shell啟動的所有進(jìn)程將收到這個信號像棘,默認(rèn)動作為終止進(jìn)程稽亏。
- SIGINT:當(dāng)用戶按下了<ctr+C>組合鍵時,用戶終端向正在運行中的由該終端啟動的程序發(fā)出此信號缕题。默認(rèn)動作為終止進(jìn)程
- SIGQUIT:當(dāng)用戶按下<ctrI+\>組合鍵時產(chǎn)生該信號截歉,用戶終端向正在運行中的由該終端啟動的程序發(fā)出些信號。默認(rèn)動作為終止進(jìn)程
- SIGILL:CPU檢測到基進(jìn)程執(zhí)行了非法指令烟零。默認(rèn)動作為終止進(jìn)程并產(chǎn)生core文件瘪松。
- SIGTRAP:該信號由斷點指令或其他trap指令產(chǎn)生。默認(rèn)動作為終止里程并產(chǎn)生core文件锨阿。
- SIGABRT:調(diào)用abort函數(shù)時產(chǎn)生該信號宵睦。默認(rèn)動作為終止進(jìn)程并產(chǎn)生core文件。
- SIGBUS:非法訪問內(nèi)存地址墅诡,包括內(nèi)存對齊出錯壳嚎,默認(rèn)動作為終止進(jìn)程并產(chǎn)生core文件。
- SIGFPE:在發(fā)生致命的運算錯誤時發(fā)出。不僅包括浮點運算錯誤诬辈,還包括溢出及除數(shù)為0等所有的算法錯誤酵使。默認(rèn)動作為終止進(jìn)程并產(chǎn)生core文件
- SIGKILL:無條件終止進(jìn)程。本信號不能被忽略焙糟,處理和阻塞口渔。默認(rèn)動作為終止進(jìn)程。它向系統(tǒng)管理員提供了可以殺死任何進(jìn)程的方法穿撮。
- SIGUSR1:用戶定義的信號缺脉。即程序員可以在程序中定義并使用該信號。默認(rèn)動作為終止進(jìn)程悦穿。
- SIGSEGV:指示進(jìn)程進(jìn)行了無效內(nèi)存訪問攻礼。默認(rèn)動作為終止進(jìn)程并產(chǎn)生core文件。
- SIGUSR2:另外一個用戶自定義信號栗柒,程序員可以在程序中定義并使用該信號礁扮。默認(rèn)動作為終止進(jìn)程。
- SIGPIPE::Broken pipe向一個沒有讀端的管道寫數(shù)據(jù)瞬沦。默認(rèn)動作為終止進(jìn)程太伊。
- SIGALRM:定時器超時,超時的時間由系統(tǒng)調(diào)用alarm設(shè)置逛钻。默認(rèn)動作為終止進(jìn)程僚焦。
- SIGTERM:程序結(jié)束信號,與SIGKILL不同的是曙痘,該信號可以被陽塞和終止芳悲。通常用來要示程序正常退出。執(zhí)行shell命令Kll時边坤,缺省產(chǎn)生這個信號名扛。默認(rèn)動作為終止進(jìn)程。
- SIGSTKFLT: Linux 早期版本出現(xiàn)的信號茧痒,現(xiàn)仍保留向后兼容罢洲。**默認(rèn)動作為終止進(jìn)程。(不用)
- SIGCHLD:子進(jìn)程狀態(tài)發(fā)生變化時文黎,父進(jìn)程會收到這個信號惹苗。默認(rèn)動作為忽略這個信號。
- SIGCONT:如果進(jìn)程已停止耸峭,則使其繼續(xù)運行桩蓉。默認(rèn)動作為繼續(xù)/忽略。
- SIGSTOP:停止進(jìn)程的執(zhí)行劳闹。信號不能被忽略院究,處理和阻塞洽瞬。默認(rèn)動作為暫停進(jìn)程。
- SIGTSTP:停止終端交互進(jìn)程的運行业汰。按下<ctrl+z>組合鍵時發(fā)出這個信號伙窃。默認(rèn)動作為暫停進(jìn)程。
3.4 alarm
設(shè)置定時器样漆,結(jié)束后向進(jìn)程發(fā)送SIGALARM为障,結(jié)束進(jìn)程,返回0或者剩余時長放祟,
重新調(diào)用alarm會打斷當(dāng)前鳍怨,返回剩余時間。
因此暫停alarm使用alarm(0);
int alarm(int seconds);
3.5 信號集的操作(未決跪妥,阻塞)
3.5.1 信號集的設(shè)置
sigset_t set; //設(shè)置信號集
int sigemptyset(sigset_t * set); //清空信號集
int sigfillset(sigset_t * set); //將信號集全部置1
int sigaddset(sigset_t * set,int signum); //將某個信號加入信號集 1
int sigdelset(sigset_t *set,int signum) //將某個信號清出信號集 0
int sigismember(const sigset_t *set, int signum); //判斷某個信號是否在信號集
3.5.2 sigprocmask
設(shè)置阻塞信號集
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
//
/*
how的取值
SIG_BLOCK 設(shè)置阻塞
SIG_UNBLOCK 取消阻塞
SIG_SETMASK 用自定義mask替換set
oldset:傳出參數(shù)鞋喇,舊有的mask
*/
3.5.3 sigpending
查看未決信號集
int sigpending(sigset_t *set);
//set 傳出參數(shù),查看未決信號集
案例
//設(shè)置ctrl c阻斷
sigset_t set,oldset,tempset;
sigemptyset(&set);
sigaddset(&set,SIGINT);
int ret=sigprocmask(SIG_BLOCK,&set眉撵,&oldset);
sigpending(&tempset);
for(int i=1;i<32;i++)
if(sigismember(set,i))
putchar(1);
else
putchar(0);
3.6 信號捕捉
注冊一個信號捕捉函數(shù)
#include<signal.h>
//該操作又signal和sigaction函數(shù)
signal(int sig, sig_t func);
int sigaction(int sig, const struct sigaction * act, struct sigaction * oact);
3.7 信號回收子進(jìn)程
3.7.1 SIGCHILD
產(chǎn)生條件:子進(jìn)程狀態(tài)發(fā)生變動侦香,包括終止暫停等
void catch_child(int signo){
wait(NULL);
return;
}
父進(jìn)程執(zhí)行:
struct sigaction act;
act.sa_handler=catch_child;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
sigaction(SIGCHLD,&act,NULL);
四纽疟、本地套接字
兩通信者鄙皇,互相創(chuàng)建兩個socket,通過socket連接建立通信
server.c
int lfd=socket(AF_UNIX,SOCK_STREAM / SOCK_DGRAM,0);
struct sockaddr_un addr;
/*
struct sockaddr_un{
_kernel_sa_family_t sun_family; //AF_UNIX
char sun_path[UNIX_PATH_MAX]; //socket文件名
};
*/
strcpy(addr.sun_path,"srv.socket");
int len=offsetof(struct sockaddr_un,sun_path)+strlen("srv.socket");
unlink("srv.socket");
bind(fd,(struct sockaddr*)&addr,len);//bind函數(shù)調(diào)用成功時仰挣,會創(chuàng)建一個socket,為了保證創(chuàng)建成功缠沈,在bind之前會調(diào)用unlink
listen(lfd,28);
while(1){
socklen_t clen=sizeof(caddr);
accept(lfd,(struct sockaddr*)&caddr,&len);
}
client.c
int cfd=socket(AF_UNIX,SOCK_STREAM / SOCK_DGRAM,0);
struct sockaddr_un caddr,saddr;
/*
struct sockaddr_un{
_kernel_sa_family_t sun_family; //AF_UNIX
char sun_path[UNIX_PATH_MAX]; //socket文件名
};
*/
strcpy(caddr.sun_path,"clie.socket");
int len=offsetof(struct sockaddr_un,sun_path)+strlen("clie.socket");
unlink("clie.socket");
bind(fd,(struct sockaddr*)&addr,len);
saddr.sun_family=AF_UNIX;
strcpy(saddr.sun_path,"srv.socket");
int len=offsetof(struct sockaddr_un,sun_path)+strlen("srv.socket");
connect(cfd,(struct sockaddr*)&saddr,len);