进程的概念
进程就是执行中的程序,是系统资源分配的最小单位。
进程的内存分配
进程的作用
宏观上是并行的,微观上是串行的
进程的状态
对于基本的操作系统:有三个状态: 就绪态->执行态-> 阻塞态
在LInux中有四种:运行态,睡眠态,僵尸态,暂停态。
进程基本的函数
#include <unistd.h> //fork()函数的头文件
fork( )
一次调用,会返回两次。
子进程先运行和是父进程先进程,顺序不确定。
变量不共享。
子进程复制父进程的0到3g空间和父进程内核中的PCB,但id号不同。(同非常ID号子比父的要大一些)
功能:通过该函数可以从当前进程中克隆一个同名新进程。
克隆的进程称为子进程,原有的进程称为 父进程。
子进程是父进程的完全拷贝。
子进程的执行过程是从fork函数之后执行。
返回值:
int 类型的数字
在父进程中 : 成功 返回值是子进程的pid号 > 0
失败 返回-1
在子进程中: 成功 返回值0
失败 无
getpid()
获得本进程的pid号
getppid()
获得父进程的pid号
父子进程的关系
子进程是父进程的副本。子进程获得父进程数据段,堆,栈,正文段共享。
进程的终止
8个情况
1)main 中return
2)exit(), c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件。已经清理函数(atexit)。
3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。
4) 主线程退出
5)主线程调用pthread_exit
异常终止
6)abort()
7)signal kill pid
8)最后一个线程被pthread_cancle
注意:
exit(0 / 1 ) 在使用的时候都是对的写 0
存在错误的时候写 1
僵尸进程和孤儿进程
僵尸进程:一个主进程用fork创建了子线程,当子线程结束的时候,主线程没有调用wait或者waitpid回收掉这个子进程,这个子进程的任务描述符仍然在系统中。
孤儿进程:一个主进程用fork创建了子线程,主进程已经退出了,而子进程还在继续运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
进程操作示例代码:
1、创建两个线程:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
pid_t pid = fork(); //用pid接受fork的返回值判断是父进程还是子进程
if(pid>0)
{
while(1)
{
printf("fahter, 发送视频\n");
sleep(1);
}
}
else if(0 == pid)
{
while(1)
{
printf("child, 接收控制\n");
sleep(1);
}
}
else
{
perror("fork");
return 1;
}
return 0;
}
2、对父子进程共用代码端的观察
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
int a = 10;
int main(int argc, char *argv[])
{
pid_t pid = fork();
if(pid>0)
{
sleep(3);
printf("father a is %d\n",a);
}
else if(0 == pid)
{
a+=20;
printf("child a is %d\n",a);
}
else
{
perror("fork");
return 1;
}
printf("a is %d\n",a);
return 0;
}
//输出结果
child a is 30
a is 30
father a is 10
a is 10
由此可知在父子进程中,在fork之前部分的代码为共用,而分进程之后进行的操作各自都会有自己的执行空间。
3、获得自己的pid和父进程的pid
pid_t pid = fork();
if(pid>0)
{
sleep(3);
printf("father a is %d, pid:%d ppid:%d\n",a,getpid(),getppid());
}
else if(0 == pid)
{
a+=20;
printf("child a is %d pid:%d ,ppid:%d\n",a,getpid(),getppid());
}
else
{
perror("fork");
return 1;
}
4、atexit //注册终止函数
调用了exit函数使进程终止退出,在进程终止之前,如果注册了终止函数,那么exit函数会先去依次调用进程终止函数,,每调用完一个终止函数并返回,调用顺序是以栈的形式来调用,调用flush刷新IO缓冲区的数据之后返回。
eg:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
char * tmp=NULL;
void clean(void)
{
printf("this is clean %s\n",tmp);
free(tmp);
}
int main(int argc, char *argv[])
{
atexit(clean);
tmp =(char*) malloc(50);
strcpy(tmp,"hello");
printf("123123\n");
return 0;
}
输出结果为:
123123
this is clean hello