【如何建立多进程程序】
在程序中有时候会用到一个程序需要启动另一个程序的情况。首先呢,程序运行的实体就是进程,因此在一个程序运行过程中我们再启动另一个程序的运行,专业地来讲就是用一个进程启动另一个进程的过程。Linux编程中有几个方法来实现。现在将这几种方法做一个比较:
1、用system系统调用实现。
使用方法举例:system("ps -ax"),system函数的参数是一个字符串,这个函数等效于在shell中执行字符串里面的内容,对于本例等效于在shell中执行ps -ax。
这种方法用shell来启动想要执行的程序,因此在启动程序之前必须启动一个shell,对环境的依赖性较大。而且程序要等到system函数返回之后才能得到继续执行。因此为了原来的程序能够得到立刻执行,可以在原来的命令末尾增加&让它在后台执行,即system("ps -ax &").
2、采用exec函数族实现。
exec函数族是没有返回值的,除非发生错误才会返回-1。这个函数族的作用是运行一个新的程序来替换旧的程序。而新的进程会继承旧的继承的很多特征,事实上就是本来在运行中的程序开始运行新程序的代码。特别是在原进程中已打开的文件描述符在新进程中仍保持打开。
3、fork+exec
上面所说的exec的方式是用一个新的进程替换了旧的进程,没有达到让他们同时工作的目的。那么,还有一种方法,使用fork+exec的组合来完成这项任务。
首先fork是什么作用呢?它是用来复制当前进程的函数。它在进程表中创建一个新的表项,这个表项有很多属性与当前进程相同,新进程与原进程机会完全一样,执行的代码也一样,但新进程有自己的数据空间、环境和文件描述符。父进程和子进程是共享代码的,因此让父进程和子进程执行不同的语句只有用fork的返回值来标识进程的执行行为,对于父进程从fork中获得一个非零值,对于子进程从fork调用中得到0。在子进程中执行exec就可以实现让父子进程运行不同的代码啦。
在一个进程里面启动另一个进程实现了进程的并发执行,提高了系统的吞吐量。但是进程是申请资源的基本单位,它的创建、撤销和切换都需要系统付出较大的时空开销,因此系统的并发执行的进程数量不宜过高,因此也限制了系统的并行性的进一步提升。从而产生了多线程技术来进一步提高系统的并发行。
上面说进程是一个申请系统资源的最小单位,而线程则是一个系统调度和分配的最小单位。实际上,线程是运行于线程中的用于调度和占有处理机的基本单位。线程基本上不拥有资源,但是它可以和同属于一个进程的其他线程并发执行,共享所属进程的资源。
【如何建立多线程程序】
通过调用pthread_creat函数来创建新的线程,这个函数的参数中要传递新线程的入口函数。
【线程间的同步】
那么既然涉及到多进程和多进程,那么必然会遇到一个问题,进程间或者是线程间同步和通信的问题。对于多线程而言,每个变量所拥有的全局变量是所有线程都可见的,因此可以采用一个全局的信号量或互斥量来实现同步。注意,这些信号量和互斥量都是原子变量,即对它的增减操作一旦开始就要执行完,不可能出现未执行完就出现上下文切换的情况。
【进程间的管道通信】
进程间的通信可以通过管道来实现。管道有三种不同的应用场景:
1、通过popen打开一个程序,这种调用要通过shell来启动程序的执行。将打开的程序输出作为一个文件一样来处理;
2、通过pipe打开一对文件描述符,向一个文件写的内容可以从另一个文件读出来。因为fork语句产生的父子进程对之间是共享打开的文件描述符的,因此可以通过pipe与fork结合,在父子进程之间进行通信。但是父子进程执行的代码是一样的,这可能与我们程序的初衷不一样。然而对于子进程采用exec,让子进程来执行我们期望的代码。但是exec调用的程序并不了解原来的文件描述符,因此pipe+fork+exec的情况可以将文件描述符作为参数传递给它。
3、通过命名管道fifo的使用,打破上面只能在父子进程间通信的限制。通过fifo建立的管道其使用方式就如果一个普通文件一样,只要一个进程创建管道文件,其他进程通过读写这个管道文件就可以使用管道来通信了。而且,fifo还可以运行在阻塞方式,这样便于进行进程间的同步通信。