`
897371388
  • 浏览: 528099 次
文章分类
社区版块
存档分类
最新评论

Linux操作系统分析(10) - 进程通信之管道与信号量

 
阅读更多

提要

Linux中进程间的通信机制主要有:管道FIFO,信号量,消息,共享内存区,套接字。程序员在使用中可以根据不同的需求进行选择。


管道

管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。


下面是一个简单的例子.先看两个linux中常用的命令

grep

功能说明:查找文件里符合条件的字符串。

语  法:grep [-abcEFGhHilLnqrsvVwxy][-A<显示列数>][-B<显示列数>][-C<显示列数>][-d<进行动作>][-e<范本样式>][-f<范本文件>][--help][范本样式][文件或目录...]

补充说明:grep 指令用于查找内容包含指定的范本样式的文件,如果发现某文件的内容符合所指定的范本样式,预设grep指令会把含有范本样式的那一列显示出来。若不指定任何文件名称,或是所给予的文件名为“-”,则grep指令会从标准输入设备读取数据。


ls

功能说明:列出目标目录中所有的子目录和文件。

语 法:ls [选项] [目录名]


通过 " | " 可以创建一个管道,把这两个进程链接在一起。

终端运行

ls | grep in

目录下文件名中带in的文件或文件夹会显示出来:



在linux的内核中实际上执行的操作如下:

1.调用pipe() 系统调用,假设pipe()返回文件描述符3(管道的读通道)和4(管道的写通道)。

2.两次调用fork() 系统调用。

3.两次调用close() 系统调用来释放文件描述符3和4.


使用管道

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int pipe_default[2];

int main()
{
	pid_t pid;
	char buffer[32];

	memset(buffer, 0, 32);
	if(pipe(pipe_default) < 0)
	{
		printf("Faild to create pipe.\n");
		return 0;
	}
	//Child process
	if(0 == (pid =fork()))
	{
		close(pipe_default[1]);
		if(read(pipe_default[0], buffer, 32) > 0)
		{
			printf("Receive data from server, %s!\n", buffer);
		}
		close(pipe_default[0]);
	}else     //Parent process
	{
		close(pipe_default[0]);
		if(-1 != write(pipe_default[1], "Fuck", strlen("Fuck")))
		{
			printf("Send data to client,Fuck!\n");
		}
		close(pipe_default[1]);
		waitpid(pid,NULL,0);
	}
		return 1;


}




主要解释一下几个相关的函数。

int pipe(int filedes[2]);
filedes[0]用于读出数据,读取时必须关闭写入端,即close(filedes[1]);
filedes[1]用于写入数据,写入时必须关闭读取端,即close(filedes[0])


void *memset(void *s, char ch, size_t n);
将s中前n个字节 (typedef unsigned int size_t)用 ch 替换并返回 s 。


pid_t waitpid(pid_t pid,int * status,int options);

暂时停止目前进程的执行,直到有信号来到或子进程结束。


read和write分别向管道中传送数据。


信号量

信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为1就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。

  一个任务要想访问共享资源,首先必须得到信号量,获取信号量的操作将把信号量的值减1,若当前信号量的值为负数,表明无法获得信号量,该任务必须挂起在该信号量的等待队列等待该信号量可用;若当前信号量的值为非负数,表示可以获得信号量,因而可以立刻访问被该信号量保护的共享资源。

  当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加1实现,如果信号量的值为非正数,表明有任务等待当前信号量,因此它也唤醒所有等待该信号量的任务。

PV原子操作的具体定义如下:(好好理解,很重要的啊)
● P操作:如果有可用的资源(信号量值>0),则此操作所在的进程占用一个资源(此时信号量值减1,进入临界区代码);如果没有可用的资源(信号量值=0),则此操作所在的进程被阻塞直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。
● V操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程;如果没有进程等待它,则释放一个资源(即信号量值加1)。

一个实现PV操作的例子。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define DELAY_TIME 5

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};

int initsem(int sem_id, int init_value);
int del_sem(int sem_id);
int sem_p(int sem_id);
int sem_v(int sem_id);

int main(void)
{
	pid_t result;
	int sem_id;
	sem_id = semget(ftok(".",'a'),1,0666|IPC_CREAT);
	init_sem(sem_id, 0);
	result = fork();
	if(result == -1)
	{
		perror("fork error!\n");
	}
	else if(result == 0)
	{
		printf("Child process at id:%d, I run first.\n",getpid());
		sleep(DELAY_TIME);
		sem_v(sem_id);
	}
	else
	{
		sem_p(sem_id);
		printf("Parent process at id:%d\n",getpid());
		sem_v(sem_id);
		del_sem(sem_id);
	}
	return 0;
}

int init_sem(int sem_id, int init_value)
{
	union semun sem_union;
	sem_union.val = init_value;
	if(semctl(sem_id, 0, SETVAL, sem_union) == 1)
	{
		perror("Init semaphore!");
		return -1;
	}
	return 0;
}

int del_sem(int sem_id)
{
	union semun sem_union;
	if(semctl(sem_id, 0, IPC_RMID, sem_union) == 1)
	{
		perror("Delete semaphore!");
		return -1;
	}
}

int sem_p(int sem_id)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = -1;
	sem_b.sem_flg = SEM_UNDO;

	if(semop(sem_id, &sem_b, 1) == -1)
	{
		perror("P operation.");
		return -1;
	}
	return 0;
}

int sem_v(int sem_id)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = 1;
	sem_b.sem_flg = SEM_UNDO;

	if(semop(sem_id, &sem_b, 1) == -1)
	{
		perror("P operation.");
		return -1;
	}
	return 0;
}



在程序中,由于使用了信号量,确保了每次执行子进程运行在父进程之前。



参考

Linux进程间通信(六)---信号量通信之semget()、semctl()、semop()及其基础实验 -http://blog.csdn.net/mybelief321/article/details/9086151

Linux环境进程间通信(一) -http://www.ibm.com/developerworks/cn/linux/l-ipc/part1/

分享到:
评论

相关推荐

    非常宝贵的LINUX学习笔记

    【linux学习笔记-10】Linux进程相关系统调用(3) 【linux学习笔记-11】守护进程daemon 【linux学习笔记-12】守护进程的日志实现 【linux学习笔记-13】基本进程通信--文件锁 【linux学习笔记-14】基本进程通信--信号...

    Linux学习笔记Linux学习资料Linux教程

    【linux学习笔记-10】Linux进程相关系统调用(三).doc 【linux学习笔记-11】守护进程daemon.doc 【linux学习笔记-12】守护进程的日志实现.doc 【linux学习笔记-13】基本进程通信--文件锁.doc 【linux学习笔记-14】...

    Linux 进程通信

    首先,进程间通信至少可以通过传送打开文件来实现,不同的进程...而Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信 方法:管道、消息队列、共享内存、信号量、套接口等等。下面我们将逐一介绍。

    Linux操作系统实验报告

    实验一 熟悉Linux常用命令 实验二 Linux下程序设计基础 实验三 Linux下进程间管道通信 实验四 IPC进程间共享内存通信 实验五 IPC信号量使用 实验六 Linux内存基本原理 实验八 设备驱动程序 实验九 Linux下socket网络...

    进程通信.doc

    因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。管道实例见:pipe_rw.c #include #include #include #include #include int main() ...

    操作系统课程实验.rar

    (3) 通过 Linux 管道通信机制、消息队列通信机制、共享内存通信机制的使用,加深 对不同类型的进程通信方式的理解。 (4) 通过对 linux 的 Posix 信号量的应用,加深对信号量同步机制的理解。 (5)请根据自身情况,...

    操作系统课设 linux 下编程模拟多进程共享临界资源

    开发环境为eclipse+cdt插件, 用消息与共享内存实现信号量的控制 设计内容 要求产生3个进程: 1、两个进程模拟需要进入临界区的用户进程,当需要进入临界区时,显示:“进程x请求进入临界区…”,同时向管理进程...

    Linux内核源代码情景分析 (上下册 高清非扫描 )

    6.8 信号量 第7章基于socket的进程间通信 7.1系统调用socket() 7.2函数sys—socket()——创建插口 7.3函数sys—bind()——指定插口地址 7.4函数sys—listen()——设定server插口 7.5函数sys—accept()——接受...

    Linux内核情景分析

    6.8 信号量 《LINUX内核源代码情景分析(下册)》图书目录如下: -------------------------------------------------------------------------------- 第 7章 基于socket的进程间通信 7.1 系统调用socket() ...

    linux内核源代码情景分析

    《linux内核源代码情景分析》(非扫描电子版本) 第1章 预备知识 1.1 Linux内核简介 1.2 Intel X86 CPU系列的寻址方式 1.3 i386的页式内存管理机制 1.4 Linux内核源代码中的C语言代码 1.5 Linux内核源代码中的...

    Linux内核情景分析(非扫描版)

    6.8 信号量 《LINUX内核源代码情景分析(下册)》图书目录如下: -------------------------------------------------------------------------------- 第7章 基于socket的进程间通信 7.1 系统调用socket() ...

    嵌入式Linux ARM开发课件第九讲

    ARM7~ARM9体系结构体系结构介绍 ARM7(9)TDMI处理器内核...进程间通信: 进程通信的基本概念,管道、信号、消息队列、信号量、共享内存。 网络通讯接口,socket通信编程。 串口通讯程序和编程实践 多线程程序设计 等。

    Linux编程--Linux内核

    1.2.2 什么是操作系统 11 1.2.3 内核数据结构 13 第2章 内存管理 15 2.1 虚拟内存抽象模型 15 2.1.1 请求调页 17 2.1.2 交换 17 2.1.3 共享虚拟内存 18 2.1.4 物理寻址模式和虚拟寻址模式 18 2.1.5 访问控制 18 2.2 ...

    linux网络编程-宋敬彬-part3

    第1章 Linux操作系统概述 2 1.1 Linux发展历史 2 1.1.1 Linux的诞生和发展 2 1.1.2 Linux名称的由来 3 1.2 Linux的发展要素 3 1.2.1 UNIX操作系统 4 1.2.2 Minix操作系统 4 1.2.3 POSIX 标准 4 1.3 ...

    linux网络编程-宋敬彬-part2

    第1章 Linux操作系统概述 2 1.1 Linux发展历史 2 1.1.1 Linux的诞生和发展 2 1.1.2 Linux名称的由来 3 1.2 Linux的发展要素 3 1.2.1 UNIX操作系统 4 1.2.2 Minix操作系统 4 1.2.3 POSIX 标准 4 1.3 ...

    尚观史上Linux嵌入式开发系统课程

    ├day19_匿名管道子父进程通信、有名管道创建、删除.mp4 ├day20_有名管道的特点、在子父进程及非子父进程间操作.mp4 ├day21-01 有名管道的创建、缓冲区大小、阻塞模式、信号.mp4 ├day21-02 有名管道进程间通信....

    嵌入式Linux ARM开发课件第六讲

    ARM7~ARM9体系结构体系结构介绍 ARM7(9)TDMI处理器内核...进程间通信: 进程通信的基本概念,管道、信号、消息队列、信号量、共享内存。 网络通讯接口,socket通信编程。 串口通讯程序和编程实践 多线程程序设计 等。

    Linux高级程序设计 (不完整版只到11.5.2章节)

    以Linux操作系统(内核为2.6版本)为开发平台、GCC 4.0/GDB 6.3为开发调试环境,详细介绍了Linux系统下编程环境及编程工具、文件管理(文件类型、ANSI以及POSIX标准下文件读写操作)、进程管理(创建、退出、执行、...

    LINUX高级程序设计(中文第二版)杨宗德 (1)

    本书以linux操作系统(内核为2.6版本)为开发平台、gcc 4.0/gdb 6.3为开发调试环境,详细介绍了linux系统下编程环境及编程工具、文件管理(文件类型、ansi以及posix标准下文件读写操作)、进程管理(创建、退出、执行、...

Global site tag (gtag.js) - Google Analytics