管道是针对对本地计算机的两个进程之间的通讯而设计的通讯方式,管道创建后,实际得到两个文件描述符,一个读取另外一个写入。最多见的IPC机制,经过PIPE系统调用。管道是单工的,数据只能向一个方向流动,须要双向通讯时,须要创建起两个管道。管道的本质是内核中的缓存。linux
#include <unistd.h> #include <stdio.h> #include <stdlib.h> /** *Desc:扇形多线程之间管道通讯 *author:xiao_dingo *since:2018-03-07 *email:wwc0524@163.com */ char *cmd1[3] = {"/bin/cat","/etc/passwd",NULL}; char *cmd2[3] = {"/bin/grep","root",NULL}; int main(void){ int fd[2]; if(pipe(fd) < 0){ perror("pipe error"); } int i = 0; pid_t pid; for(;i < 2; i++){ pid = fork(); if(pid < 0){ perror("fork error"); exit(1); }else if(pid == 0){//child process if(i == 0){ close(fd[0]); //将标准输出重定向到管道的写端 if(dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO){ perror("dup2 error"); } close(fd[1]); if(execvp(cmd1[0],cmd1) < 0){ perror("execvp error"); exit(1); } break; } if(i == 1){ close(fd[1]); //将标准输入重定向到管道的读端 if(dup2(fd[0],STDIN_FILENO) != STDIN_FILENO){ perror("dup2 error"); } close(fd[0]); if(execvp(cmd2[0],cmd2) < 0){ perror("execvp error"); exit(1); } break; } }else{//parent process if(i == 1){ close(fd[0]); close(fd[1]); wait(NULL); wait(NULL); } } } exit(0); }
#include <unistd.h> #include <string.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <signal.h> /** *Desc:不完整管道之间操做 *author:xiao_dingo *since:2018-03-08 *email:wwc0524@163.com */ void sig_handler(int signo){ if(signo == SIGPIPE){ printf("sigpipe occured\n"); } } void main(void){ int fd[2]; if(pipe(fd) < 0){ perror("pipe error"); exit(1); } pid_t pid; if((pid = fork()) < 0){ perror("fork error"); exit(1); }else if(pid > 0){//parent process sleep(5); close(fd[0]); if(signal(SIGPIPE,sig_handler) == SIG_ERR){ perror("signal sigpipe error"); exit(1); } char *s = "1234"; if(write(fd[1],s,sizeof(s)) != sizeof(s)){ fprintf(stderr,"%s,%s\n",strerror(errno),(errno == EPIPE) ? "EPIPE" : ",UNKNOW"); } }else{//child process close(fd[0]); close(fd[1]); } }
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char* pathname,mode_t mode);
System v IPC对象(消息队列,共享内存和信号量)存在于内核中而不是文件系统中,由用户控制释放(用户管理IPC对象的生命周期),不像管道和释放由内核控制。IPC对象经过标识符来引用和访问,全部的IPC对象在内核空间有惟一标识ID,在用户空间的惟一标识称为Key缓存
#include <sys/msg.h> int msgget(key_t key,int flag);//查询 int msgctl(int msgid,int cmd,struct msgid_ds *buf);//控制 int msgsnd(int magid,const void *ptr,szie_t nbytes,int flag);//发送 ssize_t msgrvc(int msgqid,void *ptr,size_t nbytes,long type,int flag);//接收
共享内存容许系统内两个或多个进程共享同一块内存空间,而且数据不用在客户进程和服务器进程间复制,所以共享内存是通讯速度最快的一种IPC。
实现的机制简单描述以下:一个进程在系统中申请开辟了一块共享内存空间,而后使用这个共享内存空间的各个进程分别打开这个共享内存空间,并将这个内存空间映射到本身的进程空间上,这样各个进程就能够共同使用这个共享内存空间,就如同使用本身进程地址空间的内存同样
要实现共享内存空间,内核作了许多工做:好比给每一个共享内存块分发一个“身份证”、容许用户进程将共享内存映射到各自的地址空间上、在进程提出申请之后将共享内存和进程地址空间脱离,并在适当的时候讲共享内存删除,让其回到能够被建立的状态。
用户利用共享内存实现进程间的通讯,实际上就是使用内核提供的服务完成对共享内存的创建、映射、脱离、删除等。当创建并映射成功之后,进程间就能经过共享内存实现数据的交互。服务器
/** *shmget实现共享内存的创建或者打开 *当共享内存的键值key 还没有存在时,调用这个函数而且指定shmflg 参数为IPC_CREAT 能够建立一个大小为 size 的共享内存空间。假设key指定的共享内存已经存在,调用这个函数能够打开这个共享内存,但不会建立。 * */ int shmget(key_t key, size_t size, int shmflg); /** *该函数将一个共享内存空间映射到调用进程的地址空间上,而且返回在进程地址空间中的地址。用户拿到改地址后就能够经过这个地址间接的访问共享内存。 *shmid 参数就是shmget 函数的返回值,shmaddr 参数其实是指出了共享内存映射到进程地址空间上的位置,可是咱们通常不会指定这个地址,而是令其为NULL ,让内核选择一个合适的地址。shmflg 参数是配合着shmaddr 参数使用的,在shmaddr 为NULL时就变得没有实际意义,所以一般指定为0 */ void *shmat(int shmid,const void* shmaddr,int shmflg); /** *这个函数将一个进程已经映射了的共享内存脱离进程地址空间。shmaddr 参数就是在进程地址空间的地址,实际就是shmat 函数的返回值 */ int shmdt(const void* shmaddr); /** *此函数实际上有不少的功能,可是咱们通长用它将一个已经建立了的共享内存删除,所谓删除实际就是将它放回能够被建立的共享内存队列中。指定cmd 参数为IPC_RMID,就能够将shmid键值指定的共享内存删除,而buf其实是能够获取这共享内存在内核中的状态,若是不想了解能够指定为0 */ int shmctl(int shmid, int cmd, struct shmid_ds *buf);
用于进程间的huchi与同步,每种共享资源对应一个信号量,为了便于大量共享资源的操做引入了信号量集,可对全部信息量一次性操做,对信号量集中全部操做能够要求所有成功,也能够部分红功。
它是一个特殊变量,只容许对它进行等待和发送信号这两种操做。多线程
#include <sys/sem.h> /** * * */ int semget(key_t key,int nsems,int semflg);