第八章(一) 进程控制
来源:互联网 发布:pixhawk导航算法 编辑:程序博客网 时间:2024/05/22 13:32
第八章 进程控制
进程标志
每个进程都有一个非负整型表示的唯一的进程 ID, 即PID
进程ID是可以 复用 的,即一个进程结束后可给另一个新的用,但往往采用的是延迟复用,即新建的ID不同于最近终止的ID。
ID为 0 的往往是 交换进程
是内核的一部份,并不执行磁盘上任何程序,为系统进程
ID为 1 的常是 init 进程
在自举过程结束时由内核调用,负责在自举内核后启动一个Unix系统。
函数 getpid、 getppid
得到进程ID 和 父进程ID
函数 fork ,子进程与父进程
#include <stdio.h>
int globvar = 6;
char buf[] = "a write to stdout";
int main()
{
intvar;
pid_tpid;
var= 88;
if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!= sizeof(buf)-1)
{
perror();
exit(1);
}
printf("...............");
if((pid=fork())< 0)
{
perror();
exit(1);
}
elseif(pid == 0)
{
globvar++;
var++;
}
else
sleep(2);
printf("pid= %d ; globvar = %d ; var = %d\n",(long)getpid(),globvar,var);
return0;
}
执行:
$./a.out
awrite to stdout
…………………
pid= 300 ; globvar = 7 ; var = 89 子
pid= 299 ; globvar = 6 ; var = 88 父
$./a.out > temp
awrite to stdout
………………….
pid= 302; globvar = 7 ; var = 89 子
………………….
pid = 301 globvar = 6 ; var = 88 父
乍一看,为什么会多了一行printf 的数据呢?
1、 父进程数据不改变是因为除非是环境变量,父子之间的变量才会相互影响
2、 多了一行数据是因为
一、write函数无缓冲区,会直接输出
二、printf函数有缓冲区,当其指向终端时,是行缓冲,其他则是全缓冲
三、因为此时printf为行缓冲,没有碰到换行符,数据存在缓冲区中,父进程被fork后缓冲区也被复制了,所以有两次缓冲区内容的输出
strlen 为函数,计算不包含终止null字节的字符串长度
sizeof 则包含null长度
父进程的所有文件描述符都被<复制>( 说复制,是指父进程和子进程每个相同的文件描述符共享一个文件表项)到子进程中去了,共享偏移量
看到这里,不禁想到 在第三章 I/O 的最后,我对此有过一些想法,这里正好证实了此想法。
在fork之后常有以下两种处理文件描述符的方式:
1、 父进程等待子进程完成。
这样,当子进程终止后,它曾读写过的文件描述符的文件偏移量已经做了更新,并传递给父进程继续使用。
2、 父进程和子进程各自执行不同的程序段。
这种情况下,fork之后父子进程各自关闭他们不使用的文件描述符,不干扰另一方使用描述符。
之前在看管道部分的时候,不明白为什么要父子各自关闭不用的文件描述符,现如今看来,其中一方面可能是因为父子进程的文件描述符共享文件表项,会互相影响偏移量。之前大致知道会影响对方输入输出,现在算是知道了些细节。
这里写了一小串子进程不继承父进程的东西,有几个我不知道的:
1、子进程不继承父进程的文件锁
2、子进程的未处理闹钟被清除
3、子进程的未处理信号集被清除
Fork也有失败的时候:
1、 进程太多
2、 实际用户ID的进程总数超过了系统限制。
用法:
1、比如父进程接受请求,请求到来后使子进程处理请求,父进程继续等待
2、一个进程要执行不同的程序
函数 vfork
返回值和 fork 相同,但语义不同。(书上说此函数已在某些系统中被删除,若是可移植程序不该使用此函数)
vfork保证子进程先运行,在其调用exec或exit中的一个函数前,其在父进程空间中运行(即相当于父进程的代码在执行),这两个中的任意一个函数之后,父进程会恢复运行。
比如这里将 vfork 代替fork 重写上面的 部分代码
#include <stdio.h>
int globvar = 6;
int main()
{
intvar;
pid_tpid;
var= 88;
printf("...............");
if((pid=vfork())< 0)
{
perror();
exit(1);
}
elseif(pid == 0)
{
globvar++;
var++;
_exit(0);
}
printf("pid= %d ; globvar = %d ; var = %d\n",(long)getpid(),globvar,var);
return0;
}
运行:
$ ./a.out
$ ..............
$ pid = ? ; globvar = 7 ; var = 89
正如以上所述,vfork 保证子进程在 exec 或 exit 之前,子程序在父进程空间运行 从而影响了父进程最终输出,且此期间父进程等待直到 exec 或 exit 之后再被唤醒。
函数 exit
这里列举了8种进程终止的方式(第七章中的5种正常 3种异常终止)
但是不管进程最终如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开的文件描述符,释放其所使用的存储器。
如果父进程在子进程之前终止,子进程将如何?
答案是子进程将由 init 进程收养。 大致过程是:
在一个进程终止时,内核逐个检查所有活动进程。以判断它是否是正要终止进程的子进程:如果是,则将其PPID 改为 1。
那如果子进程在父进程之前结束,是否是无声无息呢?
答案是内核会为每个终止子进程保存一定的信息,父进程可以通过调用 wait 或 waitpid 获得这些信息(进程ID、终止状态、进程使用的CPU时间总量)。内核会释放其使用的所有缓存区,
关闭所有打开的文件。
在UNIX中,一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放其所占用的资源)的进程被称为僵死进程。
看到这里,我不禁又起了疑惑, wait或waitpid是干嘛的呢? 不是说内核会释放资源的吗? 难道wait的作用不仅仅是得到子进程的终止信息???
查了下 manpage:
... are used to wait for state changes ( be terminated or be stopped by a signal or be resumed by a signal ) in a child of the
calling process and otbtain information about the child whose state has changed. In the case of a terminated child, performing a wait allows the
system to release the resources associated with the child, or this child remains in a "zombie" state.
即wait之类函数用于等待子进程状态改变,可能是终止状态,中止状态或恢复重启状态; 但是要注意的是, 一个要终止的子进程,若对其使用wait, 可使系统释放其所
占资源; 否则,该子进程将进入 僵死 状态!
可能内核和wait所是否的对象不一样 。。。
一个子进程一旦被init 进程收养,就不会称为僵死进程。 因为但凡有子进程终止,其都会调用wait函数处理其。
函数 wait、 waitpid
当一个进程正常或异常终止时, 内核就向其父进程发送 SIGCHLD 信号。
调用这两个函数的进程会:
若所有子进程都还在运行, 则阻塞;
若有一个子进程终止,正等待父进程获取其终止状态,立即返回
若没有任何子进程,立即出错返回。
函数返回值:
若成功返回 子进程ID; 失败返回 0;
函数区别:
在一个子进程终止前,wait使其调用者阻塞,而waitpid有一选项,可使调用者不阻塞
waitpid并不等待在其调用之后的第一个终止子进程,有若干的选项可以控制其所等待的进程。
之前所学过程中,对于waitpid的使用并不多,只是查阅资料的时候看到过。这里说他不等带第一个终止子进程倒是新奇,不知道有什么作用呢?
waitpid (pid, status, option),pid参数说明
若 pid == -1 , 等待任一子进程。 与wait等效。如果子进程也创了一个子进程呢,那还算在这个 -1 范围里吗? 不算的。
若 pid > 0 , 等待进程ID与pid相等的子进程。
若 pid == 0 , 等待组ID等于调用进程组ID的任一子进程。
若 pid < -1 , 等待组ID等于pid绝对值的任一子进程
option参数说明:(按位或运算)
WCONTINUED 若实现支持作业控制,那么由pid制定的任一子进程在停止后已经继续,但其状态尚未报告,则返回其状态。
WNOHANG 若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时其返回值为0.
WUNTRACED 若某实现支持作业控制,而由pid指定的任一子进程已经处于停止状态,并且其状态自停止以来还未报告过,则返回其状态
两个函数再一次比较,waitpid 比 wait 函数多了几项功能:
1、waitpid可以等待一特定的进程。
2、waitpid 提供一个wait 的非阻塞版本。有时希望获取一个子进程的状态但不想阻塞。
3、waitpid 通过WCONTINUED 、WUNTRACED 来支持作业控制。
学到这里,我又有些不清楚了。什么叫支持作业控制呢? 上网查了一下:
作业控制(jobcontrol)是shell的另一个特性,它允许用户同时运行多个作业而产生,并且根据需求可将前后台的作业进行切换。当启动某个作业时,它通常是运行在前
台,因此该作业是与终端相连接的。利用作业控制这一功能,可将正处于前台工作的作业切换到后台去,在后台该作业可继续运行,并且在前台可以监视另一个作业。如果想关
注一下某个正在后台运行的作业,那么可将其切换到前台工作,以使其又一次与终端相连接。
接下来,书本提供了一种既能父进程不等待子进程,子进程又不成为僵死状态的方法:
不过,首先让我们来捋一捋。
1、 什么时候子进程成为僵死进程?
一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放其所占用的资源)的进程被称为僵死进程
2、 那什么时候子进程会被 init 进程收养呢?
如果父进程在子进程之前终止,子进程将由 init 进程收养。
3、 子进程在被init进程收养后,还会成为僵死进程吗?
不会了,init会处理好它的 - -。
那问题就来了, 我只要父进程在子进程之前完成不久可以了吗?
的确,但是父进程有父进程的事情要干,谁能规定他一定要什么时候完成呢?他就是不服气要慢点呢?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define oops(m) {perror(m);exit(1);}
int main()
{
pid_t pid;
if((pid = fork()) < 0)
oops("fork")
else if(pid == 0)
{
if((pid = fork()) < 0)
oops("fork")
else if(pid > 0)
exit(0); //here is the key point
//------------------------I am the child process. I know that our parent would become init as soon as
//our real parent calls exit(0) in the statement above.Here's where we'd continue excuting, knowing that
//when we're done, init will reap our status.
sleep(2);
printf("I am actually a child process, parent id = %ld\n",(long)getppid());
exit(0);
}
if(waitpid(pid,NULL,0) != pid)
oops("waitpid");
//------------------I am the parent.I can continue excuting because I knwo I am not the parent of the process above.
return 0;
}
根据以上的程序来看,即实现了祖孙一起运行的局面,谁也不用管谁,自个儿走自个儿的就行了。
但是千万别忘了释放掉爷爷的儿子,不然就有个僵死进程出现了。。。
函数 waitid ---没想到还有一个这种类型的函数
类似于waitpid,但是比waitpid更具灵活性
函数形式是: wait(idtype, id, *infop, optiosns)
参数 idtype:
P_PID 、 P_PGID 、 p_ALL 分别表示 特定id, 特定组id中任一进程 , 任一进程忽略id
canshu options:
WCONTINUED 等待一进程,它以前曾被停止,此后又已经继续,但未报告状态。
WEXITED 等待已退出的进程
WNOHAND 如无可用的子进程退出状态,立刻返回而非阻塞
WNOWAIT 不破坏子进程退出状态,该子进程退出状态可由后续的wait waitpid 或 waitid 调用取得。
WSTOPPED 等待一进程,它已经停止,但其状态未报告。
- 第八章(一) 进程控制
- 第八章 进程控制
- 第八章 进程控制
- 第八章 进程控制
- 第八章 进程控制
- apue第八章 进程控制 (1)
- apue第八章 进程控制(2)
- [APUE]第八章 进程控制
- APUE第八章 进程控制
- [APUE]第八章 进程控制
- APUE第八章 进程控制
- APUE 第八章 进程控制
- apue学习笔记(第八章 进程控制)
- apue第八章 进程控制(3) exec函数族
- APUE学习笔记:第八章 进程控制
- AUPE学习第八章------进程控制
- apue 第八章 进程控制 笔记
- 第八章 进程控制 fork函数
- 微软BI架构图
- HDU 1005 Number Sequence
- OJ刷题之时间间隔
- 变量及函数参数的初始化
- Android虚拟机加速
- 第八章(一) 进程控制
- 利用SVNListParentPath增加http浏览仓库根目录的功能
- 动态绑定和静态绑定的简单理解
- C++算法之 判断是否为平衡二叉树 求二叉树的镜像
- 匿名函数自调用(IIFE)
- 装饰类及装饰设计模式
- 博客推广——提交搜索引擎
- 从内核创建用户态线程
- ios新设备(包括iphone6和iphone6 plus)的开发用分辨率和像素