目录
1.进程
1.
进程的概念
程序:二进制文件,占用的磁盘空间
进程: 启动的程序
所有的数据都在内存中
需要占用更多的系统资源
cpu,
物理内存
2.
并行和并发
并发:
在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是
在同一个处理机上运行
并行:
当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
其实决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行
并行和并发的区别:
并发,指的是多个事情,在同一时间段内同时发生了。
并行,指的是多个事情,在同一时间点上同时发生了。
并发的多个任务之间是互相抢占资源的。
并行的多个任务之间是不互相抢占资源的。
只有在多
CPU
或者一个
CPU
多核的情况中,才会发生并行。否则,看似同时发生的事情,其实都是并发执行的。
3.PCB
(进程控制块)
每个进程在内核中都有一个进程控制块(
PCB
)来维护进程相关的信息,
linux
内核的进程控制块是
task_struct
结构体。
/user/src/linux-headers-3.16.0-30/include/linux/sched.h
头文件中可以查看
struct task_struct
结构体定义。内部成员有很多比如:
进程
id
。系统中每个进程有唯一的
id,
在
C
语言中用
pit_t
类型表示,其实就是一个非负整数
进程的状态,有就绪,运行,挂起,停止等状态。
进程切换时需要保存和恢复一些
CPU
寄存器
描述虚拟地址空间的信息
描述控制终端的信息
当前工作目录(
Cueernt Working Directory)
umask
掩码
文件描述符,包含很多指向
file
结构体的指针
和信号相关的信息
用户
id
和组
id,stat
会话(
Session
)和进程组
进程可以适用的资源上线(
Resource Limit
)
,umilit -a
4.
进程状态
进程基本的状态有五种,分别为初始态,就绪态,运行态,挂起态和终止态。
其中初始态为进程准备阶段,常常与就绪态结合来看。

2.进程控制
1.fork函数
一个进程,包括代码、数据和分配给进程的资源。
fork
()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用
fork
()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
包含的头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
pid_t fork(void);
两个返回值:
=0
:当前进程为子进程
>0
:当前进程为父进程
‐1
,出错
2.getpid/getppid
函数
getpid:
得到当前进程的
PID
getppid:
得到当前进程的父进程的
PID


父进程执行结束 子进程开始执行。
3.ps和kill命令
1.ps
查看进程信息
部分参数:
a :
显示现行终端机下的所有程序,包括其他用户的程序
u:
以用户为主的格式来显示程序状况
x:
显示所有程序,不以终端机来区分


2.kill
向指定的进程发送信号
kill
可将指定的信息送至程序。预设的信息为
SIGTERM(15)
,可将指定程序终止。若仍无法终止该程序,可使用
SIGKILL(9)
信息尝试强制删除程序。程序或工作的编号可利用
ps
指令或
job
指令查看。
参数:
-a
:当处理当前进程时,不限制命令名和进程号的对应关系;
-l <
信息编号
>
:若不加
<
信息编号
>
选项,则
-l
参数会列出全部的信息名称;
-p
:指定
kill
命令只打印相关进程的进程号,而不发送任何信号;
-s <
信息名称或编号
>
:指定要送出的信息;
-u
:指定用户。
比如用kill函数终止程序:

4.父子进程间的数据共享
后续各自进行了不同的操作
各个进程的地址空间中的数据是完全独立的
对于同一个变量,读时共享
写的时候分别在物理地址上拷贝一份变量进行单独读写
父子进程之间可不可以通过全局变量通信?
不能,两个进程内存不能共享


5.exec函数族
让父子进程来执行不相干的操作
能够替换进程地址空间的代码.text段
执行另外的程序,不需要创建额外的的地址空间
当前程序中调用另外一个应用程序
指定执行目录下的程序 int execl(const char *path, const char *arg, ... /* (char *) NULL */);





