unix编程以及xv6系统浅谈(五)进程控制

5.1 进程标志

​ 每一个进程都有一个pid惟一的表示它,虽然是惟一的,可是是能够复用的。c++

​ ID为0的进程一般是调度进程,因为不执行任何磁盘上的程序,因此又是系统进程。网络

​ ID为1的进程一般是init进程,在自举过程结束时由内核调用。异步

​ ID为2的是页守护进程,此进程负责支持虚拟存储器系统的分页操做。函数

pid_t getpid()
    获取调用进程的pid
pid_t getppid()
    获取调用进程的父进程pid
uid_t getuid()
    获取调用进程的用户id
gid_t getgid()
    获取调用进程的组用户id
复制代码

5.2 建立进程

​ 可使用一些函数建立一个进程ui

​ 子进程和父进程会继续执行fork以后的命令,子进程得到父进程数据空间,堆栈的副本,可是不是共享的,父子进程共享的只有正文段spa

​ 子进程还会复制父进程中标准io缓冲区中未清楚的部分命令行

​ 注意因为文件部分的实现,文件描述符指向的文件表项是由内核管理,因此父子进程是会指向一样的文件表项的,除非修改文件描述符,文件表项中包含了文件的偏移量(即任意进程修改事后下一个进程读到的就是修改的内容)!指针

​ 网络服务进程中通常会各自关闭它们不须要的文件描述符,以避免产生没必要要的影响。code

​ 如今的不少实现并不执行一个父进程数据段,堆栈的彻底副本,使用了写时复制的技术。继承

​ 这些区域由父子进程共享,内核将它们的访问权限改变为只读。若是父进程或者子进程中的任意一个试图修改这些区域,则内核只为修改区域那一块内存制做一块副本,一般是虚拟内存中的一页。

​ 事实上,内核也为子进程分配了一段页表,可是这些页表指向父进程,而且标记为只读,当子进程试图写的时候会发生写保护的错误,错误处理方式就是复制出错的内容进入页表。

pid_t fork()
    两种不一样的返回值
    	子进程返回0
    	父进程返回建立的子进程的pid
复制代码

vfork不一样关于fork,通常它是为了调用exec才建立的,因此

​ vfork中:

​ 在父进程的空间中运行,不会像fork同样执行写时复制!其实就是关闭了页表的只读变成可写。

​ 一旦子进程修改数据(除了vfork的返回值),进行函数调用,或者没有exec或者exit就返回均可能带来问题

​ 保证子进程先运行,一旦子进程中调用了exec或者exit以后才会调度父进程恢复运行。

​ 注意exit()函数和_exit()函数的区别,exit()函数会冲洗缓冲区,因此父进程的缓冲区极可能会被清洗。

pid_t vfork()
复制代码

5.3 僵尸进程和孤儿进程

孤儿进程

​ 对于父进程已经终结的进程,它们的父进程都会改变为init进程

​ 操做过程为: 当一个进程终结时,内核会逐个检查全部活动进程,以判断它是不是正要终结的进程的子进程,若是是,则将其的父进程id 改成1

​ 注意,孤儿进程并不会变为僵尸进程,由于它终结的时候init进程一定会调用一个wait函数取得其终止状态。

僵尸进程

​ 终止进程的父进程调用wait或者waitpid时,能够获得一些子进程的信息,包括进程id,终止状态和CPU时间总量。内核会根据这些信息释放终止进程的存储区,关闭其全部打开的文件。

​ 一个已经终止,可是父进程还没有对其进行善后处理的进程称为僵尸进程。

//老子不收尸,儿子变僵尸
//孤儿不用怕,养父是老大
//生个孙子当龙子,哈哈哈
//简单的例子,不须要等待孙子进程,孙子进程也不会变成僵尸进程
if(fork())
{
 //进程1处理进程2结束
  wait(NULL);
}
else
{
    //进程2fork出进程3再终结
    if(fork())
    {
        exit();
    }
    else
    {
        //成功得到养父进程init
    }
    
}
复制代码

5.4 进程结束处理

​ 当一个进程正常或异常终止的时候,内核就会向其父进程发送SIGCHLD信号,这是个异步事件,可能在父进程运行的任什么时候候发生。

//返回终结的子进程id
pid_t wait(int *statloc)
    若是子进程已经终止而且是个僵尸进程,则当即返回而且取得该子进程的状态
    不然wait阻塞直到任意一个子进程终结,因为返回子进程的id,因此总会知道是哪一个子进程终结了
	statloc:
				NULL	不关心终止状态
				整形指针	将终止状态填入地址指向的内存中
pid_t waitpid(pid_t pid,int *statloc,int options)
    pid:
    		-1	等待任意子进程
    		>0	等待进程id与pid相等的子进程终结
    		0	等待组id等于调用进程组id的任一子进程
    		<-1	等待组id等于pid绝对值的任一子进程
    statloc同上
    options:
    		0	阻塞等待
    		或者下面的按或的结果
    			WCONTINUE	
    			WNOHANG		不阻塞直接返回0(若是没有子进程为僵尸进程的话)
    			WUNTRACED
复制代码

​ 注意,除了父进程能够等待子进程的结束,子进程也能够等待父进程的结束

//子进程等待父进程的结束
while(getppid()!=1)
{
    //非阻塞
}
while(getppid()!=1);	//阻塞方式
复制代码

5.5 函数exec

​ 当进程调用一种exec的时候,该进程执行的程序彻底替换成为新程序,而新程序则从其main函数开始执行,由于调用exec并不建立新进程,先后的进程id未改变,exec只是用磁盘上的一个新进程替换了当前进程的正文段,数据段,堆段和栈段。

​ 用exec能够初始执行新的程序,exit函数和wait函数处理终止和等待终止。

exec
//exec(l,v)(p)
//l表示将参数传入到函数参数中
//v表示将参数定义为一个传入到函数的参数中
//p表明须要自定义环境变量,若没有p则继承进程的环境变量
int system(const char *cmdstring)
    //会调用fork,返回该进程的终止状态
    至关于在命令行中直接输入cmdstring
复制代码

5.6 更改用户id和更改用户组id

​ 注意须要有超级权限

int setuid(uid_t uid) //设置用户id int setgid(gid_t gid) //设置用户组id 复制代码
相关文章
相关标签/搜索