进程与线程基础

本文详细阐述了进程和线程的概念,以工厂和车间为比喻解释了它们的关系,涉及进程状态、创建和终止进程的方法,以及进程程序替换和waitpid函数的使用。重点讲解了互斥锁和信号量在资源管理中的作用。
摘要由CSDN通过智能技术生成

目录

一、详细概念

二、形象概念(借用阮一峰老师的描述)

三、进程状态

3.1 进程的三种基本状态:

3.2 进程控制

3.2.1 创建进程

3.2.2 终止进程

3.2.3 等待进程

3.2.4 进程程序替换


一、详细概念

进程:进程是一个具有一定独立功能的程序关于某一个数据集合的一次运行活动,是操作系统动态执行的基本单元(进程就是一段程序的执行过程)

线程:是错做系统能够运算调度的最小单位,呗包含在进程之中,是进程中的实际运作单位

二、形象概念(借用阮一峰老师的描述)

① 计算机的核心是 CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。

② 假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个 CPU 一次只能运行一个任务。

③ 进程就好比工厂的车间,它代表 CPU 所能处理的单个任务。任一时刻,CPU 总是运行一个进程,其他进程处于非运行状态。

④ 一个车间里,可以有很多工人。他们协同完成一个任务。

⑤ 线程就好比车间里的工人。一个进程可以包括多个线程。

⑥ 车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存

⑦ 可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

⑧ 一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。

⑨ 还有些房间,可以同时容纳 n 个人,比如厨房。也就是说,如果人数大于 n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。

⑩ 这时的解决方法,就是在门口挂 n 把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做 "信号量"(Semaphore),用来保证多个线程不会互相冲突。

不难看出,互斥锁 Mutex 是信号量 semaphore 的一种特殊情况(n = 1时)。也就是说,完全可以用后者替代前者。但是,因为 Mutex 较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。

三、进程状态

3.1 进程的三种基本状态:

        运行态:占有CPU,并在CPU上运行;

        就绪态:已经具备运行条件,但由于没有空闲CPU,而暂时不能运行;

        阻塞态:因等待某一件事而暂时不能运行。

3.2 进程控制

用于创建一个新进程,终止一个已完成的进程,或终止一个因出现某事件而使其无法运行下去的进程,还可负责进程运行中的状态转换

3.2.1 创建进程

1)  函数:pid_t fork(void)(主要)

        返回值:父进程返回值为子进程的进程id,子进程返回值为0;

        特性:子进程复制父进程PCB,二者之间代码共享,数据独有,拥有各自的进程虚拟地址,运用了写时拷贝技术;

 2)  函数:pid_t vfork(void)

        和foke区别:vfork在使用时和fork几乎没有什么区别,返回值及其含义也和fork完全相同。其和fork的区别在于,用v_fork创建出来的子进程,也是拷贝父进程的PCB,但它和父进程共享同一个进程虚拟地址空间。

3.2.2 终止进程

终止情况:正常退出(从main函数返回、调用exit函数、调用_exit函数)和异常退出(程序崩溃、Ctrl+C、信号终止)

1)    函数:void exit(int status);

       参数: stauts定义了进程的终止状态,由用户自己传递,父进程可以通过wait来获取该值

2)    函数:void _exit(int status);

        与exit区别:exit是库函数,_exit是系统调用函数,而库函数内部封装了系统调用。 也就是说,调用exit函数最终也会调用_exit来使进程退出,只不过在其调用_exit之前,还会做一些其他的事情

3.2.3 等待进程

概念父进程通过进程等待的方式,回收子进程的资源,获取子进程的退出状态

实际作用:防止僵尸进程的产生。

1)   函数:pid_t wait(int *status);

        返回值:成功会返回被等待进程的pid,失败则会返回-1

        参数:一级指针status,它其实是个输出型参数,用于获取子进程的退出状态,如果不关心则可以设置为NULL.wait是一个阻塞接口,意味着它在等待子进程退出期间是阻塞在函数内部的,直到子进程退出,它获取了子进程的退出状态并回收子进程的资源,才会返回\

2)   函数:pid_t waitpid(pid_t pid, int *status, int options);

返回值: ①等待成功正常返回则返回被等待进程的pid

                ②如果第三个参数options设置成了WNOHANG,而此时没有子进程退出(没有成功等待到子进程),就会返回0,而不是阻塞在函数内部

                ③ 调用出错则返回-1

参数: ① pid,设置成-1则表示等待任意一个子进程,同wait;如果>0则表示等待一个指定的子进程,pid就是被等待子进程的进程号
           ② status,出参,获取子进程的退出状态,同wait
           ③ options,可以设置为0或WNOHANG。设置为0则与wait一样,如果没有等待到子进程退出会一直阻塞;而设置为WNOHANG则表示非阻塞,如果被等待的子进程未退出,则会返回0值,成功等待到子进程则会返回被等待子进程的pid


使用场景:如果使用waitpid接口并设置options参数为WNOHANG,则未等待到子进程退出时也会立即返回,而不是阻塞,因此这种场景我们一般搭配循环来使用,以确保可以成功等待到子进程退出

3.2.4 进程程序替换

进程程序替换其实是替换当前正在运行程序的代码段和数据段,并更新堆栈信息

1)函数 exec函数簇

path/file:要替换的可执行程序的名称,path需要带路径
arg/argv[]:可执行程序的参数,规定其第一个参数必须是可执行程序的名称,并以NULL结尾表示参数传递完毕,二者的区别在于参数是以可变参数列表还是字符数组的方式给出
envp[]:程序员自己组织的环境变量,以数组的方式给出,内部同样需要以NULL结尾,如果传入NULL则认为当前程序没有环境变量

如果以上描述还不是很好理解,那么我们可以再仔细观察下这些函数的区别,可以发现,除了开头都是exec这一共同点之外,其余字母无非就是l或v的区别、有没有p的区别以及有没有e的区别:

l或v的区别:

l表示命令行参数为可变参数列表,传参数时需要以NULL结尾
v表示命令行参数为指针数组,由程序员自己提供
有没有p的区别:是否会去搜索环境变量

有p则代表会去环境变量PATH去搜索当前要替换程序所在的位置
没有p则意味着不会去搜索环境变量,需要程序员自己提供想要替换的程序所在的路径
有没有e的区别:是否需要程序员自己组织环境变量

没有e则表示不需要程序自己组织环境变量,内核会将当前的环境变量继承下来
有e则代表需要程序员自己组织环境变量,如果直接传递NULL则表示当前程序没有环境变量;如果自己组织环境变量,则指针数组中得到环境变量需要以NULL结尾

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值