MyException - 我的异常网
当前位置:我的异常网» 操作系统 » 过程标识符操作函数

过程标识符操作函数

www.MyException.Cn  网友分享于:2013-09-28  浏览:0次
进程标识符操作函数
    每个进程都有一个非负整型表示的唯一进程 ID。虽说是唯一的,但进程 ID 是可复用的,当一个进程终止时,其进程 ID 就成为复用的候选者。多数 UNIX 系统使用延迟复用算法,使得赋予新建进程的 ID 不同于最近终止进程的 ID,以免将新进程误认为是使用同一 ID 的某个已终止的先前进程。
    系统中有一些专用进程,但具体细节随实现而不同。ID 为 0 的进程通常是调度进程,常常被称为交换进程。该进程是内核的一部分,并不执行任何磁盘上的程序,因此也被称为系统进程。进程 ID 1 通常是 init 进程(在 Mac OS X 10.4 中是 launchd 进程),在自举过程结束时由内核调用,以启动一个 UNIX 系统。该进程的程序文件一般是 /etc/init 或 /sbin/init,它通常读取与系统有关的初始化文件,如 /etc/rc* 文件、/etc/inittab 文件 和 /etc/init.d 中的文件等,并将系统引导到一个状态(如多用户)。init 进程不会终止,它是一个普通的用户进程而非内核中的系统进程,但它是以超级用户特权运行的。此外,每个 UNIX 系统实现都有它自己的一套提供操作系统服务的内核进程,例如,在某些 UNIX 的虚拟存储器实现中,进程 ID 2 是页守护进程,负责支持虚拟存储器系统的分页操作。
    除了进程 ID,每个进程还有其他一些标识符。下列函数可返回这些标识符(它们都没有出错返回)。
#include <unistd.h>
pid_t getpid(void);          /* 返回值:调用进程的进程 ID */
pid_t getppid(void);         /* 返回值:调用进程的父进程 ID */

uid_t getuid(void);          /* 返回值:调用进程的实际用户 ID */
uid_t geteuid(void);         /* 返回值:调用进程的有效用户 ID */
gid_t getgid(void);          /* 返回值:调用进程的实际组 ID */
gid_t getegid(void);         /* 返回值:调用进程的有效组 ID */


    一个现有进程可以调用 fork 函数创建一个新的子进程(某些平台提供了 fork 的几种变体,比如 vfork 以及 Linux 3.2.0 提供的 clone 系统调用,它允许调用者控制哪些部分由父进程和子进程共享)。
#include <unistd.h>
pid_t fork(void);  /* 返回值:子进程返回 0,父进程返回子进程 ID;若出错,返回 -1 */

    fork 函数被调用一次,但返回两次:子进程返回 0,而父进程返回新建子进程的进程 ID。子进程和父进程会继续执行 fork 调用之后的指令。子进程是父进程的副本,例如,子进程获得父进程的数据空间、堆和栈的副本,而不是共享这些存储空间部分,但子进程和父进程共享正文段。不过由于在 fork 之后经常跟随着 exec,所以现在很多实现并不执行一个父进程数据段、堆和栈的完全副本,而是使用了写时复制(Copy-On-Write,COW)技术。这些区域由父进程和子进程共享,而且内核将它们的访问权限改变为只读。如果父进程和子进程中的任一个试图修改这些区域,则内核只为修改区域的那块内存制作一个副本,通常是虚拟存储系统中的一页。
    下面是一个 fork 函数使用示例,从中可以看到子进程对变量的修改并不影响父进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int globval = 6;	// external variable in initialized data.
char buf[] = "a write to stdout\n";

int main(void){
	int	var = 88;	// automatic variable on the stack
	pid_t pid;
    // 不写末尾的 null 字节
	if(write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1){
		printf("write error\n");
		exit(2);
	}
	printf("before fork\n");	// we don't flush stdout

	if((pid=fork()) < 0){
		printf("fork error\n");
		exit(2);
	}else if(pid == 0){		// child
		globval++;
		var++;
	}else{				// parent
		sleep(2);
	}

	printf("pid=%ld, glob=%d, var=%d\n", (long)getpid(), globval, var);
	exit(0);
}

    运行结果如下。
