多进程编程
1 fork系统调用,exec系统调用
fork()
- fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。
- 在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。
- 它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值; - 这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
- 进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。
- 父进程么有函数获取所以子进程,故子进程可以通过管道传给父进程,或者干脆把进程号写到文件里。
- 子进程复制父进程的数据;采用写时复制(只有父进程或子进程对数据操作修改,先缺页中断,然后操作系统给子进程分配内存并复制父进程数据)
进阶:fork循环打印
fork若有缓冲区则多打印
fork循环详解
exec()
fork产生一个进程,该进程如果只是运行主进程的程序,那如果我们希望这个进程去干其他的活动,比如所我们在打字的时候同时听着歌,怎么办呢?这里就用到了exec()。
一个进程一旦调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。(不过exec类函数中有的还允许继承环境变量之类的信息。)
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int i;
pid_t pid;
//for another process
pid = fork();
if(pid < 0)
{ //error
printf("Fork failed");
return 1;
}
else if(pid == 0){ //run program.c
execl("./program", "program", NULL);
}
else{
wait(NULL);
printf("Child complete\n");
}
return 0;
}
其中program.c
#include <stdio.h>
#include <ctype.h>
int main()
{
int i;
for(i = 0; i < 10; i++)
{
printf("A new program\n");
}
return 0;
}
这里子进程就是运行的program.c。
在传统的Unix、linux环境下,有两个基本的操作用于创建和修改进程:函数fork( )用来创建一个新的进程,该进程几乎是当前进程的一个完全拷贝;函数族exec( )用来启动另外的进程以取代当前运行的进程。这些函数给程序猿的多进程编程提供了遍历,也增加了CPU的利用率。
2 僵尸进程和避免(wait,waitpid)
3 孤儿进程与守护进程
子进程init接管、建立进程组、会话、更改到根目录、设置掩码等。
4 进程间通信:管道,信号量,共享内存,消息队列
管道
信号
信号量
共享内存
消息队列
进程间传递文件描述符:socket
服务器编程规范
- 服务器后台以守护进程的形式进行
- 服务器程序一般有日志系统
- 一般有某个专门的root用户身份
- 启动时生成pid文件
- 考虑系统资源和限制
5 进程同步
通过进程间通信
- 临界区
- 同步与互斥
- 信号量
- 管程
6 线程同步
多个线程并发,应该同步线程避免发生资源冲突
- 互斥量
- 信号量
- 事件等