进程间通信的方法和实现
linux 中进程通信实现
通信方式:管道 消息队列 共享内存 信号量 套接口
1.管道
包括无名管道和有名管道,前者用于父子进程的通信,后者用于运行于同一台
机器上的任意两个进程间的通信。
1)无名管道
创建:#include<unistd.h>
int pipe(int pipefd[2])
参数:pipefd[0]为读而打开,pipefd[1]为写而打开。
例:
#include<unistd.h> #include<sys/types.h> #include<stdio.h> #include<stdlib.h> #include<string.h> void main() { int pidfd[2]; char buf[256]; int fd=pipe(pidfd); if(fd<0) { printf("pipe error\n"); exit(-1); } pid_t pid; if((pid=fork())==-1) { printf("error fork\n"); exit(-1); } if(pid == 0) { printf("I am child process!\n"); close(pidfd[0]); write(pidfd[1],"hello ipc",strlen("hello ipc")+1); exit(0); } else { printf("I am father process!\n"); close(pidfd[1]); read(pidfd[0],buf,sizeof(buf)); printf("%s\n",buf); } }
说明:关闭打开管道,只是相对于自己的进程号来看的。
但是系统创建的管道,由于字节是依赖于系统定义的,使用流管道,就会突破系统
的限制。
讲解函数:
例:
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<fcntl.h> #define BUFSIZE 1024 int main() { FILE *fp; char *cmd = "ps -ef"; char buf[BUFSIZE]; buf[BUFSIZE]='\0'; if((fp = popen(cmd,"r"))==NULL) perror("popen"); while((fgets(buf,BUFSIZE,fp)) != NULL) printf("%s",buf); pclose(fp); return 0; }
说明:FILE *popen(const char *command,const char *type),
int pclose(FILE * stream),个人感觉,就是文件IO差不多
但是,这样使用了流管道而已。
2)命名管道(FIFO)
不同祖先的进程之间可以通过命名管道共享数据
命名管道创建和操作
命名管道的创建
#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
创建成功为0 ,出错为-1
操作步骤:
1.open
2.read/write
3.close
4.unlink
int unlink(const char *pathname)
功能:可以删除文件,包括链接文件。
向管道写文件进程
#include<stdio.h> #include<fcntl.h> #include<errno.h> #include<stdlib.h> #include<sys/types.h> #include<unistd.h> typedef struct { char name[20]; int age; }person_t; void main() { int ret = mkfifo("fifo",0644); if(ret<0) { perror("mkfifo"); exit(-1); } int fd = open("fifo",O_WRONLY); if(fd<0) { perror("open"); exit(-1); } person_t x; puts("input name age:"); scanf("%s%d",x.name,&x.age); write(fd,&x,sizeof(x)); close(fd); } 读管道文件内容 #include<stdio.h> #include<fcntl.h> #include<stdlib.h> #include<sys/types.h> #include<errno.h> #include<unistd.h> typedef struct { char name[20]; int age; }person_t; int main(int argc ,char **argv) { if(argc != 2) { printf("%s argument!\n",argv[0]); exit(-1); } person_t y; int fd = open(argv[1],O_RDONLY); if(fd<0) { perror("open"); exit(-1); } int ret = read(fd,&y,sizeof(y)); if(ret<0) { perror("read"); exit(-1); } printf("name :%s age : %d\n",y.name,y.age); close(fd); unlink(argv[1]); } 说明:运行的时候,两个进程同时运行。
2.消息队列
消息队列用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中
用来保存消息的队列,它在西戎内核中是以消息链表的形式出现。消息链表中节点的结构用msg
声明。 实例:
write: #include<sys/types.h> #include<stdlib.h> #include<fcntl.h> #include<unistd.h> #include<sys/stat.h> #include<sys/ipc.h> #include<stdio.h> typedef struct { long type; char name[20]; int age; }msg_t; int main() { key_t key = ftok("/home",'a'); int msgid = msgget(key,IPC_CREAT|O_WRONLY|0777); if(msgid<0) { perror("msgget"); exit(-1); } msg_t m; puts("input :type name age:"); scanf("%ld%s%d",&m.type,m.name,&m.age); msgsnd(msgid,&m,sizeof(m)-sizeof(m.type),0); return 0; } read: #include<sys/types.h> #include<sys/types.h> #include<fcntl.h> #include<sys/ipc.h> #include<stdio.h> #include<stdlib.h> typedef struct { long type; char name[20]; int age; }msg_t; int main() { key_t key = ftok("/home",'a'); int msgid = msgget(key,O_RDONLY); if(msgid<0) { perror("msgid"); exit(-1); } msg_t m; puts("input :type"); scanf("%ld",&m.type); printf("m.type:%d\n",m.type); msgrcv(msgid,&m,sizeof(m)-sizeof(m.type),m.type,0); printf("name:%s age:%d\n",m.name,m.age); return 0; } 说明:两个程序必须同时启动,要不然接收不到msg,创建和使用消息队列的基本步骤总结, 发送端:1.ftok 接收端:1.ftok 必须使用统一类型才能接收到。 2.msgsnd 2.msgrcv
3.共享内存(最快的)
共享内存时运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间
复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。得到共享内
存有两种方式:
1).映射/dev/mem 设备
2).内存映像文件
此处重点讲一下内存映像文件方式:
shmget--shmat--shmdt--shmctl
创建 挂接 解除 删除
write: #include<sys/shm.h> #include<sys/ipc.h> #include<errno.h> #include<stdlib.h> #include<stdio.h> #include<string.h> int main() { key_t key = ftok("/home",'b'); int shmid = shmget(key,100,IPC_CREAT|0777); if(shmid<0) { perror("shmget"); exit(-1); } char *p = (char*)shmat(shmid,NULL,0); if(p==(void *)-1) { perror("shmat"); exit(-1); } strcpy(p,"welcome to beijing!"); shmdt(p); return 0; } 说明:一个进程创建内存 int shmget(key_t key, size_t size, int shmflg); 挂接 void *shmat(int shmid, const void *shmaddr, int shmflg); 解除 int shmdt(const void *shmaddr); 删除 int shmctl(int shmid, int cmd, struct shmid_ds *buf); read: #include<stdio.h> #include<sys/shm.h> #include<stdlib.h> #include<sys/ipc.h> #include<errno.h> int main() { key_t key = ftok("/home",'b'); int shmid = shmget(key,0,0); if(shmid<0) { perror("shmget"); exit(-1); } char *p= (char *)shmat(shmid,NULL,SHM_RDONLY); if(p==(void*)-1) { perror("shmat"); exit(-1); } puts(p); shmdt(p); shmctl(shmid,IPC_RMID,NULL); return 0; } 4.信号 信号处理三个方式: 1.忽略 2.捕捉 3.默认 信号登记: typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler) 例:#include<stdio.h> #include<string.h> #include<signal.h> void deal_fun(int signo) { switch(signo) { case SIGINT:puts("1"); break; case SIGTERM:puts("2"); break; case SIGTSTP:puts("3"); break; default:puts("4"); break; } } int main() { signal(SIGINT,deal_fun); signal(SIGTERM,deal_fun); signal(SIGTSTP,deal_fun); for(;;) { write(1,".",1); sleep(3); } return 0; }