$ ./forkDemo.out
a write to stdout
before fork
pid=430, glob=7, var=89           # 子进程的变量值改变了
pid=429, blob=6, var=88
$
$ ./forkDemo.out > temp.out
$ cat temp.out
a write to stdout
before fork                       # 子进程输出一次
pid=432, blob=7, var=89
before fork                       # 父进程输出一次
pid=431, blob=6, var=88
$

    一般来说,fork 之后父进程和子进程的执行先后顺序是不确定的,这取决于内核所使用的调度算法。如果要求父进程和子进程之间相互同步,则要求某种形式的进程间通信。
    本程序中需要注意 fork 与 I/O 函数之间的交互关系。由于 write 函数是不带缓冲的,write 又是在 fork 之前调用,所以其数据写到标准输出一次。但是标准 I/O 库是带缓冲的,如果标准输出连到终端设备,则它是行缓冲的;否则它是全缓冲的。所以当以交互方式运行该程序时,只得到该 printf 输出的行一次,因为标准输出缓冲区由换行符冲洗。而当将标准输出重定向到一个文件时,却得到 printf 输出行两次。这是因为在 fork 之前调用了 printf 一次,但当调用 fork 时,该行仍在缓冲区中,然后在将父进程数据空间复制到子进程中时,该缓冲区数据也被复制到子进程中,此时父进程和子进程各自有了该行内容的缓冲区。在 exit 之前的第二个 printf 将其数据追加到已有的缓冲区中。当每个进程终止时,其缓冲区中的内容都被写到相应文件中。
    另外,还需要注意的是,fork 的一个特性是父进程的所有打开文件描述符都会被复制到子进程中。重要的一点是,父进程和子进程共享同一个文件偏移量(具体可参考文件共享一节)。所以如果上面程序中若没有调用 sleep() 之类的函数来等待子进程退出的话,它们的输出就可能是相互混合的(当然这里调用 sleep 其实也不一定能保证)。
    除了打开文件之外,父进程的其它大部分属性也由子进程继承,比如进程组 ID、实际组 ID、存储映像和资源限制等。
    父进程和子进程的区别主要如下:
    * fork 的返回值不同。
    * 进程 ID 不同。
    * 各自的父进程 ID 不同。
    * 子进程的 tms_utime、tms_stime、tms_cutime 和 tms_ustime 的值设置为 0。
    * 子进程不继承父进程设置的文件锁。
    * 子进程的未处理闹钟被清除。
    * 子进程的未处理信号集设置为空集。
    一般使 fork 失败的两个主要原因是:(a)系统中已经有了太多的进程,(b)该实际用户 ID 的进程总数超过了限制。

文章评论

程序员和编码员之间的区别
程序员和编码员之间的区别
我的丈夫是个程序员
我的丈夫是个程序员
旅行,写作,编程
旅行,写作,编程
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
代码女神横空出世
代码女神横空出世
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
程序员都该阅读的书
程序员都该阅读的书
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
鲜为人知的编程真相
鲜为人知的编程真相
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
 程序员的样子
程序员的样子
漫画:程序员的工作
漫画:程序员的工作
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员必看的十大电影
程序员必看的十大电影
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员的鄙视链
程序员的鄙视链
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
中美印日四国程序员比较
中美印日四国程序员比较
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序员应该关注的一些事儿
程序员应该关注的一些事儿
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
编程语言是女人
编程语言是女人
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
我是如何打败拖延症的
我是如何打败拖延症的
Java程序员必看电影
Java程序员必看电影
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
一个程序员的时间管理
一个程序员的时间管理
那些争议最大的编程观点
那些争议最大的编程观点
老程序员的下场
老程序员的下场
10个调试和排错的小建议
10个调试和排错的小建议
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有