进程间通信
无名管道
无名管道用于在在亲缘关系的进程间通信(父子进程或兄弟进程),它是一种半双工的工作模式。
在父进程中创建无名管道后,又创建子进程,则父子进程中分别拥有独立的读端和写端。所以要把父进程的写端 fd[1]和子进程的读端 fd[0]关闭。这时,父子进程之间就建立起了一条“子进程写入父进程读”的通道。
数据保存在无名管道中,而无名管道在内核中。
无名管道、消息队列、信号量都位于内核,共享内在位于用户空间。
#include<unistd.h> #include<string.h> #include<sys/types.h> #include<error.h> #include<stdio.h> #include<stdlib.h> int main(){ int pipe_fd[2]; pid_t pid; char buff1[100]; int len; memset(buff1,0x00,sizeof(buff1)); //创建管道 if(pipe(pipe_fd)<0){ perror("pipe"); exit(1); } //创建子进程。父进程中打开的文件、IPC对象等在子进程中也可以使用 if((pid=fork())==0){ //在子进程中 close(pipe_fd[1]); //关闭写端 sleep(1); //等待父进程写入数据 if((len=read(pipe_fd[0],buff1,sizeof(buff1)))>0) printf("%d numbers read from the pipe is %s\n",len,buff1); close(pipe_fd[0]); //关闭读端 exit(0); } else if(pid>0){ //在父进程中 close(pipe_fd[0]); //关闭读端 if((write(pipe_fd[1],"hello",5))!=-1) printf("parent write success!\n"); if((write(pipe_fd[1],"pipe",4))!=-1) printf("parent write success!\n"); close(pipe_fd[1]); //关闭写端 sleep(1); waitpid(pid,NULL,2); exit(0); } //int main()函数可以没有返回值 }
输出:
parent write success!
parent write success!
9 numbers read from the pipe is hellopipe
#include <stdio.h> FILE *popen(const char *command, const char *type)
type--“r”:文件指针连接到 command 的标准输出,即该命令的结果产生输出;“w”:文件指针连接到 command 的标准输入,即该命令的结果产生输入
int pclose(FILE *stream)
#include<stdio.h> #include<stdlib.h> #include<fcntl.h> #define MAX 32700 int main() { FILE *f; char buff[MAX]; const char *cmd = "ps -ef"; if ((f = popen(cmd, "r")) == NULL) { perror("popen"); exit(1); } while (fgets(buff, MAX, f)) { printf("%s", buff); } pclose(f); }
有名管道
有名管道依托于文件,严格遵从先进先出,不能使用lseek()等随机读写函数。
由于普通文件的读写时不会出现阻塞问题,而在管道的读写中却有阻塞的可能,这里的非阻塞标志可以在 open 函数中设定为 O_NONBLOCK。
当管道已满时,对于写进程
• 若该管道是阻塞打开,则写进程而言将一直阻塞直到有读进程读出数据。
• 若该管道是非阻塞打开,则当前 FIFO 内没有读操作,写进程都会立即执行写操作。
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode);
创建了有名管道pathname,就相当于创建了一个文件pathname。对有名管道的读写就使用读写普通文件的那几个函数就可以了。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> #include<error.h> #define oops(m,x){ \ perror(m); \ exit(x); \ } int main(){ char *file="/home/orisun/today"; if((mkfifo(file,0644))==-1) //创建有名管道 oops("mkfifo",1); int fd; if((fd=open(file,O_RDWR|O_NONBLOCK,0644))==-1) //打开有名管道 oops("open",1); char *str="hust"; char buf[10]={0}; if((write(fd,str,strlen(str)))<0) //向管道中写入数据 oops("write",1); if((read(fd,buf,sizeof(buf)))<0) //从管道中读取数据 oops("read",1); printf("read:%s\n",buf); unlink(file); //删除有名管道 }
共享内存
1.创建共享内存--shmget。即从内存中获得一片共享区域
2.映射共享内存--shmat。把共享内存映射到具体的进程空间中去
(此时就可以使用不带缓冲的I/O读写命令对其进行操作了)
3.撤销映射--shmdt
1.#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key,int size,int shmflg)
Key:IPC_PRIVATE
Size:共享内存区大小
Shmflg:同 open 函数的权限位,也可以用八进制表示法
函数返回值
成功:共享内存段标识符
出错:−1
2.char *shmat(int shmid,const void *shmaddr,int shmflg)
shmid:要映射的共享内存区标识符
shmaddr:将共享内存映射到指定位置(若为 0 则表示把该段共享内存映射到调用进程的地址空间)
Shmflg:
SHM_RDONLY:共享内存只读
默认 0:共享内存可读写
函数返回值
成功:被映射的段地址
出错:−1
#include<sys/shm.h> #include<sys/ipc.h> #include<sys/types.h> #include<stdio.h> #include<stdlib.h> #define BUFSZ 2048 int main() { int shmid; char *shmadd; /*建立共享内存 */ if ((shmid = shmget(IPC_PRIVATE, BUFSZ, 0600)) == -1) { perror("shmget"); exit(1); } else { printf("create shmid=%d\n", shmid); system("ipcs -m"); //system()函数可以在程序中执行shell命令,"ipcs -m"表示查看共享内存段的使用情况 } /*映射共享内存 */ if ((shmadd = shmat(shmid, 0, 0)) < (char*)0) { perror("shmat"); exit(1); } else { printf("share memory attached\n"); system("ipcs -m"); } /*撤销共享内存 */ if (-1 == (shmdt(shmadd))) { perror("shmdt"); exit(1); } printf("share memory detached\n"); system("ipcs -m"); /*删除共享内存*/ if(shmctl(shmid,IPC_RMID,NULL)<0){ perror("shmctl"); exit(1); } printf("delete share memory\n"); system("ipcs -m"); return 0; }
输出:
create shmid=55672849
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 55869463 orisun 600 10 0
share memory attached
0x00000000 55869463 orisun 600 10 1
share memory detached
0x00000000 55869463 orisun 600 10 0
delete share memory
nattch表示IPC对象的访问次数
消息队列
ftok原型如下:
key_t ftok( char * fname, int id )
fname就时你指定的文件名,id是子序号。
#include<sys/types.h> #include<sys/ipc.h> #include<sys/shm.h> #include<string.h> #include<stdio.h> #include<stdlib.h> #define MSGLEN 1024 struct message { long msg_type; char msg_cont[MSGLEN]; }; int main() { struct message msg, msg2; int queueid; int len; key_t key; if ((key = ftok(".", 'a')) == -1) { perror("tfok"); exit(1); } /*创建消息队列 */ if ((queueid = msgget(key, IPC_CREAT | 0666)) == -1) { perror("msgget"); exit(1); } printf("message queue created.\n"); puts("please input the message you add to the queue:"); //puts和fgets都是行读写函数。puts会在行后自动加上换行符'\n' if ((fgets((&msg)->msg_cont, MSGLEN, stdin)) == NULL) { //fgets会自动在读取的消息后面加一个'\n'然后才写入缓冲区。具体情况看运行结果截图 perror("fgets"); exit(1); } (&msg)->msg_type = getpid(); len = strlen(msg.msg_cont); //strlen得到字符串的真实长度,不包括末尾的'\0' /*将消息添加到队尾 */ if ((msgsnd(queueid, (&msg), len, 0)) < 0) { //第3个参数是消息的长度,要求不能以NULL结尾 perror("message posted"); exit(1); } /*从队列中接收消息 */ if ((msgrcv(queueid, (&msg2), MSGLEN, msg.msg_type, 0)) == -1) { perror("msgrcv"); exit(1); } printf("received message is %s\n", msg2.msg_cont); /*从内核中移除消息队列 */ if ((msgctl(queueid, IPC_RMID, NULL)) == -1) { perror("msgctl"); exit(1); } return 0; }
本文来自博客园,作者:高性能golang,转载请注明原文链接:https://www.cnblogs.com/zhangchaoyang/articles/1942572.html