Head First C (3) 进程间通信

进程的数据流

标准输出是三大默认数据流之一。顾名思义,数据流就是流动的数据,数据从一个进程流出,然后流入另一个进程。除了标准输入、标准输出和标准错误,还有其他形式的数据流,例如文件连接和网络连接也属于数据流。重定向进程的输出,相当于改变进程发送数据的方向。原来标准输出会把数据发送到屏幕,现在可以让它把数据发送到文件。

进程含有它正在运行的程序,还有栈和堆数据空间。除此之外,进程还需要记录数据流的连向,比如标准输出连到了哪里。进程用文件描述符表示数据流,所谓的描述符其实就是一个数字。进程会把文件描述符和对应的数据流保存在描述符表中。下表中,前两列是描述符表,后一列是我自己加进去的。

#数据流名称
0键盘标准输入
1屏幕标准输出
2屏幕标准错误
3数据库连接进程也可能打开其他形式的数据流

描述符表的前三项万年不变: 0号标准输入, 1号标准输出, 2号标准错误。其他项要么为空,要么连接进程打开的数据流。比如程序在打开文件进行读写时,就会占用其中一项。创建进程以后,标准输入连到键盘,标准输出和标准错误连到屏幕。它们会保持这样的连接,直到有人把它们重定向到了其他地方。

重定向即替换数据流
标准输入/输出/错误在描述符表中的位置是固定的,但它们指向的数据流可以改变。也就是说,如果想重定向标准输出,只需要修改表中1号描述符对应的数据流就行了。

#数据流
0键盘
1屏幕(stories.txt文件)
2屏幕
3数据库连接

所有向标准输出发送数据的函数会先查看描述符表,看1号描述符指向哪条数据流,然后再把数据写到这条数据流中, printf()便是如此。在 Head First C (1) 数据流 一文中提到过,标准错误要用“2 >” 来重定向,因为2是标准错误在描述符表中的编号。在很多操作系统中,也可以 用 “1 > ” 来重定向标准输出。

fileno()返回描述符号
每打开一个文件,操作系统都会在描述符表中新注册一项。假设你打开了某个文件:

FILE *my_file = fopen("guitar.mp3", "r");

操作系统会打开guitar.mp3文件,然后返回一个指向它的指针,操作系统还会遍历描述符表寻找空项,把新文件注册在其中。那么如何根据文件指针知道它是几号描述符呢?答案是调用fileno()函数。

int descriptor = fileno(my_file); //它会返回4
#数据流
0键盘
1屏幕
2屏幕
3数据库连接
4guitar.mp3文件

dup2()复制数据流
如果你想修改某个已经注册过的数据流,比如想让3号描述符重新指向其他数据流,该怎么做?可以用dup2()函数, dup2()可以复制数据流。

dup2(4, 3);
#数据流
0键盘
1屏幕
2屏幕
3数据库连接 guitar.mp3文件
4guitar.mp3文件
错误代码独家秘方

麻烦代码:

pid_t pid = fork();
if (pid == -1) 
{
	fprintf(stderr, "无法克隆进程: %s\n", strerror(errno));
	return 1;
}
if (execle(...) == -1) 
{
	fprintf(stderr, "无法运行脚本: %s\n", strerror(errno)); //重复错误代码
	return 1;
}

简单代码:

#include<stdlib.h> //为了使用exit系统调用,必须包含stdlib.h头文件
void error(char *msg)
{
	fprintf(stderr, "%s: %s\n", msg, strerror(errno));
	exit(1);
}

pid_t pid = fork();
if (pid == -1) 
	error("无法克隆进程");
if (execle(...) == -1) 
	error("无法运行脚本");
练习1

程序把rssgossip.py脚本的输出保存到stories.txt文件中。 程序只搜索一个RSS源, 其他都和newshound一样。 具体解释见上一篇。

int main(int argc, char *argv[])
{
	char *phrase = argv[1];
	char *vars[] = {"RSS_FEED=http://www.cnn.com/rss/celebs.xml", NULL};
	FILE *f = fopen("stories.txt", "w"); //以“写”模式打开stories.txt
	if (!f) //如果f是0,说明无法打开文件
		error("Can't open stories.txt");
		
	pid_t pid = fork();
	if (pid == -1) 
		error("Can't fork process");
	if (!pid)  //这段代码会修改子进程,因为pid是0
	{
		if ( dup2(fileno(f),1)==-1 ) //令1号描述符指向stories.txt文件
			error("Can't redirect Standard Output");
		if (execle("/usr/bin/python", "/usr/bin/python", "./rssgossip.py",phrase, NULL, vars) == -1) 
			error("Can't run script");
	}
	return 0;
}

在这段代码执行结束后,新闻并没有被保存在stories.txt文件中,是因为子进程一旦创立就和父进程没有关系了,当子进程还没完成的任务的时候,父进程已经结束。所以,操作系统必须提供一种方式,让父进程等待子进程完成任务。修改程序:

#include <sys/wait.h>
...
    //这一段代码加到newshound2程序底部
	int pid_status;
	// pid:进程号
	// pid_status:这个变量用来保存进程信息,是一个int指针
	if (waitpid(pid, &pid_status, 0) == -1) 
		error("等待子进程时发生了错误");
	return 0;
}

重定向输入、输出,然后让进程相互等待, 进程间通信就这么简单。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值