进程
初步理解进程
A进程创建了B进程,则A叫做B的父进程,B是A的子进程,而在进程创建过程中,OS内核一般会把父进程的地址空间拷贝一份给子进程,这样父子进程之间的任何操作都不会影响对方,即他们的资源是独立的。但为了节省资源,常常会用到引用计数,这样就可以使父子进程在读时共用一份相同的资源,而当其中一方要进行写时,才会拷贝要修改的那部分资源。
而在代码上,进程是由一个结构体描述的,即PCB(在Linux中是task_struct),这样把所有进程都放在一个数据结构中,就可以统一的进行管理。
进程状态
- R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列
里。
- S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
(interruptible sleep))。
- D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的
进程通常会等待IO的结束。
- T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可
以通过发送 SIGCONT 信号让进程继续运行。
- X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
我们操作系统往往存在多个进程,这些进程可以近似并行,就行我们可以后台打开音乐听着歌,前台写着代码,而之所以进程能够并行,是因为每个进程都运行一个时间片的时间,然后立马切换到下一个进程,而这个时间片十分短,所以可以达到近似并行的状态。
而在这个过程中,管理进程的主要是两个队列。OS内核有两个指针active指针和expired指针,active指向一个活动队列,其中都是活动的进程,而expired指向的过期队列都是由是时间片耗尽的进程组成,即活动队列的进程时间片耗尽后,如果还有任务,都会进入过期队列,当活动队列为空时,交换活动队列和过期队列。
进程操作
创建进程
#include <unistd.h>
pid_t fork(void);
//返回值:自进程中返回0,父进程返回子进程id,出错返回-1
int main()
{
pid_t pid = fork();
if(pid>0)
{
//子进程代码
xxxxxxx
return 0;
}
//父进程代码
xxxxxxxx
return 0;
}
进程等待
子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏,因此需要父进程等待子进程退出。
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
//返回值: 成功返回被等待进程pid,失败返回-1。
//参数: 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
pid_ t waitpid(pid_t pid, int *status, int options);
//返回值:当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
//参数:
//pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
//status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
//options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
退出状态 (status)
基础替换
fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数
以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
#### #include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);