进程基础知识
1、进程标识符pid
pid_t (int16_t),有可能不够用 。
命令ps, ps -axf
**进程号是顺次向下使用。(与fd有区别)**
获取当前进程的pid:getpid();
获取父进程的pid:getppid();
2、进程的产生 fork();
①duplicating的,复制,拷贝,一模一样,连运行到的位置都相同
②fork后父子进程的区别
:fork的返回值不一样,pid不同,ppid也不同,
未决信号(还没来得及去响应的信号)和文件锁不继承,资源利用量归零。
③Init进程:1号,是所有进程的祖先进程。
④fork成功,父进程中返回的是子进程的Pid,子进程中返回0;fork失败,父进程中返回-1,设置errno。
永远不要猜测父子进程哪先被调度,这是由调度器的调试策略决定的。
了解:vfork();
用vfork创建的子进程,实际和父进程指向的是同一块物理内存;但vfork创建的子进程,只能调用_exit()或exec(),其它行为(如关闭一个文件)是未定义的。
fork()是写时拷贝,只读时父子进程指向同一块空间。父子进程谁想写,谁去复制一份内存到其它空间。fork通过写时拷贝技术,基本将过去vfork的功能糅合进来了,所以vfork逐渐被废弃。
fork函数例程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
pid_t pid;
printf("[%d]:begin.\n",getpid());
//加了\n,因为默认输出设备是stdout,行缓冲,会刷新缓冲区;
//所以命令./fork,打印一次begin
//命令./fork1 > /tmp/out,还来得及没写到文件(全缓冲)中,马上fork了,
//使得父子进程缓冲区都有存在一句“begin.”。
fflush(NULL);//正确的做法是fork之前刷新所有打开的流。
pid = fork();
if(pid < 0)
{
perror("fork()");
exit(1);
}
if(pid == 0) //child
{
printf("[%d]:child is working\n",getpid());
}
else //parent
{
printf("[%d]:parent is working\n",getpid());
}
printf("[%d]:end.\n",getpid());
exit(0);
}
打印结果,与缓冲区有关。比较下列三种结果:
①终端输出,行缓冲
②fork前使用fflush刷新缓冲区
③fork前不调用fflush:
3、进程的消亡及释放资源
使用wait或waitpid函数等待进程状态发生变化
①函数原型:pid_t wait(int *status);
成功返回终止子进程的pid,将子进程的状态存储到status指向的内存。死等,是阻塞的。
②函数原型:pid_t waitpid(pid_t pid, int *status, int options);
options是位图,如果有WNOHANG参数,函数变成非阻塞的。
pid: <-1,收进程组id为|-n|中的任何一个子进程
-1,收任何一个子进程
0,收与当前进程同组的一个子进程
>0,收指定pid号的子进程
wait(&status) 相当于 waitpid(-1,&status,0)
如果只是要回收而不关心子进程的状态,也可以wait(NULL)。
多进程编程:
找出某区间内的所有质数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LEFT 30000000
#define RIGHT 30000200
int main()
{
int i,j,mark;
pid_t pid;
for(i = LEFT; i <= RIGHT; i++)
{
//父进程不断创建子进程
pid = fork();
if(pid < 0)
{
perror("fork()");
exit(1);
}
//质数的计算交给子进程
if(pid == 0) //child
{
mark = 1;
for(j = 2; j < i/2; j++)
{
if(i % j == 0)
{
mark = 0;
break;
}
}
if(mark)
printf("%d is primer\n",i);
//如果没有exit,子进程还会去fork。
exit(0);//子进程正常终止
}
}
for(i = LEFT; i <= RIGHT; i++)
wait(NULL);//只是要回收,不关心回收的子进程的状态
exit(0);
}
/***************没必要每次计算都创建进程,可以创建N个进程交叉分配计算任务,作出如下优化***********************/
int main()
{
int i,j,n,mark;
pid_t pid;
for(n = 0; n < N; n++)
{
pid = fork();
if(pid < 0)
{
perror("fork()");
exit(1);
}
//父进程连续创建N个子进程后等待N个子进程完成计算,子进程交叉分配任务
if(pid == 0) //child
{
//第一个进程分配的数总是3的倍数
for(i = LEFT + n; i <= RIGHT; i += N)
{
mark = 1;
for(j = 2; j < i/2; j++)
{
if(i % j == 0)
{
mark = 0;
break;
}
}
if(mark)
printf("[%d]:%d is primer\n",n,i);
}
exit(0);//子进程正常终止
}
}
for(n = 0; n < N; n++)
wait(NULL);//只是要回收,不关心回收的子进程的状态
exit(0);
}