【系统编程】 进程间通信方式
1.管道pipe:
利用管道进行父子进程间通信:
利用管道进行父子间双向通信:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #define BUF_SIZE 20 int main(int argc,char *argv[]){ int fd[2],fd1[2]; char buf[BUF_SIZE]; pipe(fd);pipe(fd1); pid_t pid = fork(); if(pid==0){ char str[] = "Hello world\n"; write(fd[1],str,sizeof(str)); int len = read(fd1[0],buf,BUF_SIZE); write(STDOUT_FILENO,buf,len); } else{ sleep(1); char str[] = "zytxdy\n"; write(fd1[1],str,sizeof(str)); int len = read(fd[0],buf,BUF_SIZE); write(STDOUT_FILENO,buf,len); } sleep(1); return 0; }
2.有名管道FIFO:
创建方法:1.直接mkfifo FIFONAME 创建有名管道。
2.在.c里写代码创建,头文件包括sys/stat.h,参数1是名称,参数2是权限
实现无血缘间进程的通信:
其实FIFO和文件打开关闭相似性大,与管道pipe其实关联性并不大,FIFO可以一个写端多个读端/多个写端一个读端。
/*注意以下代码运行的时候要加参数,参数即为FIFO名*/
写端write:
#include<stdio.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h> #include<fcntl.h> #include<stdlib.h> #include<string.h> int main(int argc,char *argv[]){ int fd,i; char buf[4096]; fd = open(argv[1],O_WRONLY); i=0; while(1){ sprintf(buf,"hello itcast: %d\n",i++); write(fd,buf,strlen(buf)); sleep(1); } close(fd); return 0; }
读端read:
#include<stdio.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h> #include<fcntl.h> #include<stdlib.h> #include<string.h> int main(int argc,char *argv[]){ int fd,len; char buf[4096]; fd = open(argv[1],O_RDONLY); int i=0; while(1){ len = read(fd,buf,sizeof(buf)); write(STDOUT_FILENO,buf,len); sleep(1); } close(fd); return 0; }
3.共享存储映射:
1.文件直接用于通信(由于比较简单就略过)
2.存储映射I/O(mmap:Memory map):
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<error.h> #include<pthread.h> #include<sys/mman.h> #include<fcntl.h> int main(int argc,char *argv[]){ char *p = NULL; int fd; fd = open("testmap",O_RDWR|O_CREAT|O_TRUNC,0644); lseek(fd,20,SEEK_END); //扩展文件大小 write(fd,"\0",1); int len = lseek(fd,0,SEEK_END); //获取文件大小 p = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //可读可写,p为字符串形式文件内容 if(p == MAP_FAILED){ perror("mmap error"); exit(1); } //使用 p 对文件进行读写操作 strcpy(p,"Hello mmap"); //写操作 printf("-----%s\n",p); int ret = munmap(p,len); if(ret == -1){ perror("munmap error"); exit(1); } return 0; }
4.信号集操作函数:
set会与阻塞信号集做或操作
示例代码:
#include<stdio.h> #include<stdlib.h> #include<signal.h> #include<unistd.h> #include<errno.h> void print_set(sigset_t *set){ for(int i=1; i<32 ;i++){ if(sigismember(set,i)) putchar('1'); else putchar('0'); } printf("\n"); } int main(int argc,char *argv[]){ sigset_t set,oldset,pedset; int ret = 0; sigemptyset(&set); sigaddset(&set,SIGINT); ret = sigprocmask(SIG_BLOCK,&set,&oldset); if(ret == -1){ perror("sigprocmask error"); exit(1); } while(1){ ret = sigpending(&pedset); if(ret == -1){ perror("sigpending error"); exit(1); } print_set(&pedset); sleep(1); } return 0; }
如下所示,按下<ctrl+c>组合键就能看到位发生了变化,因为其在未决信号集之中
ps:注意,kill -9即使被屏蔽了也照样生效,其他的被屏蔽了就无法生效。
5.信号:
阻塞信号集(信号屏蔽字):将某些信号加入集合,对他们设置屏蔽,当屏蔽x信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)
未决信号集:
1.信号产生,未决信号集中描述该信号的位立刻为1,表示信号处于未决状态。当信号被处理对应位反转为0。这一时刻往往非常短暂。
2.信号产生后由于某些原因(主要是阻塞)不能抵达。这类信号的集合称为未决信号集。在屏蔽解除前,信号一直处于未决状态。
信号4要素:
信号编号、信号名称、信号对应事件、信号默认处理动作
信号使用之前,应先确定其4要素,而后再用。
默认动作:
Term:终止进程
lgn:忽略信号(默认即时对该种信号忽略操作)
Core:终止进程,生成Core文件。(查验进程死亡原因,用于gdb调试)
Stop:停止(暂停)进程
Cont:继续运行进程
硬件异常产生信号:
除0操作 -> 8)SIGFPE(浮点数例外)
非法访问内存 -> 11)SIGSEGV(段错误)
总线错误 -> 7)SIGBUS
信号捕捉:
signal函数:
#include<stdio.h> #include<signal.h> #include<unistd.h> void sig_catch(int signo){ printf("catch you! %d\n",signo); return ; } int main(int argc,char *argv[] ){ signal(SIGINT,sig_catch); while(1); }
这时候编译运行后,一旦输入<ctrl+c>组合键,就会显示捕捉到信号
sigaction函数:
也是用来注册一个信号的捕捉函数
#include<stdio.h> #include<signal.h> #include<unistd.h> void sig_catch(int signo){ //步骤函数,回调函数 printf("catch you! %d\n",signo); return ; } int main(int argc,char *argv[] ){ struct sigaction act,oldact; act.sa_handler = sig_catch; //设置捕捉的函数名 sigemptyset(&act.sa_mask);//初始化,设置屏蔽字mask act.sa_flags = 0; //默认属性 sigaction(SIGINT,&act,&oldact); while(1); }
当然我们也可以捕捉很多函数,如下图所示即可捕捉2个信号:
信号捕捉特性:
1:捕捉函数执行期间,信号屏蔽字,由mask -->sa_mask,捕捉函数执行结束,恢复回mask
2:捕捉函数执行期间,本信号自动被屏蔽(sa_flags = 0)
3:捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次