关于IPC (进程间通信)

作者:fanxin | 分类: 大话技术 | 标签: | 日期:2009-03-18

在操作系统中,进程间通信一般有以下几种: 共享内存,信号量,管道,消息队列等 。

1. 共享内存 。 共享内存一般适合于进程间共享大量的数据。 如oracle中的SGA就是用了共享内存来实现数据交换的 ,由于多个进程共同存取共一份数据,所以引进了 lock ,latch 锁机制来保证数据的完整性。

共享内存实现一般有如下步骤:

1) 进程A向操作系统申请了一块内存区域用作共享内存,并给这块内存赋予一个键值(key),例如我现在申请一块键值为123456 的共享内存,代码如下

shmid=shmget((key_t)123456,sizeof(struct shm_buffer),0666|IPC_CREAT);

2) 进程A将共享内存隐射到自己的地址空间

shared_mem=shmat(shmid , (void *) 0 ,0) ; // 这里是让 操作系统自己选择映射过来的地址空间 ,oralce 里面就是指定了的

3 ) 现在进程A就可以对这块内存进行读写了 。其他进程如果要访问这块共享内存, 那么第一步需要根据键值获取这块内存的shmid ,然后下面同样的步骤,连接共享内存到自己的地址空间,并进行相应的操作

看完上面的原理我们就很容易想明白怎么自己写程序去读取oracle 的SGA ,第一步找出 oracle 共享内存的 shmid ,这可通过 操作系统看出来或通过oracle 的提工的工具 , 第二步将oracle SGA 链接到进程自己的地址空间中,至于SGA里面

的具体内容数据结构等 需要自己去hack了 , oralce数据字典里面也提供了部分。

2. 信号量

这里所说的信号量就可以简单的理解为一个变量 , 用来标志一个锁的状态。与信号量相关的操作有P,V 原子操作。信号量一般用来协调进程间的工作。

举个例子 , oracle 中如何协调 用户进程和 lgwr 之间的工作呢 ? 用户进程A发出 commit 操作, 此时要通知 lgwr 把 redo buffer 中的数据写出去, 用户进程A等待lgwr 完成工作 , lgwr 写完以后还要通知

进程A 我工作做完了 ,此时用户进程A继续工作 。这一系列的进程间的同步操作可以通过P,V操作信号量完成。

先介绍下P,V 操作吧 。P 操作可以看成是原子的把一个信号量减1, 当信号量的值小于等于0时,进程进入睡眠; V 操作可以看成吧一个信号量原子的加1,并把等待此信号量的队列中取一个进程唤醒。

那么上面lgwr 和 用户进程的代码之间的协调可以用代码描述为 :

首先定义两个信号量 i,j , i 为用户进程信号量,j为lgwr 的信号量

用户进程代码commit描述为:

commit()

{

………;

v(j); // 唤醒lgwr

p(i); // 等待lgwr 完成,睡眠

}

lgwr 代码协调代码描述为

lgwr()

{

write buffer to online redo log;

v(i); // 唤醒 用户进程

p(j); // 休眠 ,等待用户进程的任务

}

3 管道

管道应该在我们平时的工作中用的是最多的 。

例如:

1) ps -ef | grep “xxxx”

2) expect 程序 ,批量fdisk 的时候会用到

3) 大家还会想到我们在windows 里面sqlplus 命令进去后 可以用 上下键 翻出我们以前运行过的命令 ,非常方便,但linux 下就不行,其实这个功能我们也都可以通过管道来实现的 .

管道如何实现就不多讲了 ,可以看操作系统代码 ,linux 在 fs/pipe.c 。 这么主要讲如果利用管道实现上述功能.

实现上面功能简单的讲就是重定向某个进程的标准输入,输出操作.那么怎么来重定向呢 ? 基本上用到几个函数 pipe() ,fork() , dup() ,execve() 。

1. 首先利用pipe 函数生成一对读写管道的文件描述符

2. fork 生成子进程 ,注意, 子进程会继承父进程的文件描述符的 ,此时父进程,子进程都有了指向管道的读写文件描述符

3. dup , dup 系统调用是复制一个文件描述符到用户文件描述符表的第一个空槽位。标准输入占据槽位0,标准输出占据槽位1。 因为如果我们想替换子进程的标准输入,那么首先关闭子进程的标准输入

close(0);

然后 dup() 把管道的读文件描述符复制到 标准输入的槽位上 ,此时就替代了 子进程的 标准输入了 ,以后子进程的的读就重定向到了管道。

4. execve() 。 上面介绍只是替换子进程的标准输入输出,那么如果替换指定进程的标准输入输出呢 。 可以用 execve() 这个函数。 该函数可以把子进程的代码段全部替换掉,相当于子进程就变成execve()

