目录
什么是PIPE?
PIPE注意事项
有名和匿名管道
内核管道通信
管道读函数
管道写函数
管道创建
什么是PIPE?
进程间通信(IPC,Inter-Process Communication)是指在不同进程间进行数据通信和交换的过程。管道(pipe)是一种进程间通信的机制,它是一种单向、先进先出的通信方式。一个进程将数据写入管道,而另一个进程从管道中读取数据。在Linux或Unix系统中,进程间还可以使用共享内存、信号量、消息队列等不同的IPC机制进行通信。通过IPC,进程可以在互不依赖和独立运行的情况下,共享资源和数据,实现协同完成任务。同时,IPC也需要对进程的安全性和稳定性进行保障,以避免出现数据丢失、资源竞争等问题。
PIPE注意事项
管道是进程间通信的一种方式,使用管道需要注意以下事项:
-
管道是单向的:管道只能用于单向数据传输,数据只能由一个进程写入,另一个进程读取。如果两个进程需要进行双向通信,需要创建两个管道。
-
管道是有限的:管道具有固定的缓冲区大小,当缓冲区满时,写入进程会被阻塞,直到读取进程读取数据释放缓冲区。同样的,当缓冲区为空时,读取进程会被阻塞,直到写入进程写入数据。
-
管道是匿名的:管道是匿名的,没有名称或标识符。管道的创建必须在父进程中完成,子进程通过继承父进程的文件描述符来使用管道。
-
管道的传输数据是顺序的:管道中写入的数据按照先后顺序传输,读取进程读取数据的顺序与写入进程写入数据的顺序相同。
-
管道的使用需要进程同步:在使用管道时,需要进行进程同步,以确保数据的正确传输和处理。
总之,管道是一个简单而有效的进程间通信方式,但需要注意以上事项,以确保其正确而安全的使用。
有名和匿名管道
在进程间通信中,有名和匿名是用来描述管道的两种不同类型。
匿名管道只存在于内存中,没有文件名和文件系统路径,因此只能用于相关进程之间的通信。匿名管道由父进程创建,通过fork() 创建子进程,子进程继承了管道的读写文件描述符,父子进程就可以通过管道通信。
有名管道(也称为命名管道)则存在于文件系统中,具有文件名和路径,任何进程都可以打开管道进行通信。有名管道需要在文件系统中显示创建,可以使用mkfifo()函数创建一个有名管道。进程可以打开管道文件并向其中写入数据,或者从中读取数据。
总体而言,匿名管道相对简单、高效,但只能用于具有亲缘关系的进程之间通信;而有名管道相对复杂、灵活,任何进程都可以打开它进行通信,但性能相对差一些。
内核管道通信
管道读函数
这段代码是一个读取管道的函数。它接受一个指向m_inode结构的指针表示管道的i节点、一个缓冲区指针和一个计数值作为参数。
函数使用一个循环,直到读取完所需的字节数为止。在每次循环迭代中,它首先检查管道的大小是否为零。如果是零,则唤醒等待该i节点的进程,并检查是否有写入者。如果没有写入者,函数将返回已读取的字节数。否则,它会将自身进程放入等待队列,并睡眠等待信号。
如果管道的大小非零,函数计算可以从管道中读取的最大字符数chars。它将chars限制为剩余的count和管道大小之间的较小值。然后,它从管道中读取chars个字符,并将其写入缓冲区。读取的字符数和总共读取的字节数都会相应增加。
最后,函数唤醒等待该i节点的进程,并返回已读取的字节数。
int read_pipe(struct m_inode * inode, char * buf, int count)
{
int chars, size, read = 0;
while (count>0) {
while (!(size=PIPE_SIZE(*inode))) {
wake_up(&inode->i_wait);
if (inode->i_count != 2) /* are there any writers? */
return read;
sleep_on(&inode->i_wait);
}
chars = PAGE_SIZE-PIPE_TAIL(*inode);
if (chars > count)
chars = count;
if (chars > size)
chars = size;
count -= chars;
read += chars;
size = PIPE_TAIL(*inode);
PIPE_TAIL(*inode) += chars;
PIPE_TAIL(*inode) &= (PAGE_SIZE-1);
while (chars-->0)
put_fs_byte(((char *)inode->i_size)[size++],buf++);
}
wake_up(&inode->i_wait);
return read;
}
管道写函数
这段代码是一个写入管道的函数。它接受一个指向m_inode结构的指针表示管道的i节点、一个缓冲区指针和一个计数值作为参数。
函数使用一个循环,直到写入完所有的字节数为止。在每次循环迭代中,它首先检查管道的可用空间大小是否为零。如果是零,则唤醒等待该i节点的进程,并检查是否有读取者。如果没有读取者,函数将设置当前进程的信号位,表示管道已满,然后返回已写入的字节数如果有写入的话,否则返回-1。然后,它会将自身进程放入等待队列,并睡眠等待信号。
如果管道的可用空间非零,函数计算可以写入管道的最大字符数chars。它将chars限制为剩余的count和可用空间大小之间的较小值。然后,它从缓冲区中读取chars个字符,并将其写入管道。写入的字符数和总共写入的字节数都会相应增加。
最后,函数唤醒等待该i节点的进程,并返回已写入的字节数。
int write_pipe(struct m_inode * inode, char * buf, int count)
{
int chars, size, written = 0;
while (count>0) {
while (!(size=(PAGE_SIZE-1)-PIPE_SIZE(*inode))) {
wake_up(&inode->i_wait);
if (inode->i_count != 2) { /* no readers */
current->signal |= (1<<(SIGPIPE-1));
return written?written:-1;
}
sleep_on(&inode->i_wait);
}
chars = PAGE_SIZE-PIPE_HEAD(*inode);
if (chars > count)
chars = count;
if (chars > size)
chars = size;
count -= chars;
written += chars;
size = PIPE_HEAD(*inode);
PIPE_HEAD(*inode) += chars;
PIPE_HEAD(*inode) &= (PAGE_SIZE-1);
while (chars-->0)
((char *)inode->i_size)[size++]=get_fs_byte(buf++);
}
wake_up(&inode->i_wait);
return written;
}
管道创建
这段代码是用于创建管道的系统调用函数。它接受一个指向文件描述符数组的指针作为参数。
函数首先尝试在文件表中找到两个未使用的文件结构体file。它会逐个检查文件表的条目,如果找到未使用的文件结构体,则将其计数加1,并将其索引加上文件表的基地址,保存到文件结构体数组f[]中。通过这个过程,函数获取了两个可用的文件结构体。
如果只找到一个可用的文件结构体,那么将第一个文件结构体的计数重置为0。
然后,函数在当前进程的文件指针数组中找到两个未使用的槽位,并将之前获得的文件结构体分别存储到这两个槽位中,并将对应的文件描述符保存到fd[]数组中。
如果只找到一个可用的槽位,那么将该槽位设置为NULL。
如果找不到两个可用的槽位,函数返回-1表示创建管道失败。在这种情况下,需要恢复之前增加的文件结构体的计数,并将其重新设置为未使用状态。
如果成功找到两个可用的槽位,函数调用get_pipe_inode()函数获取一个新的管道i节点。如果获取失败,函数将之前分配的文件结构体和槽位进行清理,并返回-1表示创建管道失败。
如果成功获取管道i节点,函数将管道i节点分别赋值给两个文件结构体的f_inode字段。然后,将两个文件结构体的f_pos字段设置为0,并将第一个文件结构体的f_mode字段设置为1表示读取模式,第二个文件结构体的f_mode字段设置为2(表示写入模式)。
最后,函数使用put_fs_long()函数将两个文件描述符保存到用户空间的fildes数组中,并返回0表示成功创建管道。
int sys_pipe(unsigned long * fildes)
{
struct m_inode * inode;
struct file * f[2];
int fd[2];
int i,j;
j=0;
for(i=0;j<2 && i<NR_FILE;i++)
if (!file_table[i].f_count)
(f[j++]=i+file_table)->f_count++;
if (j==1)
f[0]->f_count=0;
if (j<2)
return -1;
j=0;
for(i=0;j<2 && i<NR_OPEN;i++)
if (!current->filp[i]) {
current->filp[ fd[j]=i ] = f[j];
j++;
}
if (j==1)
current->filp[fd[0]]=NULL;
if (j<2) {
f[0]->f_count=f[1]->f_count=0;
return -1;
}
if (!(inode=get_pipe_inode())) {
current->filp[fd[0]] =
current->filp[fd[1]] = NULL;
f[0]->f_count = f[1]->f_count = 0;
return -1;
}
f[0]->f_inode = f[1]->f_inode = inode;
f[0]->f_pos = f[1]->f_pos = 0;
f[0]->f_mode = 1; /* read */
f[1]->f_mode = 2; /* write */
put_fs_long(fd[0],0+fildes);
put_fs_long(fd[1],1+fildes);
return 0;
}