# Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

Unix IPC包括：管道(pipe)、命名管道(FIFO)与信号(Signal)

[En]

Pipes can be used for communication between related processes, and named pipes overcome the limitation that pipes do not have names. Therefore, in addition to the functions of pipes, they also allow communication between unrelated processes.

[En]

A pipe is a buffer managed by the kernel, which is equivalent to a note we put into memory. One end of the pipe is connected to the output of a process. This process puts information into the pipeline. The other end of the pipe is connected to the input of a process that takes out the information that has been put into the pipe. A buffer does not need to be very large and is designed as a circular data structure so that the pipeline can be recycled. When there is no information in the pipe, the process reading from the pipe waits until the process on the other side puts the information in. When the pipeline is full of information, the process that tries to put in the information waits until the process on the other side takes out the information. When both processes end, the pipeline automatically disappears.

Linux 中，管道的实现并没有使用专门的数据结构，而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点，而这个 VFS 索引节点又指向一个物理页面而实现的。如下图

·内存中有足够的空间可容纳所有要写入的数据；

·内存没有被读程序锁定。

[En]

The read process of a pipe is similar to the write process. However, a process can return an error message immediately when there is no data or memory is locked, rather than blocking the process, depending on the open mode of the file or pipe. Conversely, the process can sleep in the waiting queue of the index node waiting for the write process to write data. When all processes have completed the pipe operation, the pipe’s index node is discarded and the shared data page is released.

Linux函数原型

#include

int pipe(int filedes[2]);

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

filedes[1]用于写入数据，写入时必须关闭读取端，即close(filedes[0])。

int main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];

if(pipe(fd)  0){                 /* 先建立管道得到一对文件描述符 */
exit(0);
}

if((pid = fork())  0)            /* 父进程把文件描述符复制给子进程 */
exit(1);
else if(pid > 0){                /* 父进程写 */
close(fd[0]);                /* 关闭读描述符 */
write(fd[1], "\nhello world\n", 14);
}
else{                            /* 子进程读 */
close(fd[1]);                /* 关闭写端 */
write(STDOUT_FILENO, line, n);
}

exit(0);
}

FIFO (First in, First out)为一种特殊的文件类型，它在文件系统中有对应的路径。当一个进程以读(r)的方式打开该文件，而另一个进程以写(w)的方式打开该文件，那么内核就会在这两个进程之间建立管道，所以FIFO实际上也由内核管理，不与硬盘打交道。之所以叫FIFO，是因为管道本质上是一个先进先出的队列数据结构，最早放入的数据被最先读出来，从而保证信息交流的顺序。FIFO只是借用了文件系统(file system,命名管道是一种特殊类型的文件，因为Linux 中所有事物都是文件，它在文件系统中以文件名的形式存在。)来为管道命名。写模式的进程向FIFO文件中写入，而读模式的进程从FIFO文件中读出。当删除FIFO文件时，管道连接也随之消失。FIFO的好处在于我们可以通过文件的路径来识别管道，从而让没有亲缘关系的进程之间建立连接

#include
#include

int mkfifo(const char *filename, mode_t mode);
int mknode(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0 );

#include
#include
#include
#include

int main()
{
int res = mkfifo("/tmp/my_fifo", 0777);
if (res == 0)
{
printf("FIFO created/n");
}
exit(EXIT_SUCCESS);
}

gcc –o fifo1.c fifo

$./fifo1 用ls命令查看所创建的管道$ ls -lF /tmp/my_fifo
prwxr-xr-x 1 root root 0 05-08 20:10 /tmp/my_fifo|

FIFO读写规则

1.从FIFO中读取数据： 约定：如果一个进程为了从FIFO中读取数据而阻塞打开了FIFO，那么称该进程内的读操作为设置了阻塞标志的读操作

2.从FIFO中写入数据： 约定：如果一个进程为了向FIFO中写入数据而阻塞打开FIFO，那么称该进程内的写操作为设置了阻塞标志的写操作。

• 忽略信号。随着这一选项的设置，进程将忽略信号的出现。有两个信号 不可以被忽略：SIGKILL，它将结束进程；SIGSTOP，它是作业控制机制的一部分，将挂起作业的执行。
• 恢复信号的默认操作。
• 执行预先安排的信号处理功能。进程可以注册特殊的信号处理函数。当进程接收到信号时，信号处理函数像中断服务例程一样被调用，并且当从信号处理函数返回时，控制权返回到主程序并继续正常执行。
[En]

execute a pre-scheduled signal processing function. Processes can register special signal handling functions. When the process receives a signal, the signal processing function is called like an interrupt service routine, and when returned from the signal processing function, the control is returned to the main program and continues to execute normally.*

[En]

However, signals and interrupts are different. The response and processing of the interrupt occur in the kernel space, while the response of the signal occurs in the kernel space, while the execution of the signal processor occurs in the user space.

• 由于系统调用、中断或异常，当前进程在进入内核空间前夕从内核空间返回到用户空间
[En]

the current process returns from kernel space to user space on the eve of entering kernel space due to system calls, interrupts or exceptions*

• 当前进程在内核休眠后被唤醒时，由于检测到信号，提前返回用户空间。
[En]

when the current process is awakened after going to sleep in the kernel, it returns to user space ahead of time due to the detection of a signal.*

[En]

A signal is a simulation of the interrupt mechanism at the software level. in principle, a process receives a signal in the same way as a processor receives an interrupt request. The signal is asynchronous, and a process does not have to wait for the signal to arrive through any operation. in fact, the process does not know when the signal will arrive.

[En]

The kernel sends a soft interrupt signal to a process by setting the bit corresponding to the signal in the signal field of the process table item in which the process is located. It should be added here that if the signal is sent to a sleeping process, it depends on the priority at which the process enters sleep, and if the process sleeps at an interruptible priority, the process is awakened; otherwise, only the corresponding bits of the signal field in the process table are set, but the process is not awakened. This is important because the time a process checks to see if it receives a signal is when a process is about to return from kernel state to user state, or when a process is about to enter or leave an appropriate low scheduling priority sleep state.

[En]

The kernel processes soft interrupt signals received by a process in the context of that process, so the process must be in a running state. If the process receives a signal to capture, the user-defined function is executed when the process returns to the user state from the kernel state. And the method of executing user-defined functions is very ingenious. The kernel creates a new layer on the user stack, in which the value of the return address is set to the address of the user-defined handler, so that when the process returns to the top of the pop-up stack from the kernel, it returns to the user-defined function. When the function returns and pops out of the top of the stack, it returns to the place where it originally entered the kernel, and then continues to run. The reason for this is that user-defined handlers cannot and are not allowed to execute in kernel state (users can get any permissions if user-defined functions run in kernel state).

SIGCLD信号的作用是唤醒一个睡眠在可被中断优先级上的进程。如果该进程捕捉了这个信号，就象普通信号处理一样转到处理例程。如果进程忽略该信号，则 什么也不做。其实wait不一定放在信号处理函数中，但这样的话因为不知道子进程何时终止，在子进程终止前，wait将使父进程挂起休眠。

http://www.cnblogs.com/vamei/archive/2012/10/10/2715398.html

Original: https://www.cnblogs.com/biyeymyhjob/archive/2012/11/03/2751593.html
Author: as_
Title: Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

(0)