进程是程序运行的实例;每个进程可以并发运行多个线程。进程内(线程间)的数据可以共享,但是进程之间并不能互相访问,因为每个进程都维护着自己的text、stack、heap等等,它们的地址对其他进程不可见。 除了使用文件,还有其他几种方法可以实现进程间的数据共享。它们各自有各自的特点,适用于不同的应用场景。 (* 这里不涉及到网络套接字。) Pipes ================================ 管道通信,是Unix系统里非常古老的通信形式。它有两个限制: * 半双工(某些系统提供全双工); * 通信的进程必须有共同的ancester。 管道的应用非常常见,比如当我们在终端输入管道命令时,shell会为每个命令建立一个进程,并且在前一个命令的输出和后一个命令的输入之间建立管道通信。 每个进程有两个管道口,一个用作输入,一个用作输出。如果这个进程fork子进程,那么子进程同样具有两个管道口。父进程关掉自己的输入口,子进程关掉自己的输出口,父进程的输出就可以到达子进程的输入。当然,进程内部也可以使用管道通信(虽然它没什么作用^_^)。 pile函数的原型如下: #include int pipe(int fd[2]); 其中,fd[0]为管道输入,fd[1]为管道输出。 假设在一个种植月季的智能温室里,控制中心的电脑上运行着这样一个应用程序,名为RoseGarden,它启动多个进程,分别执行不同的任务,比如温度调节、光线调节、水分调节、肥料施用…… 程序如下: #include <iostream> #include <unistd.h> #include <memory.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/shm.h> using namespace std; int main() { cout << "****************************" << endl; cout << "******** RoseGarden ********" << endl; cout << "****************************" << endl; int fd[2]; if (pipe(fd) < 0) { cerr << "Error: pipe." << endl; return -1; } pid_t pid; if ((pid = fork()) < 0) { cerr << "Error: fork." << endl; return -1; } else if (pid == 0) { close(fd[1]); cout << " Start." << endl; char data[64]; string str; while (1) { memset(data, 0, sizeof(data)); read(fd[0], data, sizeof(data)); str = " R: " + string(data); cout << str << endl; } } else { close(fd[0]); int n = 0; string str; while (1) { str = "RoseGarden " + to_string(n++) + " piping."; write(fd[1], str.data(), str.size()); sleep(1); } } return 0; } 执行结果: $ ./RoseGarden **************************** ******** RoseGarden ******** **************************** Start. R: RoseGarden 0 piping. R: RoseGarden 1 piping. R: RoseGarden 2 piping. R: RoseGarden 3 piping. R: RoseGarden 4 piping. R: RoseGarden 5 piping. 这说明父进程RoseGarden的数据通过管道,到达了子进程Water中。 如果将pipe的文件描述符duplicate到标准输入/输出上,会发生非常有趣的事情。在文件IO中,dup函数用来复制文件描述符,函数原型如下: #include int dup(int fd); int dup2(int fd, int fd2); 使用dup函数,那么其返回值和fd会指向内核中的同一个文件table,因此不论是操作其返回值还是fd,修改的是同一个文件。使用dup2函数,那么fd2会关闭自己(如果它已打开),然后指向fd的文件table。 将代码修改如下: if ((pid = fork()) < 0) { cerr << "Error: fork." << endl; return -1; } else if (pid == 0) { close(fd[1]); cout << " Start." << endl; if (fd[0] != STDIN_FILENO) { if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) { cerr << " Error: dup2." << endl; exit(1); } close(fd[0]); } if (execlp("more", "more", (char *)0) < 0) { cerr << " Error: execlp." << endl; exit(1); } } else { close(fd[0]); int n = 0; string str; while (1) { str = "RoseGarden " + to_string(n++) + " piping.\r\n"; for (int i = 0; i < 10; i++) str += to_string(i) + " ++++++++++++\r\n"; write(fd[1], str.data(), str.size()); sleep(1); } } 程序执行的结果: $ ./RoseGarden **************************** ******** RoseGarden ******** **************************** Start. RoseGarden 0 piping. 0 ++++++++++++ 1 ++++++++++++ 2 ++++++++++++ 3 ++++++++++++ 4 ++++++++++++ 5 ++++++++++++ 6 ++++++++++++ 7 ++++++++++++ 8 ++++++++++++ 9 ++++++++++++ RoseGarden 1 piping. 0 ++++++++++++ 1 ++++++++++++ 2 ++++++++++++ 3 ++++++++++++ 4 ++++++++++++ 5 ++++++++++++ 6 ++++++++++++ --More-- 这说明,父进程往pipe里面写的数据,由pipe的read端接收。此时more命令读取的STDIN_FILENO的数据,实际上就是读取管道的数据。此时more的命令,比如s、d、q……有效。 另外,虽然管道是单向的,我们可以建立两条管道,实现双向通信功能。 其实,大部分我们要实现的功能是类似的,比如创建一条管道、fork一个子进程、关闭不用的管道ends、exec一个命令、等待命令结束……我们可以使用popen函数,处理这些类似的功能。它的函数原型为: #include FILE *popen(const char *cmdstring, const char *type); int pclose(FILE *fp); 因此之前的程序可以大大的被简化,如下所示: #include <iostream> #include <unistd.h> #include <memory.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/shm.h> using namespace std; int main() { cout << "****************************" << endl; cout << "******** RoseGarden ********" << endl; cout << "****************************" << endl; FILE *fp; if ((fp = popen("more", "w")) == NULL) { cerr << "Error: popen." << endl; exit(1); } int n = 0; string str; cout << "Start writing pipe: " << endl; while (1) { str = "RoseGarden " + to_string(n++) + " piping.\r\n"; for (int i = 0; i < 10; i++) str += to_string(i) + " ++++++++++++\r\n"; fprintf(fp, str.data()); fflush(fp); sleep(1); } return 0; } ps,这里的fp是文件stream,因此别忘了fflush。 FIFOs ================================ FIFOs也被称为named pipes。 Unnamed pipes只能用在有相同ancestor的进程之间,就是说创建这个pipe的进程必须是同一个。 FIFOs也是文件。 FIFO的实验在终端下进行,首先在终端1号上执行: $ mkfifo fifo1 $ cat fifo1 然后在终端2号上执行: $ echo "hello world" >> fifo1 就会发现终端1号上打印: $ cat fifo1 hello world 这说明终端2号往fifo1里面写入的数据,被终端1号读取到了。 FIFOs在终端里的作用非常大,它和之前的pipe一起,可以实现各种命令的组合。 XSI IPC: Message Queues ================================ 消息队列是消息组成的链表,它存储在内核中,由一个identifier引用。 * 《Unix环境高级编程》的作者,是有多不喜欢消息队列?以至于隔一会儿就把它批一顿,而且完全没有给出它的应用example,另外在最末尾还建议“we come to the conclusion that we shouldn't use them for new applications”。 * 既然这样,就不做练习了!直接进入下一节吧。 XSI IPC: Semaphores ================================ 信号量其实不是用于进程间通信的,它是一个counter,用来保护进程间的共享数据。 * 同上。需要使用信号量的时候,再考虑考虑mutex和record locking。 * 不做练习!直接进入下一节。 XSI IPC: Shared Memory ================================ 共享内存是最快速的IPC方法。 假设有这样一个应用场景:RoseGarden每隔一小时拍摄一张照片,放置在共享内存中,供另外一个进程PictureDraw使用。由于照片的体积很大,因此拷贝照片耗费的时间太太,共享内存是非常合适的解决方法。 代码如下: #include <iostream> #include <unistd.h> #include <memory.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/shm.h> using namespace std; #define SHM_SIZE 4000000 int main() { cout << "****************************" << endl; cout << "******** RoseGarden ********" << endl; cout << "****************************" << endl; pid_t pid; if ((pid = fork()) < 0) { cerr << "Error: fork." << endl; exit(1); } else if (pid == 0) { cout << " Start." << endl; int shmid; void *shmptr; if ((shmid = shmget(IPC_PRIVATE, SHM_SIZE, 0600)) < 0) { cerr << "Error: shgmget." << endl; exit(1); } if ((shmptr = shmat(shmid, 0, 0)) == (void *)-1) { cerr << "Error: shmptr." << endl; exit(2); } cout << " Shared-Memory start at " << shmptr << endl; cout << " shmid = " << shmid << endl; if (shmctl(shmid, IPC_RMID, 0) < 0) { cerr << "Error: shmctl." << endl; exit(1); } } else { } return 0; } 执行结果: $ ./RoseGarden **************************** ******** RoseGarden ******** **************************** Start. Shared-Memory start at 0x7fd5d7bb3000 shmid = 11075601
关闭
站长推荐
/3
DiracFatCat 2016-6-6 13:32
用户1291786 2016-6-6 09:48
用户1678053 2016-6-1 08:40