寒假学习 第21天 (linux 高级编程) 笔记总结
IPC(Inter-Process Communication,进程间通信)
例子:
main1.c 向tmp写入数据
#include <stdio.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, const char *argv[]) { int fd=open("tmp",O_RDWR|O_CREAT|O_TRUNC,0666); ftruncate(fd,4); int *p=mmap(0,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); int i=0; while(1) { sleep(1); *p=i; i++; } close(fd); return 0; }
#include <stdio.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, const char *argv[]) { int fd=open("tmp",O_RDWR); int *p=mmap(0,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); while(1) { sleep(1); printf("%d\n",*p); } close(fd); return 0; }
IPC提出的应用背景
进程之间需要同步处理
同步需要通信。
普通文件就是最基本的通信手段
信号:系统跟应用程序之间的通信,父子进程之间通信,但两个独立的程序用信号通信是不可行的,因为不知道pid
普通文件IPC技术的问题:
一个进程改变文件,另外一个文件无法感知
解决方法:
一个特殊的文件,管道文件
命令行 mkfifo命令
mkfifo函数
int mkfifo(const char *pathname, mode_t mode);
无法使用映射,只能只用
例子:
#include <stdio.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> void end(int s) { unlink("my.pipe"); //关闭管道文件 exit(1); } int main(int argc, const char *argv[]) { signal(SIGINT,end); int fd; int i=0; mkfifo("my.pipe",0666); //建立管道 fd=open("my.pipe",O_RDWR); //打开管道 while(1) { sleep(1); write(fd,&i,4); ++i; } return 0; }
#include <stdio.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, const char *argv[]) { int fd; int i; fd=open("my.pipe",O_RDWR); //打开管道 while(1) { read(fd,&i,4); printf("%d\n",i); } return 0; }
1.read没有数据read阻塞,而且read后数据删除
2.数据有序
3.打开的描述符既可以读也可以写(two-way双工)
int shutdown(int sockfd, int how);// how: SHUT_WR,SHUT_RD,SHUT_RDWR
关闭读或者写, 实现单向通信,防止混乱
4.管道文件关闭后,数据不持久
5.那为什么产生管道文件这个东西?
管道的数据存储在内核的缓冲中,内核没有对管道文件进行映射,管道文件仅仅是在创建文件描述符的时候,用内核来识别创建文件描述符的内存结构的内存而已。内核创建缓冲,如果是普通文件,就直接与缓冲建立映射关系,如果是管道文件,者建立fifo。
有名的管道的名字仅仅是内核识别是否返回同一个fd的标示
所以当管道名失去标示作用的时候,实际是可以不要的
在父子进程之间:打开文件描述符后创建进程。(创建子进程,会拷贝父进程的所有东西)父子进程都有描述符,管道文件就没有价值了
匿名管道只能使用在父子进程中。
创建匿名管道
int pipe(int pipefd[2]); //输出2个文件描述符,pipfd[0] 只读,pipfd[1]只写(这样做为了避免混乱)
pipe函数:创建管道-->打开管道-->拷贝管道-->改变管道的读写权限
例子:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, const char *argv[]) { int fd[2]; int r; r=pipe(fd); printf("%d\n",getpid()); while(1); return 0; }
就可以看到匿名管道,一个只读,一个只写
rwx------ 1 root root 64 Feb 10 22:52 0 -> /dev/pts/5
lrwx------ 1 root root 64 Feb 10 22:52 1 -> /dev/pts/5
lrwx------ 1 root root 64 Feb 10 22:52 2 -> /dev/pts/5
lr-x------ 1 root root 64 Feb 10 22:52 3 -> pipe:[4479252]
l-wx------ 1 root root 64 Feb 10 22:52 4 -> pipe:[4479252]
例子:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, const char *argv[]) { printf("%d\n",getpid()); int fd[2]; int r; char buf[20]; pipe(fd); write(fd[1],"hello",5); write(fd[1],"word",5); r=read(fd[0],buf,sizeof(buf)); buf[r]=0; printf("%s\n",buf); write(fd[1],"AAAA",4); r=read(fd[0],buf,100); buf[r]=0; printf("%s\n",buf); return 0; }
例子2:父子进程匿名管道
#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(int argc, const char *argv[]) { int fd[2]; pipe(fd); if(fork()){ //父进程 close(fd[0]); //父进程只负债写 while(1) { write(fd[1],"hello",5); sleep(1); } }else{ //子进程 close(fd[1]); //子进程只负责读 char buf[100]; int r; while(1) { r=read(fd[0],buf,100); buf[r]=0; printf("%s\n",buf); } } return 0; }
例子3:建立2个子进程,分别找1~5000跟5001~1000的素数,并且存入
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <stdlib.h> #include <string.h> #include <sched.h> int idx=0; int fddata; void handle(int s) { int status; if(s==SIGCHLD){//进程结束信号 wait(&status); idx++; close(fddata); printf("任务完成!\n"); if(idx==2) exit(0); } } int isprimer(int ta)//判断素数 { int i; for(i=2;i<ta;++i){ if(ta%i==0){ return 0; } } return 1; } int main(int argc, const char *argv[]) { int a,b; int id=1; int fd[2]; pipe(fd); signal(SIGCHLD,handle); while(1) { if(id==1){ //让子进程1查找2~5000的素数 a=2; b=5000; }else if(id==2){//让子进程2查找5000~10000的素数 a=5001; b=10000; } if(fork()){//父进程:创建子进程,保存有子进程传来的数据.这里是为了创建2个子进程 id++; if(id>2){ break; } continue; }else{ //子进程 close(fd[0]); int i; for(i=a;i<=b;++i){ if(isprimer(i)){ write(fd[1],&i,4); } sched_yield();//告诉系统,我不执行了,把cpu调度给其他进程,把它拍到进程队列的最后 } printf("%d退出\n",getpid()); exit(0); } } int re; char buf[20]; close(fd[1]); fddata=open("result.txt",O_RDWR|O_CREAT,0666); while(1) //父进程:这里为了保留子进程传来的数据 { read(fd[0],&re,4); sprintf(buf,"%d\n",re); write(fddata,buf,strlen(buf));//是strlen不要搞错成sizeof printf("%s",buf); sched_yield();//告诉系统,我不执行了,把cpu调度给其他进程,把它拍到进程队列的最后 } return 0; }