进程间通讯

进程间通讯 1.进程间通讯概述进程间通讯---IPC2.管道通讯原理IPC的方式通常有管道包括无名管道和命名管道、消息队列、信号量、共享存储、Socket、Streams等。其中Socket和Streams支持不同主机上的两个进程IPC。一.管道管道通常指无名管道是UNIX系统IPC最古老的形式。1.特点1.它是半双工的具有固定的读端和写端。----管道中的数据读走就没了2.它只能用于具有亲缘关系的进程之间通信。3.它可以看成一种特殊文件对于它的读写可以使用普通的read、write等函数。但他不是普通文件并不属于任何文件系统并且只存于内存中。2.原型#include unistd.h int pipe(int pipefd[2]);//成功返回0失败返回-1当一个管道建立时它会创建两个文件描述符fd[0]为读而打开fd[1]为写而打开。要关闭管道只需将这两个文件描述符关闭即可若要数据从父进程流向子进程则需关闭父进程的读端fd[0]与子进程的写端fd[1]反之则子流向父。3.管道编程实战代码如下#include stdio.h #include unistd.h #include string.h #include stdlib.h int main() { int fd[2]; int pid; char readBuf[128]; // int pipe(int pipefd[2]); if(pipe(fd) -1) { printf(pipe create failed\n); } pid fork(); if(pid 0) { printf(create child failed\n); } else if(pid 0) { printf(this is father\n); close(fd[0]); write(fd[1],hello from father,strlen(hello from father)); wait(NULL); } else if(pid 0) { printf(this is child\n); close(fd[1]); read(fd[0],readBuf,128); printf(read from father,readBuf:%s\n,readBuf); exit(0); } return 0; }运行结果4.创建命名管道二.FIFOFIFO也称为命名管道它是一种文件类型。1.特点1.FIFO可以在无关的进程之间交换数据与无名管道不同。2.FIFO与路径名与之相关联它以一种特殊设备文件形式存在于文件系统中。2.原型#include sys/types.h #include sys/stat.h int mkfifo(const char *pathname, mode_t mode);//返回值成功返回0失败返回-1其中mode参数与open函数中的mode相同。一旦创建了一个FIFO就可以用一般的文件I/O函数操作它。#include sys/types.h #include sys/stat.h #include stdio.h #include errno.h int main() { if((mkfifo(./file,0600) -1) (errno EEXIST)) { printf(mkfifo failure!\n); perror(why); } return 0; }5.命名管道的数据通讯编程实现#include sys/types.h #include sys/stat.h #include stdio.h #include errno.h #include fcntl.h int main() { if((mkfifo(./file,0600) -1) (errno EEXIST)) { printf(mkfifo failure!\n); perror(why); } int fd open(./file,O_RDONLY); printf(open success!\n); return 0; }运行结果如下发现程序阻塞了。当open一个FIFO时是否设置非阻塞标志O_NONBLOCK的区别1.若没有指定O_NONBLOCK默认只读open要阻塞到某个其他进程为写而打开此FIFO类似的只写open要阻塞到某个其他进程为读而打开它。2.若指定了O_NONBLOCK则只读open立即返回。而只写open将出错返回-1如果没有进程已经为读而打开该FIFO其errno置ENXIO。write#include sys/types.h #include sys/stat.h #include stdio.h #include errno.h #include fcntl.h #include string.h int main() { char *str message from fifo; int fd open(./file,O_WRONLY); printf(write open success!\n); while(1) { write(fd,str,strlen(str)); sleep(1); } close(fd); return 0; }read#include sys/types.h #include sys/stat.h #include stdio.h #include errno.h #include fcntl.h int main() { char buf[30]; int nread; if((mkfifo(./file,0600) -1) (errno ! EEXIST)) { printf(mkfifo failure!\n); perror(why); } int fd open(./file,O_RDONLY); printf(open success!\n); while(1) { nread read(fd,buf,30); printf(read %d byte from fifo,context:%s\n,nread,buf); } close(fd); return 0; }只有当write打开之后read才能继续执行6.消息队列的通讯原理三.消息队列消息队列是消息的链接表存放在内核中。一个消息队列由一个标识符来标识。1.特点1.消息队列是面向记录的其中的消息具有特定的格式以及特定的优先级。2.消息队列独立于发送与接收过程。进程终止时消息队列及其内容不会被删除。3.消息队列可以实现消息的随机查询消息不一定要以先进先出的次序读取也可以按消息的类型读取。7.消息队列相关api2.原型#include sys/msg.h //创建或打开消息队列:成功返回队列0失败返回队列-1 int msgget(key_t key, int msgflg); //添加消息:成功返回0失败返回-1 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); //读取消息:成功返回消息数据的长度失败返回-1 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); //控制消息队列:成功返回0失败返回-1 int msgctl(int msqid, int cmd, struct msqid_ds *buf);8.消息队列编程收发数据代码接收#include stdio.h #include sys/types.h #include sys/ipc.h #include sys/msg.h #include string.h // int msgget(key_t key, int msgflg); // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); // int msgctl(int msqid, int cmd, struct msqid_ds *buf); struct msgbuf { long mtype; /* message type, must be 0 */ char mtext[128]; /* message data */ }; int main() { //huo qu struct msgbuf readBuf; struct msgbuf sendBuf {988,thank you for reach}; int msgId msgget(0x1235,IPC_CREAT|0777); if(msgId -1) { printf(get que failure!\n); } msgrcv(msgId,readBuf,sizeof(readBuf.mtext),888,0); printf(read from que:%s\n,readBuf.mtext); msgsnd(msgId,sendBuf,strlen(sendBuf.mtext),0); return 0; }发送#include stdio.h #include sys/types.h #include sys/ipc.h #include sys/msg.h #include string.h // int msgget(key_t key, int msgflg); // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); // int msgctl(int msqid, int cmd, struct msqid_ds *buf); struct msgbuf { long mtype; /* message type, must be 0 */ char mtext[128]; /* message data */ }; int main() { //huo qu struct msgbuf sendBuf {888,this is message from quen}; struct msgbuf readBuf; int msgId msgget(0x1235,IPC_CREAT|0777); if(msgId -1) { printf(get que failure!\n); } msgsnd(msgId,sendBuf,strlen(sendBuf.mtext),0); printf(send over\n); memset(readBuf.mtext,128,\0); msgrcv(msgId,readBuf,sizeof(readBuf.mtext),988,0); printf(return from Get:%s\n,readBuf.mtext); return 0; }9.键值生成及消息队列移除key_t ftok(const char *pathname, int proj_id);其中参数fname是指定的文件名这个文件必须是存在的而且可以访问的。id是子序号它是一个8bit的整数。即范围是0~255。当函数执行成功则会返回key_t键值否则返回-1。在一般的UNIX中通常是将文件的索引节点取出然后在前面加上子序号就得到key_t的值。添加代码如下添加消息队列key_t key; key ftok(.,z); printf(key %x\n,key); int msgId msgget(key,IPC_CREAT|0777);删除消息队列msgctl(msgId,IPC_RMID,NULL);10.共享内存概述使用步骤创建共享内存/打开 --- 映射 --- 数据交换 --- 释放共享内存 --- 干掉共享内存11.共享内存编程实现共享内存相关api//创建或获取一个共享内存成功返回共享内存ID失败返回-1 int shmget(key_t key, size_t size, int shmflg); //连接共享内存到当前进程的地址空间成功返回指向共享内存的指针失败返回-1 void *shmat(int shmid, const void *shmaddr, int shmflg); //断开与共享内存的连接成功返回0失败返回-1 int shmdt(const void *shmaddr); //控制共享内存的相关信息成功返回0失败返回-1 int shmctl(int shmid, int cmd, struct shmid_ds *buf);读#include sys/ipc.h #include sys/shm.h #include stdlib.h #include stdio.h #include string.h // int shmget(key_t key, size_t size, int shmflg); // void *shmat(int shmid, const void *shmaddr, int shmflg); int main() { int shmid; char *shmaddr; key_t key; key ftok(.,1); shmid shmget(key,1024*4,0); if(shmid -1) { printf(shmget no ok!\n); exit(-1); } shmaddr shmat(shmid,0,0); printf(shmat ok\n); printf(data:%s\n,shmaddr); sleep(5); shmdt(shmaddr); printf(quit\n); return 0; }写#include sys/ipc.h #include sys/shm.h #include stdlib.h #include stdio.h #include string.h // int shmget(key_t key, size_t size, int shmflg); // void *shmat(int shmid, const void *shmaddr, int shmflg); int main() { int shmid; char *shmaddr; key_t key; key ftok(.,1); shmid shmget(key,1024*4,IPC_CREAT|0666); if(shmid -1) { printf(shmget no ok!\n); exit(-1); } shmaddr shmat(shmid,0,0); printf(shmat ok\n); strcpy(shmaddr,hello world); sleep(5); shmdt(shmaddr); shmctl(shmid,IPC_RMID,0); printf(quit\n); return 0; }查看系统中有哪些共享内存ipcs -m删除系统中共享内存12.信号概述对于Linux来说实际信号是软中断许多重要的程序都需要处理信号。信号为Linux提供了一种处理霁步事件的方法。比如终端用户输入了ctrlc来中断程序会通过信号机制停止一个程序。信号的处理有三种方法分别是:忽略、捕捉和默认动作13.信号编程信号处理函数的注册1.入门版函数 signal2.高级版函数 sigaction信号处理发送函数1.入门版kill2.高级版sigqueue*************************************************kill -l*************************************************让 ctrl c 无效的程序原因分析信号处理函数在捕捉到信号时候让其执行 handler() 函数程序。使用如下方式结束进程判断 ctrl C 和 kill 9 和 kill 10 可不可以结束进程程序如下#include signal.h #include stdio.h // typedef void (*sighandler_t)(int); // sighandler_t signal(int signum, sighandler_t handler); void handler(int signum) { printf(get signum %d\n,signum); switch(signum) { case 2: printf(SIGINT\n); break; case 9: printf(SIGKILL\n); break; case 10: printf(SIGUSR1\n); break; } printf(never qiut\n); } int main() { signal(SIGINT,handler); signal(SIGKILL,handler); signal(SIGUSR1,handler); while(1); return 0; }运行结果如下自己编程杀死进程代码#include signal.h #include stdio.h #include sys/types.h #include string.h // typedef void (*sighandler_t)(int); // sighandler_t signal(int signum, sighandler_t handler); int main(int argc,char **argv) { int signum; int pid; char cmd[128] {0}; signum atoi(argv[1]); pid atoi(argv[2]); printf(num %d,pid %d\n,signum,pid); // 法一 // kill(pid,signum); // // 法二 // sprintf(cmd,kill -%d %d,signum,pid); // system(cmd); // // // printf(send signal ok\n); return 0; }16.信号量概述信号量与已经介绍过的IPC结构不同它是一个计数器。信号量用于实现进程间的互斥与同步而不是用于存储进程间通信数据。1.特点1.信号量用于进程间同步若要在进程间传递数据需要结合共享内存。2.信号量基于操作系统的PV操作程序对信号量的操作都是原子操作。3.每次对信号量的PV操作不仅限于对信号量值加1减1而且可以加减任意正整数。4.支持信号量组。2.原型最简单的信号量是只能取0和1的变量这也是信号量最常见的一种形式叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。Linux下的信号量函数都是在通用的信号量数组上进行操作而不是在一个单一的二值信号量上进行操作。//创建或获取一个信号量组若成功返回信号量集ID失败返回-1 int semget(key_t key, int nsems, int semflg); //对信号量组进行操作改变信号量的值成功返回0失败返回-1 int semop(int semid, struct sembuf *sops, unsigned nsops); //控制信号量的相关信息 int semctl(int semid, int semnum, int cmd, ...);