里面执行的程序了 。 比如execve(”sqlplus”…) 这就替换了sqlplus 的标准输入输出.

理解了上面的原理就可以很容易的实现上面的功能. 限于篇幅,具体实现方法就不再叙述,可以联系我一起讨论 .

5人发表了评论  ↓发表评论↓
  • 下面是旺旺的补充,一并贴上来

    看完上面的原理我们就很容易想明白怎么自己写程序去读取oracle 的SGA ,第一步找出 oracle 共享内存的 shmid ,这可通过 操作系统看出来或通过oracle 的提工的工具 , 第二步将oracle SGA 链接到进程自己的地址空间中,至于SGA里面的具体内容数据结构等 需要自己去hack了 , oralce数据字典里面也提供了部分。

    –补充

    由于Oracle的所有server process 都是Oracle自身的一个init进程(sid = 0,Oracle9i中)的子进程, 因此他们可以直接从父进程继承得到对应的shared memory空间(也包括这个shmid信息. 在linux系统里面可以通过使用strace来跟踪得到shm的相关的system call.

    –补充

    2. 信号量

    一般的翻译都叫信号灯(Semaphores), 其实一般的实现都是内存中的一个特定的位, 操作的时候使用的操作系统的操作一般称之为spin_lock(自旋锁), 里面具体实现的操作是test and set 模式(也就是Oracle内部的大部分latch的实现方式, 只不过Oracle的实现好像不是使用的system call, 而是自己来实现的,这样可以避免不必要的system call,也就是减少进程切换到kernel模式的次数。

    2. fork 生成子进程 ,注意, 子进程会继承父进程的文件描述符的 ,此时父进程,子进程都有了指向管道的读写文件描述符

    – 补充

    在linux系统下, 子进程会继承父进程的进程空间(会改变进程队列的信息以及pid/ppid等信息). 内存的管理方式是使用COW(copy on write)模式, 也就是如果子进程自己不修改, 它看到的东西就是父进程fork它的时候的整个address space, 当然也包括父进程打开的pipe的文件描述符, 对于pipe的处理, 一般是,父进程关闭pipe的一遍读写, 子进程关闭掉pipe打开的那两个文件描述符的另一端..

    3. dup , dup 系统调用是复制一个文件描述符到用户文件描述符表的第一个空槽位。标准输入占据槽位0,标准输出占据槽位1。 因为如果我们想替换子进程的标准输入,那么首先关闭子进程的标准输入

    close(0);

    补充:

    标准出错占据槽位2, 所以我们写脚本的时候,如果想脚本完全不受terminal影响, 最好将0,1,2 三个描述符都做重定位.

    fanxin @ March 18, 2009 |

  • 关于 test and set 类似指令的说明

    Test and set 其实是CPU 提供的一个指令 ,以此用来实现原子操作 ,其实现代的CPU 基本都提供类似的指令 , 为什么需要这样的指令呢 ?

    一般我们通过一个标志位来判断某个状态都用如下代码

    Int flag=0

    If(flag ==0)

    { flag=1}

    即用两条指令来实现 ,第一条指令为 条件判断指令 (if) ,第二天指令为设置状态指令 , 在多进程的操作系统中 , 上面的代码显然是存在问题的 , 如进程A 刚执行了if 语句并满足了条件 ,但虽然被操作系统给切换掉了 ,此时进程B执行该代码,发现

    也是满足条件,从而导致问题 。

    究其原因是 判断 和 设置 是通过两条指令来完成的 , 当然也是可以通过复杂的算法来实现一致性 ,但是这个代价太大了 。

    在单CPU 的系统中 ,实现这样的原子操作 可以通过禁止CPU 响应中断来实现 ,即:

    Cli // 屏蔽CPU 中断引脚 ,此时CPU 将不响应外部中断控制器的请求

    代码

    Seti // 恢复CPU 中断引脚

    但是在多个CPU 的系统中 , 上面的禁止CPU 中断显然是行不通的 , 总不可能禁止所有CPU 吧 , 因此CPU 就提供了这样的硬件指令把判断和设置 在一条指令中做掉 ,一条指令周期中 ,是不可中断了,而且实现了原子操作 。

    fanxin @ March 18, 2009 |

  • fjj深入牛B

    fcp @ March 18, 2009 |

  • 大师此评论更NB啊,嘿嘿

    sky @ March 31, 2009 |

  • 深入浅出。

    dbconf @ March 1, 2010 |

表情:<( ̄︶ ̄)> | (⊙ˍ⊙) | >﹏< | b( ̄▽ ̄)d | (─.─||) | (^_-)

[ Ctrl+Enter提交 ]

DBA