- 浏览: 1637624 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (405)
- C/C++ (16)
- Linux (60)
- Algorithm (41)
- ACM (8)
- Ruby (39)
- Ruby on Rails (6)
- FP (2)
- Java SE (39)
- Java EE (6)
- Spring (11)
- Hibernate (1)
- Struts (1)
- Ajax (5)
- php (2)
- Data/Web Mining (20)
- Search Engine (19)
- NLP (2)
- Machine Learning (23)
- R (0)
- Database (10)
- Data Structure (6)
- Design Pattern (16)
- Hadoop (2)
- Browser (0)
- Firefox plugin/XPCOM (8)
- Eclise development (5)
- Architecture (1)
- Server (1)
- Cache (6)
- Code Generation (3)
- Open Source Tool (5)
- Develope Tools (5)
- 读书笔记 (7)
- 备忘 (4)
- 情感 (4)
- Others (20)
- python (0)
最新评论
-
532870393:
请问下,这本书是基于Hadoop1还是Hadoop2?
Hadoop in Action简单笔记(一) -
dongbiying:
不懂呀。。
十大常用数据结构 -
bing_it:
...
使用Spring MVC HandlerExceptionResolver处理异常 -
一别梦心:
按照上面的执行,文件确实是更新了,但是还是找不到kernel, ...
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法 -
dsjt:
楼主spring 什么版本,我的3.1 ,xml中配置 < ...
使用Spring MVC HandlerExceptionResolver处理异常
进程间通信IPC:
我们以前介绍过进程控制原语,看到怎么创建多个进程。但是进程之间交互信息的方式只介绍了通过
fork或者exec继承父进程的打开文件或者通过文件系统。
经典的进程通信方式有:管道、FIFOs,消息队列,信号灯和共享内存。
1、管道:
管道是Unix系统IPC最古老的形式,PIPE有以下限制:
1)是半双工的,一些系统提供了全双工的管道,但是为了可移植性,我们最好不要作此假设。
2)管道只能在有共同祖先的进程之间。一般一个进程创建一个管道,然后进程调用fork,
然后管道在父进程和子进程之间使用。
FIFO摆脱了第二个限制,Unix domain socket和具名STREAMS-based管道可以摆脱这两者的限制。
尽管有以上限制,半双工的管道仍然是最常使用的IPC。
1)创建管道:
调用成功之后,通过filedes返回了两个文件描述符:filedes[0]被打开可以读取,filedes[1]被打开可以写,
filedes[1]的输出时filedes[0]的输入,fstat函数返回这两个文件描述符是FIFO,通过宏S_ISFIFO来判断
是否是一个管道。
一个进程的管道几乎没有用处,通常一个进程调用pipe创建管道,然后调用fork,在父子进程之间创建IPC通道。
从父进程到子进程的管道,父进程关闭读端的管道(filedes[0]),子进程关闭写端的管道(filedes[1]);从
子进程到父进程的管道,父进程关闭filedes[1],子进程关闭filedes[2].
当一端的管道关闭:
1)如果我们从一个写端已经关闭的管道读取,当所有的数据都已经读完,read返回0,指示文件结束。
2)如果我们从一个读段已经关闭的管道进行写操作,将产生SIGPIPE信号。当我们忽略或者处理它时,write返回
-1,errno设置成EPIPE。
当我们往管道里面写数据的时候,常量PIPE_BUF指示了内核管道的buffer大小,当有多个进程向该管道写时,写小
于等于PIPE_BUF大小的数据,不会交错,大于PIPE_BUF会有可能导致数据的交错。我们可以使用sysconf来查看
PIPE_BUF的大小。
例子:
这个例子创建了一个从父进程到子进程的一个pipe,并发送 了一些数据。
我们复制pipe的文件描述符到标准输入输出,这个会比较有意思,我们经常从运行我们的程序,从标准输入读或者写到
标准输出。
例子:
我们考虑写一个程序,显示一些输出,一次一页,我们可以使用系统自带的分页程序,我们避免把数据写到临时文件中或者
使用system,我们想建立从标准输入到分页的管道来实现。
2)popen和pclose函数:
既然一些常见的操作:比如创建一个从当前进程到另一个进程的pipe,去读它的输出或者向它的输入发送数据,标准的I/O支持popen和pclose
这两个函数为我们帮我们做了很多的脏活累活:创建一个管道,fork一个子进程,关闭没有用的管道的端,执行shell去运行命令,最后等待
命令的执行的终止。
popen执行一个fork和exec去执行cmdstring,返回标准的I/O文件指针。如果type是"r",文件指针和cmdstring的标准输出相连。
如果type是"w",则和cmdstring的标准输入相连。
一个好记的方法是和fopen对比,"r"表示可读,"w"表示可写。
pclose关闭标准I/O,等待命令执行终止,返回shell的执行状态。
我们使用popen来重写上面的例子:
2、FIFOs:
FIFOs经常被称为具名管道。管道只能被有共同祖先的进程之间使用。FIFO可以在不相关的进程之间交换数据。
FIFO是一种文件类型,可以通过S_ISFIFO来测试它。
创建一个FIFO和创建一个文件很相似。
1)创建一个FIFO:
成功返回0,失败返回-1,并设置errno。
例子:
删除FIFO和删除普通文件一样。
例子:
父进程读取子进程写到具名管道的数据:
2)使用FIFO客户端服务端通信:
我们使用简单的协议进行来进行客户端服务端通信,客户端将log信息写到命名管道中,服务端从命名管道中读取
然后写入文件中:
服务端:
客户端:
参考:
1、《Unix system programming》
2、《Advanced Programming in the Unix Environment》
我们以前介绍过进程控制原语,看到怎么创建多个进程。但是进程之间交互信息的方式只介绍了通过
fork或者exec继承父进程的打开文件或者通过文件系统。
经典的进程通信方式有:管道、FIFOs,消息队列,信号灯和共享内存。
1、管道:
管道是Unix系统IPC最古老的形式,PIPE有以下限制:
1)是半双工的,一些系统提供了全双工的管道,但是为了可移植性,我们最好不要作此假设。
2)管道只能在有共同祖先的进程之间。一般一个进程创建一个管道,然后进程调用fork,
然后管道在父进程和子进程之间使用。
FIFO摆脱了第二个限制,Unix domain socket和具名STREAMS-based管道可以摆脱这两者的限制。
尽管有以上限制,半双工的管道仍然是最常使用的IPC。
1)创建管道:
#include <unistd.h> int pipe(int filedes[2]);
调用成功之后,通过filedes返回了两个文件描述符:filedes[0]被打开可以读取,filedes[1]被打开可以写,
filedes[1]的输出时filedes[0]的输入,fstat函数返回这两个文件描述符是FIFO,通过宏S_ISFIFO来判断
是否是一个管道。
一个进程的管道几乎没有用处,通常一个进程调用pipe创建管道,然后调用fork,在父子进程之间创建IPC通道。
从父进程到子进程的管道,父进程关闭读端的管道(filedes[0]),子进程关闭写端的管道(filedes[1]);从
子进程到父进程的管道,父进程关闭filedes[1],子进程关闭filedes[2].
当一端的管道关闭:
1)如果我们从一个写端已经关闭的管道读取,当所有的数据都已经读完,read返回0,指示文件结束。
2)如果我们从一个读段已经关闭的管道进行写操作,将产生SIGPIPE信号。当我们忽略或者处理它时,write返回
-1,errno设置成EPIPE。
当我们往管道里面写数据的时候,常量PIPE_BUF指示了内核管道的buffer大小,当有多个进程向该管道写时,写小
于等于PIPE_BUF大小的数据,不会交错,大于PIPE_BUF会有可能导致数据的交错。我们可以使用sysconf来查看
PIPE_BUF的大小。
例子:
#include <unistd.h> #include <sys/types.h> #include <stdlib.h> int main(void){ int n; int fd[2]; pid_t pid; char line[MAXLINE]; if(pipe(fd) < 0){ perror("pipe"); exit(1); } if((pid = fork()) < 0){ perror("fork"); exit(1); }else if(pid > 0){ close(fd[0]); write(fd[1],"hello,world\n",12); }else{ close(fd[1]); n = read(fd[0],line,MAXLINE); write(STDOUT_FILENO,line,n); } exit(0); }
这个例子创建了一个从父进程到子进程的一个pipe,并发送 了一些数据。
我们复制pipe的文件描述符到标准输入输出,这个会比较有意思,我们经常从运行我们的程序,从标准输入读或者写到
标准输出。
例子:
我们考虑写一个程序,显示一些输出,一次一页,我们可以使用系统自带的分页程序,我们避免把数据写到临时文件中或者
使用system,我们想建立从标准输入到分页的管道来实现。
#include <unistd.h> #include <sys/types.h> #include <stdlib.h> #include <sys/wait.h> #include <stdio.h> #define DEF_PAGER "/bin/more" int main(int argc,char *argv[]){ int n; int fd[2]; pid_t pid; char *pager, *argv0; char line[MAXLINE]; FILE *fp; if(argc != 2){ printf("Usage: a.out <pathname>"); exit(1); } if((fp = fopen(argv[1],"r")) == NULL){ perror("fopen"); exit(1); } if(pipe(fd) < 0){ perror("pipe"); exit(1); } if((pid = fork()) < 0){ perror("fork"); exit(1); }else if(pid > 0){/* parent */ close(fd[0]);/* close read end */ while(fgets(line,MAXLINE,fp) != NULL){ n = strlen(line); if(write(fd[1],line,n) != n){ perror("write"); exit(1); } } if(ferror(fp)){ perror("fgets"); exit(1); } close(fd[1]); /* close write end of the pipe for reader */ if(waitpid(pid,NULL,0) < 0){ perror("waitpid"); exit(1); } exit(0); }else{/* child */ close(fd[1]); if(fd[0] != STDIN_FILENO){ if(dup2(fd[0],STDIN_FILENO) != STDIN_FILENO){ perror("dup2"); exit(1); } close(fd[0]);/* don't need it after dup2 } /* get arguments for execl() */ if((pager = getenv("PAGER")) == NULL){ pager = DEF_PAGER; } if((argv0 = strrchr(pager,'/')) != NULL) argv0++;/* step past rightmost slash */ else argv0 = pager; if(execl(pager,argv0,(char *)0) < 0){ perror("execl"); exit(1); } } exit(1); }
2)popen和pclose函数:
既然一些常见的操作:比如创建一个从当前进程到另一个进程的pipe,去读它的输出或者向它的输入发送数据,标准的I/O支持popen和pclose
这两个函数为我们帮我们做了很多的脏活累活:创建一个管道,fork一个子进程,关闭没有用的管道的端,执行shell去运行命令,最后等待
命令的执行的终止。
#include <stdio.h> FILE *popen(const char *cmdstring, const char *type); int pclose(FILE *fp);
popen执行一个fork和exec去执行cmdstring,返回标准的I/O文件指针。如果type是"r",文件指针和cmdstring的标准输出相连。
如果type是"w",则和cmdstring的标准输入相连。
一个好记的方法是和fopen对比,"r"表示可读,"w"表示可写。
pclose关闭标准I/O,等待命令执行终止,返回shell的执行状态。
我们使用popen来重写上面的例子:
#include <unistd.h> #include <sys/types.h> #include <stdlib.h> #include <sys/wait.h> #include <stdio.h> #define PAGER "${PAGER:-more}" int main(int argc, char *argv){ char line[MAXLINE]; FILE *fpin, *fpout; if(argc != 2){ printf("usage: a.out <pathname>\n"); exit(1); } if((fpin = fopen(argv[1],"r")) == NULL){ perror("fopen"); exit(1); } if((fout = popen(PAGER,"w")) = NULL){ perror("popen"); exit(1); } while(fgets(line,MAXLINE,fpin) != NULL){ if(fputs(line,fpout) == EOF){ perror("fputs"); exit(1); } } if(ferror(fpin)){ perror("fgets"); exit(1); } if(pclose(fpout)){ perror("pclose"); exit(1); } exit(0); }
2、FIFOs:
FIFOs经常被称为具名管道。管道只能被有共同祖先的进程之间使用。FIFO可以在不相关的进程之间交换数据。
FIFO是一种文件类型,可以通过S_ISFIFO来测试它。
创建一个FIFO和创建一个文件很相似。
1)创建一个FIFO:
#include <sys/stat.h> int mkfifo(const char *path, mode_t mode;);
成功返回0,失败返回-1,并设置errno。
例子:
#define FIFO_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S _IROTH) if(mkfifo("myfifo",FIFO_PERMS) == -1) perror("Failed to create myfifo");
删除FIFO和删除普通文件一样。
例子:
父进程读取子进程写到具名管道的数据:
#include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #define BUFSIZE 256 #define FIFO_PERM (S_IRUSR | S_IWUSR) int dofifochild(const char *fifoname, const char *idstring); int dofifoparent(const char *fifoname); int main(int argc,char *argv[]){ pid_t childpid; if(argc != 2){ fprintf(stderr,"Usage: %s pipename\n",argv[0]); return 1; } if(mkfifo(argv,FIFO_PERM) == -1){ if(errno != EEXIT){ fprintf(stderr,"[%ld]": failed to create name pipe %s: %s\n", (long)getpid(),argv[1],strerror(errno)); return 1; } } if(childpid = fork()) == -1){ perror("Failed to fork"); return 1; } if(childpid == 0){ return dofifochild(argv[1],"this was written by the child"); }else{ return dofifochild(argv[1]); } } int dofifochild(const char *fifoname, const char *idstring){ char buf[BUFSIZE]; int fd; int rval; ssize_t strsize; fprintf(stderr,"[%ld]:(child) about to open FIFO %s...\n",(long)getpid(),fifoname); if(fd = open(fifoname,O_WRONLY) == -1){ fprintf(stderr,"[%ld]:failed to open name pipe %s for write: %s\n", (long)getpid(),fifoname,strerror(errno)); return 1; } rval = snprintf(buf,BUFSIZE,"[%ld]:%s\n",(long)getpid(),idstring); if(rval < 0){ fprintf(stderr,"[%ld]": failed to make the string:\n",(long)getpid()); return 1; } strsize = strlen(buf)+1; fprintf(stderr,[%ld]:about to write...\n",(long)getpid()); rval = write(fd,buf,strsize); if(rval != strsize){ fprintf(stderr,"[%ld]:failed to write to pipe: %s\n",(long)getpid(),strerror(errno)); return 1; } fprintf(stderr,"[%ld]:finishing...\n",(long)getpid()); return 0; } int dofifoparent(const char *fifoname) { char buf[BUFSIZE]; int fd; int rval; fprintf(stderr, "[%ld]:(parent) about to open FIFO %s...\n", (long)getpid(), fifoname); if ((fd = open(fifoname, FIFO_MODES)) == -1) { fprintf(stderr, "[%ld]:failed to open named pipe %s for read: %s\n", (long)getpid(), fifoname, strerror(errno)); return 1; } fprintf(stderr, "[%ld]:about to read...\n", (long)getpid()); rval = read(fd, buf, BUFSIZE); if (rval == -1) { fprintf(stderr, "[%ld]:failed to read from pipe: %s\n", (long)getpid(), strerror(errno)); return 1; } fprintf(stderr, "[%ld]:read %.*s\n", (long)getpid(), rval, buf); return 0; }
2)使用FIFO客户端服务端通信:
我们使用简单的协议进行来进行客户端服务端通信,客户端将log信息写到命名管道中,服务端从命名管道中读取
然后写入文件中:
服务端:
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include<sys/stat.h> #define FIFOARG 1 #define BLKSIZE 1024 #define FIFO_PERMS (S_IRWXU | S_IWGRP | S_IWOTH) int main(int argc, char *argv[]){ int requestfd; if(argc != 2){ fprintf(stderr,"Usage: %s fifoname > logfile\n",argv[0]); return 1; } if((mkfifo(argv[FIFOARG],FIFO_PERMS) == -1) && (errno != EEXIST)){ perror("Server failed to create FIFO"); return 1; } if((requestfd = open(argv[FIFOARG],O_RDWR)) == -1){ perror("Server failed to open its FIFO"); return 1; } copyfile(requestfd,STDOUT_FILENO); return 1; } int copyfile(int fromfd, int tofd) { char *bp; char buf[BLKSIZE]; int bytesread, byteswritten; int totalbytes = 0; for ( ; ; ) { while (((bytesread = read(fromfd, buf, BLKSIZE)) == -1) && (errno == EINTR)) ; /* handle interruption by signal */ if (bytesread <= 0) /* real error or end-of-file on fromfd */ break; bp = buf; while (bytesread > 0) { while(((byteswritten = write(tofd, bp, bytesread)) == -1 ) && (errno == EINTR)) ; /* handle interruption by signal */ if (byteswritten <= 0) /* real error on tofd */ break; totalbytes += byteswritten; bytesread -= byteswritten; bp += byteswritten; } if (byteswritten == -1) /* real error on tofd */ break; } return totalbytes; }
客户端:
#include <errno.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <time.h> #include <unistd.h> #include <sys/stat.h> #define FIFOARG 1 int main(int argc, char *argv[]){ time_t curtime; int len; char requestbuf[PIPE_BUF]; int requestfd; if(argc != 2){ fprintf(stderr,"Usage %s fifoname", argv[0]); return 1; } if((requestfd = open(argv[FIFOARG],O_WRONLY)) == -1){ perror("Client failed to open log fifo for writing"); return 1; } curtime = time(NULL) snprintf(requestbuf,PIPE_BUF,"%d:%s",(int)getpid(),ctime(&curtime)); len = strlen(requestbuf); if(write(requestfd,requestbuf,len) != len){ perror("Client failed to write"); return 1; } close(requestfd); return 0; }
参考:
1、《Unix system programming》
2、《Advanced Programming in the Unix Environment》
发表评论
-
【zz】Vim的分屏
2012-04-26 11:13 1546分屏启动Vim 使用大写 ... -
bash的几种for循环
2012-04-26 11:13 17881、罗列式 for VARIABLE in 1 2 3 ... -
【zz】几个简单选项让你的Readline(Bash)更好用
2011-10-10 23:08 2378Reddit上的一位仁兄贴出了他的readline手册学习成果 ... -
pipe in -exec
2011-09-27 22:35 1389一个简单的例子: 将一堆的.txt文件,合并成一个文件,让每个 ... -
[zz]Unix调试的瑞士军刀:lsof
2011-08-31 10:33 1468原文见:http://heikezhi.com/2011/06 ... -
[zz]关于xargs,你可能不知道的
2011-08-29 23:05 2426如果你曾经花了许多时间在Unix命令行上,那么或许你已经见过x ... -
使用scala.sys.process包和系统交互
2011-07-19 00:00 6440在Java中我们可以使用Runtime.getRuntime( ... -
virtualbox 4.08安装虚机Ubuntu11.04增强功能失败解决方法
2011-06-23 22:17 14400在笔记本安装Ubuntu11.04增强功能失败 引用 fuli ... -
awk getline
2011-06-02 23:58 5777awk getline开始一直用做按行读取文件。 getlin ... -
[zz]服务器性能评估
2011-04-29 14:17 3988工作这么久了,主 ... -
[zz]Top命令VIRT,RES,SHR,DATA的含义
2011-04-10 15:50 4062VIRT:virtual memory usage ... -
[zz]一些shell命令
2011-04-10 15:35 14641.显示消耗内存/CPU最多的10个进程 ps aux | ... -
Open VPN免密码配置
2011-03-03 22:55 3589公司VPN的帐号密码使用的是个超长的随机数,每次输入很麻烦,如 ... -
Linux下使用RTX腾讯通
2011-03-03 22:46 10738在公司为了使用RTX,专门安装了一个XP的虚拟机,但是这个也不 ... -
[zz]linux 常用命令总结:
2010-12-11 21:30 1654linux 常用命令总结: 一。 通用命令: 1. dat ... -
Ubuntu10.10解决Empathy无法链接MSN的问题
2010-10-21 16:36 37171.在Empathy中删除无法登录的MSN账户,并关闭Empa ... -
[zz]Vim的分屏功能
2010-10-21 13:09 1643分屏启动Vim 使用大写 ... -
Ubuntu10.10 64bit使用Eclipse插件subclipse问题
2010-10-20 20:32 1932升级到ubuntu10.10什么坑都碰到了,郁闷啊。发现sub ... -
scim输入法,Eclipse代码提示之后失去焦点的问题
2010-10-20 13:36 3053突然发现Eclipse,每次代码提示之后无法打字,感觉是文本域 ... -
Linux下使用谷歌输入法
2010-10-18 23:01 3082Linux的中文输入法一直太烂,scim终于出来对google ...
相关推荐
Linux系统编程笔记.pdfLinux系统编程笔记.pdfLinux系统编程笔记.pdfLinux系统编程笔记.pdf
GUN/LINUX系统编程学习笔记,分享快乐。
Linux系统编程涵盖了众多领域,从基础的文件操作到高级的网络通信和多线程编程,为开发者提供了广泛的功能和灵活性。在这个领域工作的开发者需要深入了解Linux内核和系统底层的工作原理。 Linux 系统编程是指在 ...
linux系统 编程 开发 笔记 如果 大家 需要 可以 下载 看看 啊 希望 对你 linux 系统学习 有一定的 帮助 啊
笔记_UNIX环境网络编程卷二进程间通信_中文第二版
linux系统管理学习笔记 linux系统管理学习笔记 linux系统管理学习笔记
【linux学习笔记-13】基本进程通信--文件锁.doc 【linux学习笔记-14】基本进程通信--信号.doc 【linux学习笔记-15】基本进程通信--管道.doc 【linux学习笔记-16】shell管道重定向程序的实现.doc 【linux学习笔记-1】...
linux系统编程笔记
Linux系统在科学计算、数据分析和机器学习等领域也有广泛应用。许多知名的科学计算软件都在Linux上开发和运行,Linux系统在各个领域都有广泛的应用,其强大的功能和灵活性使得它成为许多产品和服务的基础架构。
Linux系统编程笔记Linux系统编程笔记Linux系统编程笔记Linux系统编程笔记Linux系统编程笔记
GNU_LINUX 系统编程笔记.pdf
【linux学习笔记-13】基本进程通信--文件锁 【linux学习笔记-14】基本进程通信--信号 【linux学习笔记-15】基本进程通信--管道 【linux学习笔记-16】shell管道重定向程序的实现 【linux学习笔记--17】POSIX IPC——...
linux编程学习笔记PDF资料下载
尚观 linux内核驱动开发 笔记 系统编程部分 绝对值得研究!
Linux系统在科学计算、数据分析和机器学习等领域也有广泛应用。许多知名的科学计算软件都在Linux上开发和运行,Linux系统在各个领域都有广泛的应用,其强大的功能和灵活性使得它成为许多产品和服务的基础架构。
linux socket网络编程笔记自己整理的。
学习笔记信号量和进程间通信