linux进程篇

发布于:2023-01-20 ⋅ 阅读:(10) ⋅ 点赞:(0) ⋅ 评论:(0)

目录

一.进程的概念

并行和并发

并行和并发的区别:

PCB(进程控制块)

进程状态

二.进程控制

1.fork函数

2.getpid/getppid函数

3.ps和kill命令

4.父子进程间的数据共享

5、孤儿进程和僵尸进程:

7.进程回收

8.vfork创建进程:

9.进程退出


一.进程的概念

1、程序:二进制文件,占用的磁盘空间
2、进程: 启动的程序
3、所有的数据都在内存中
4、需要占用更多的系统资源
5、cpu, 物理内存

.并行和并发

并发 在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是 在同一个处理机上运行
注意:并发不是真正意义上的 同时进行 ,只是 CPU 把一个时间段划分成几个时间片段(时间区间),然后在这几 个时间区间之间来回切换,由于CPU 处理的速度非常快,只要时间间隔处理得当,即可让用户感觉是多个应 用程序同时在进行。如:打游戏和听音乐两件事情在同一个时间段内 都是在同一台电脑上完成了 从开始到结 束的动作 。那么,就可以说听音乐和打游戏是并发的。
并行 当系统有一个以上 CPU 时,当一个 CPU 执行一个进程时,另一个 CPU 可以执行另一个进程,两个进程 互不抢占 CPU 资源,可以同时进行,这种方式我们称之为并行 (Parallel)
注意:其实决定并行的因素不是 CPU 的数量,而是 CPU 的核心数量,比如一个 CPU 多个核也可以并行

并行和并发的区别:

1、并发,指的是多个事情,在同一时间段内同时发生了。
2、并行,指的是多个事情,在同一时间点上同时发生了。
3、并发的多个任务之间是互相抢占资源的;
4、并行的多个任务之间是不互相抢占资源的、 只有在多CPU 或者一个 CPU 多核的情况中,才会发生并行。否则,看似同时发生的事情,其实都是并发执行的

.PCB(进程控制块)

每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信息, linux 内核的进程控制块是 task_struct 结构 体。
需要掌握的部分:
进程 id 。系统中每个进程有唯一的 id, C 语言中用 pid_t 类型表示,其实就是一个非负整数
进程的状态,有就绪,运行,挂起,停止等状态。
进程切换时需要保存和恢复一些 CPU 寄存器
描述虚拟地址空间的信息
描述控制终端的信息
当前工作目录(Cueernt Working Directory)
umask 掩码
文件描述符,包含很多指向 fifile 结构体的指针
和信号相关的信息
用户 id 和组 id,stat
会话(Session)和进程组
进程可以适用的资源上线( Resource Limit ,umilit -a

.进程状态

进程基本的状态有五种,分别为初始态,就绪态,运行态,挂起态和终止态。
其中初始态为进程准备阶段,常常与就绪态结合来看。

二.进程控制

1.fork函数

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

 

2.getpid/getppid函数

getpid: 得到当前进程的 PID
getppid: 得到当前进程的父进程的 PID
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc,char *argv[]){
	pid_t pid ;
	int i=0;
	for(i=0;i<4;i++)
	{
		printf("============ i=%d============= \n",i);
	}
	pid=fork();
	//father progess
	if(pid > 0)
	{
		printf("this is father progess %d \n",getpid());
	}

	//child progess

	else if(pid == 0)
	{

	printf("this is child progess %d,ppid=%d\n",getpid(),getppid());
	}
	 for(i=0;i<4;i++)
        {
                printf(" i=%d \n",i);
        }

return 0;
}

3.pskill命令

ps部分参数:
a : 显示现行终端机下的所有程序,包括其他用户的程序
u: 以用户为主的格式来显示程序状况
x: 显示所有程序,不以终端机来区分
例如:ps aux、ps ajx
kill:
kill 可将指定的信息送至程序。预设的信息为 SIGTERM(15) ,可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL(9)信息尝试强制删除程序。程序或工作的编号可利用 ps 指令或 job 指令查看。
参数:
-a :当处理当前进程时,不限制命令名和进程号的对应关系;
-l < 信息编号 > :若不加 < 信息编号 > 选项,则 -l 参数会列出全部的信息名称;
-p :指定 kill 命令只打印相关进程的进程号,而不发送任何信号;
-s < 信息名称或编号 > :指定要送出的信息;
-u :指定用户。
例如:kill -9+进程pid

4.父子进程间的数据共享

fork 之后两个地址空间区数据完全相同, 后续各自进行了不同的操作。
1、父进程全部执行
2、子进程在父进程结束子进程开始(fork()之后子进程开始执行)
3、父子进程的执行顺序,不一定父进程先执行
各个进程的地址空间中的数据是完全独立的 ;
对于同一个变量,读时共享 ;写的时候分别在物理地址上拷贝一份变量进行单独读写;
父子进程之间可不可以通过全局变量通信?
不能, 两个进程内存不能共享

5、孤儿进程和僵尸进程:

孤儿进程
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进 程(进程号为1)所收养,并由init进程对它们完成状态收集工作
为了释放子进程的占用的系统资源:
(1)进程结束之后,能够释放用户区空间
(2)释放不了 PCB ,必须由父进程释放
僵尸进程
一个比较特殊的状态,当进程退出父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产 生僵尸进程。僵尸进程会在以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。
注意:僵尸进程 是一个已经死掉了的进程,父进程没有进行回收
            用ps aux命令查看,有<defunct>的是僵尸进程

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,且子进程正在运行

8.vfork创建进程:

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

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);