我们发现此处值打印了一次i的值,这说明子进程调用exec函数后后面的文本就删除了。
/* path : 要执行程序的路径(最好是绝对路径) 变参arg : 要执行的程序需要的参数 第一位arg: 占位 后边的arg: 命令的参数 参数写完之后:null 一般执行自己写的程序 */
执行PATH环境变量能够搜索到的程序 int execlp(const char *file, const char *arg, ... /* (char *) NULL */);


返回值:
如果函数运行成功不返回
如果执行失败,打印错误信息,退出子进程
6.孤儿进程和僵尸进程
孤儿进程
:
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作
为了释放子进程的占用的系统资源:
进程结束之后,能够释放用户区空间
释放不了PCB,必须由父进程释放


僵尸进程:
一个比较特殊的状态,当进程退出父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵尸进程。僵尸进程会在以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。



7.进程回收
wait
阻塞函数
函数作用:
1.
阻塞并等待子进程退出
2.
回收子进程残留资源
3.
获取子进程结束状态(退出原因)
函数原型:pid_t wait(int *wstatus);
返回值:
‐1 :
回收失败,已经没有子进程了
>0 :
回收子进程对应的
pid
参数:
status
判断子进程如何退出状态
1.WIFEXITED(status):
为非
0
,进程正常结束
WEXITSTATUS(status)
如上宏为真,使用此宏,获取进程退出状态的参数
2.WIFSIGNALED(status):
为非
0
,进程异常退出
WTERMSIG(status):
如上宏为真,使用此宏,取得使进程种植的那个信号的编号
调用一次只能回收一个子进程



waitpid
函数
函数作用:同
wait
函数
函数原型:pid_t waitpid(pid_t pid, int *status, int options);
参数
1.pid:
指定回收某个子进程
pid == ‐1
回收所有子进程
while( (wpid=waitpid(‐1,status,0)) != ‐1)
pid > 0
回收某个
pid
相等的子进程
pid == 0
回收当前进程组的任一子进程
pid < 0
子进程的
PID
取反(加减号)
2.status:
子进程的退出状态,用法同
wait
函数
3.options:
设置为
WNOHANG,
函数非阻塞,设置为
0
,函数阻塞
返回值:
>0 :
返回清理掉的子进程
ID
‐1
:回收失败,无子进程
如果为非阻塞
=0
:参数
3
为
WNOHANG,
且子进程正在运行





这里参数3设置为WNOHANG因为没有子进程回收,但是函数还是会执行,所以会一直打印wpid is 0.
为了防止一直打印wpid is 0在这里加上if语句使wpid等于0时不执行下面的命令。


8.vfork创建进程
vfork
也可以创建进程,与
fork
有什么区别呢?
区别一:
vfork
可以直接使用父进程存储空间,不拷贝
区别二:
vfork
可以保证子进程先运行,当子进程调用
exit
退出后,父进程才执行




可看出来
vfork
可以直接使用父进程存储空间,可以共享数据。
9.进程退出
1.
正常退出
1.main
函数调用
return
2.
进程调用
exit(),
标准
C
库
3.
进程调用
_exit()
或者
_Exit(),
属于系统调用
补充:
4.
进程最后一个线程返回
5.
最后一个线程调用
pthread_exit
2.
异常退出
1.
调用
abort
函数
2.
当进程收到某些信号时,比如
ctrl +C
3.
最后一个线程对取消(
cancellation
)请求做出相应
不管进程如何终止,最后都会执行内核中的同一段代码,这段代码和相应进程关闭所有打开描述符,释放它所使用的存储器等。
对上述任一一种终止情形,我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数
(exit, _exit和 _ Exit)
,实现这一点的方法是,将其退出状态
(exit status)
作为参数传送给函数,在异常终止情况下,内核
(
不是进程本身)
产生一个指示其异常终止原因的终止状态
(termination status)
。在任意一种情况下,该终止进程的父进程都能用wait
或
waitpid
函数取得终止状态。
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
#include <stdlib.h>
void _Exit(int status);


