2017-02-25 20:50:58 jyy305 阅读数 1379
  • 2016 Linux培训教程-Linux基础入门学习

    本课程全面介绍了从linux基础内容,一步步,手把手掌握linux基础知识。让你从课堂即实战,全面了解Linux基础入门课程,学习完可轻松驾驭。 本课程为马哥10年以上积累总结的课程,已经成为业内第一优质课程,多家机构进行模仿,从未被超越。课程中穿插大量企业实战案例,请学员按照linux学习路线图进度逐步学习,学习的同时不要忘记跟着做下linux练习,有不明白的地方建议学习几次。

    13492 人正在学习 去看看 马永亮

一.管道容量:我们通过ulimit -a命令查看到的pipo size定义的是内核管道缓冲区的大小,这个值的大小是由内核设定的;而pipe capacity指的是管道的最大值,即容量,是内核内存中的一个缓冲区。

1.首先我们通过命令来看一下内核管道缓冲区的大小:

2.我们可以通过一个程序来测试管道的最大容量

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<errno.h>
#include<signal.h>
int main()
{
 int pipefd[2];
 if(pipe(pipefd) < 0)
 {
  perror("pipe");
  return -1;
 }
 int ret;
 int size = 0;
 int flags = fcntl(pipefd[1], F_GETFL);
 fcntl(pipefd[1], F_SETFL, flags | O_NONBLOCK); // 设置为非阻塞 
 while (1) 
 {
  ret = write(pipefd[1], "c", 1); 
  if (ret < 0)
  {
   perror("write"); 
   break;
  }
  size++;
 }
  printf("size=%d\n", size);
  return 0; 
}

上述代码中fcntl()函数的相关解释

可以用fcntl 函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志

 参数 F_SETFL:对于filedes设置文件描述符标志。新标志值按第三个参数 (取为整型值)设置

  参数 F_GETFL: 对应于filedes 的文件状态标志作为函数值返回

 参数 O_NONBLOCK:设置为非阻塞。

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

2018-06-01 21:46:08 xiaozuo666 阅读数 288
  • 2016 Linux培训教程-Linux基础入门学习

    本课程全面介绍了从linux基础内容,一步步,手把手掌握linux基础知识。让你从课堂即实战,全面了解Linux基础入门课程,学习完可轻松驾驭。 本课程为马哥10年以上积累总结的课程,已经成为业内第一优质课程,多家机构进行模仿,从未被超越。课程中穿插大量企业实战案例,请学员按照linux学习路线图进度逐步学习,学习的同时不要忘记跟着做下linux练习,有不明白的地方建议学习几次。

    13492 人正在学习 去看看 马永亮

在Linux中,管道是一个比较重要的概念,进程间通信的一种方法,管道分为两种,一种是匿名管道,一种是命名管道。

匿名管道

匿名管道的本质是内核中提供的的一段内存。

匿名管道的的特性:

  • 用于有亲缘关系的进程
  • 半双工,单项通信,双向通信需要两个管道
  • 声明周期随进程
  • 有内置的同步互斥机制
  • 面向字节流

匿名管道的创建非常简单,首先需要定义一个存放两个整形变量的数组:int df[2],用来作为管道的输入端口和输出端口,使用pipe()函数可以创建一个匿名管道,fd作为参数传给pipe,df[0]对应着管道的读端,df[1]对应着管道的写端。
这里写图片描述

匿名管道的读写方式分为阻塞式和阻塞式。

阻塞式:对空管道进行读,阻塞在read中,对满了的管道写,阻塞在write中

非阻塞式:如果关闭所有的写端,对管道再尝试读,read返回0,表示读到了EOF,如果关闭所有的读端,对管道再尝试写,进程会收到SIGPIPE信号

命名管道

命名管道的本质也是内核提供的一段内存,通过一个特殊的文件来标识一个命名管道。

命名管道的创建通过调用mkfifo指令或者mkfifo函数实现

命名管道的特性除了可以使用在任意进程间外,其余的4条和匿名管道相同

2016-03-21 20:25:04 Irean_Lau 阅读数 2797
  • 2016 Linux培训教程-Linux基础入门学习

    本课程全面介绍了从linux基础内容,一步步,手把手掌握linux基础知识。让你从课堂即实战,全面了解Linux基础入门课程,学习完可轻松驾驭。 本课程为马哥10年以上积累总结的课程,已经成为业内第一优质课程,多家机构进行模仿,从未被超越。课程中穿插大量企业实战案例,请学员按照linux学习路线图进度逐步学习,学习的同时不要忘记跟着做下linux练习,有不明白的地方建议学习几次。

    13492 人正在学习 去看看 马永亮

之前学习管道的时候,遇到一个问题,就是linux下的管道到底是如何实现的。

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

当然 这和网上大部分解释一样,并不能令人满意。


深入理解linux内核 P786 中有很好的解释

一下是我自己的理解,不清楚的 请查看上面的书籍。

管道创建 撤销

这里写图片描述
这里写图片描述
1.get_pipe_inode() 为pipefs中的管道 分配索引节点,并初始化
2.为 读/写通道 分配一个文件对象和文件描述符 并设置对应的读写权限
3.把两个文件对象和索引节点连在一起
4.两个文件 描述符返回给用户态进程

撤销
1.用户态进程对和管道相关的一个文件描述符调用close()系统调用,引用计数-1
2.当计数为0 ,调用release()方法 ,释放 管道缓冲页框

2.管道的读写
pipe_read()
1.获取索引节点的i_sem信号量 //我要读了
2.判断缓冲区 是否 空 ,或阻塞 //空不空啊?
阻塞: a .调用prepare_wait() 把当前进程(cur)加到等待队列
b.释放索引节信号量
c.调用 schedule()
d.cur一旦被唤醒,从等待队列中删除 。拷贝所有请求字节 //读
3.释放索引节点的i_sem信号量 //我读完了
4.唤醒管道中所有的写者进程 //你来写吧
5.返回 已经拷贝的字节数目 //读了这么多

pipe_write()
1.获取索引节点的i_sem信号量 //我要写了
2.检查是否至少有一个读进程, //有没有人正在读啊
如果不是,向当前进程发送 SIGPIPE信号,释放i_sem信号量 ,并返回-EPIPE
3.判断是否有足够的写空间, //满不满啊?
是,则向缓冲区 拷贝数据。如果不是非阻塞,释放索引节点并返回—EAGAIN; 如果不是且阻塞, 将当前写操作放入等待队列,释放信号量 ,调用schedule(),一旦被唤醒,返回 3 操作。
4.写入 所有请求的字节 //不满就写
5.释放索引节点信号量 //我写完了
6.唤醒所有等待队列的读进程 //快来读吧
7.返回写入的字节数目(如果没有写入 则返回错误码) //写了这么多

2018-08-14 20:28:25 qq_38410730 阅读数 17339
  • 2016 Linux培训教程-Linux基础入门学习

    本课程全面介绍了从linux基础内容,一步步,手把手掌握linux基础知识。让你从课堂即实战,全面了解Linux基础入门课程,学习完可轻松驾驭。 本课程为马哥10年以上积累总结的课程,已经成为业内第一优质课程,多家机构进行模仿,从未被超越。课程中穿插大量企业实战案例,请学员按照linux学习路线图进度逐步学习,学习的同时不要忘记跟着做下linux练习,有不明白的地方建议学习几次。

    13492 人正在学习 去看看 马永亮

管道是Linux由Unix那里继承过来的进程间的通信机制,它是Unix早期的一个重要通信机制。其思想是,在内存中创建一个共享文件,从而使通信双方利用这个共享文件来传递信息。由于这种方式具有单向传递数据的特点,所以这个作为传递消息的共享文件就叫做“管道”。

在管道的具体实现中,根据通信所使用的的文件是否具有名称,有“匿名管道”和“命名管道”。

 

管道与共享内存的区别

乍一看,感觉管道和共享内存并不是区别很大,这里介绍一下两者之间的区别:

  • 管道需要在内核和用户空间进行四次的数据拷贝:由用户空间的buf中将数据拷贝到内核中 -> 内核将数据拷贝到内存中 -> 内存到内核 -> 内核到用户空间的buf。而共享内存则只拷贝两次数据:用户空间到内存 -> 内存到用户空间。
  • 管道用循环队列实现,连续传送数据可以不限大小。共享内存每次传递数据大小是固定的;
  • 共享内存可以随机访问被映射文件的任意位置,管道只能顺序读写;
  • 管道可以独立完成数据的传递和通知机制,共享内存需要借助其他通讯方式进行消息传递。

也就是说,两者之间最大的区别就是: 共享内存区是最快的可用IPC形式,一旦这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传递,就不再通过执行任何进入内核的系统调用来传递彼此的数据,节省了时间。

 

匿名管道

匿名管道是在具有公共祖先的进程之间进行通信的一种方式。

前面在介绍进程的创建时讲到,由父进程创建的子进程将会赋值父进程包括文件在内的一些资源。如果父进程创建子进程之前创建了一个文件,那么这个文件的描述符就会被父进程在随后所创建的子进程所共享。也就是说,父、子进程可以通过这个文件进行通信。如果通信的双方一方只能进行读操作,而另一方只能进行写操作,那么这个文件就是一个只能单方向传送消息的管道,如下图所示:

进程可以通过调用函数pipe()创建一个管道。函数pipe()的原型如下:

int pipe(int fildes[2]);

与该函数pipe()相对应的系统调用sys_pipe()的原型如下:

asmlinkage int sys_pipe(unsigned long __user * fildes);

从本质上来说,pipe()函数的功能就是创建一个内存文件,但与创建普通文件的函数不同,函数pipe()将在参数fildes中为进程返回这个文件的两个文件描述符fildes[0]和fildes[1]。其中,fildes[0]是一个具有“只读”属性的文件描述符,fildes[1]是一个具有“只写”属性的文件描述符,即进程通过fildes[0]只能进行文件的读操作,而通过fildes[1]只能进行文件的写操作。

这样,就使得这个文件像一段只能单向流通的管道一样,一头专门用来输入数据,另一头专门用来输出数据,所以称为管道。由于这种文件没有文件名,不能被非亲进程所打开,只能用于亲属进程间的通信,所以这种没有名称的文件形成的通信管道叫做“匿名管道”。

显然,如果父进程创建的这种文件只是用来通信,那么它感兴趣的只是该文件所占用的内存空间,所以也就没有必要创建一个正式文件,只需创建一个只存在于内存的临时文件。从这一点来看,匿名管道与共享内存具有共同点,只不过匿名管道时单向通信,而且这个通信只能在亲属进程间进行。

为支持匿名管道,内核初始化时由内核函数kernel_mount()安装了一种特殊的文件系统,在该系统中所创建的都是临时文件。

由于匿名管道是一个文件,所以它也有i节点,其结构如下:

struct inode
{
        ...
        struct file_operations *i_fop;            //文件操作函数集
        struct pipe_inode_info *i_pipe;           //管道文件指针
        ...
};

可以看到,在i节点的结构中有一个pipe_inode_info类型的指针i_pipe,在普通文件中这个指针的值为NULL,而在管道文件中这个指针则只想一个叫做管道节点信息结构的pipe_inode_info,以表明这是一个管道文件。pipe_inode_info的结构如下:

struct pipe_inode_info {
	wait_queue_head_t wait;            //等待进程队列
	unsigned int nrbufs, curbuf;
	struct page *tmp_page;
	unsigned int readers;
	unsigned int writers;
	unsigned int waiting_writers;
	unsigned int r_counter;            //以只读方式访问管道的进程计数器
	unsigned int w_counter;            //以只写方式访问管道的进程计数器
	struct fasync_struct *fasync_readers;
	struct fasync_struct *fasync_writers;
	struct inode *inode;
	struct pipe_buffer bufs[PIPE_BUFFERS];            //缓冲区数组
};

结构中的域bufs就是构成管道的内存缓冲区。该缓冲区用结构pipe_buffer来描述:

struct pipe_buffer {
	struct page *page;                    //缓冲页的结构
	unsigned int offset, len;
	const struct pipe_buf_operations *ops;            //缓冲区的操作函数集指针
	unsigned int flags;
	unsigned long private;
};

从上面的数据结构中可以看到,管道实质上就是一个被当做文件来管理的内存缓冲区。

在创建一个管道的i节点时,结构inode中的域i_fop被赋予rdwr_pipefifo_fops,即管道文件本身是既可读又可写的。rdwr_pipefifo_fops在文件linux/fs/pipe.c中的定义如下:

const struct file_operations rdwr_pipefifo_fops = {
	.llseek		= no_llseek,
	.read		= do_sync_read,
	.aio_read	= pipe_read,
	.write		= do_sync_write,
	.aio_write	= pipe_write,
	.poll		= pipe_poll,
	.unlocked_ioctl	= pipe_ioctl,
	.open		= pipe_rdwr_open,
	.release	= pipe_rdwr_release,
	.fasync		= pipe_rdwr_fasync,
};

而为进程所创建的打开文件描述符fildes[0]和fildes[1]中的i_fop,则被分别赋予了只读的函数操作集read_pipefifo_fops和只写的函数操作集write_pipefifo_fops。

read_pipefifo_fops和write_pipefifo_fops这两个操作函数集在文件linux/fs/pipe.c中分别定义如下:

const struct file_operations read_pipefifo_fops = {
	.llseek		= no_llseek,
	.read		= do_sync_read,
	.aio_read	= pipe_read,
	.write		= bad_pipe_w,
	.poll		= pipe_poll,
	.unlocked_ioctl	= pipe_ioctl,
	.open		= pipe_read_open,
	.release	= pipe_read_release,
	.fasync		= pipe_read_fasync,
};

const struct file_operations write_pipefifo_fops = {
	.llseek		= no_llseek,
	.read		= bad_pipe_r,
	.write		= do_sync_write,
	.aio_write	= pipe_write,
	.poll		= pipe_poll,
	.unlocked_ioctl	= pipe_ioctl,
	.open		= pipe_write_open,
	.release	= pipe_write_release,
	.fasync		= pipe_write_fasync,
};

创建匿名管道的进程与管道之间的关系如下图所示:

当一个进程调用函数pipe()创建一个管道后,管道的连接方式如下所示:

从图中可以看到,由于管道的出入口都在同一个进程之中,这种管道没有多大的用途的。但是当这个进程在创建一个新进程之后,情况就变得大不一样了。

如果父进程创建一个管道之后,又创建了一个子进程,那么由于子进程继承了父进程的文件资源,于是管道在父子进程中的连接情况就变成如下图一样的情况了:

在确定管道的传输方向之后,在父进程中关闭(close())文件描述符fildes[0],在子进程中关闭(close())文件描述符fildes[1],于是管道的连接情况就变成如下情况的单向传输管道:

也可以想象,通过关闭文件描述符的方法,在两个兄弟进程之间也可以实现通信管道。

创建完管道之后,怎么利用管道来进行数据的通信呢?

管道使用read()和write()函数,采用字节流的方式,具有流动性,读数据时,每读一段数据,则管道内会清除已读走的数据。

  • 读管道时,若管道为空,则被堵塞,直至管道另一端write将数据写入到管道为止。若写段已关闭,则返回0;
  • 写管道时,若管道已满,则被阻塞,直到管道另一端read将管道内数据取走为止。

用close()函数,在创建管道时,写端需要关闭fildes[0]描述符,读端需要关闭fildes[1]描述符。当进程关闭前,每个进程需要将没有关闭的描述符都进行关闭。

匿名管道具有如下特点:

  • 由于这种管道没有其他同步措施,所以为了不产生混乱,它只能是半双工的,即数据只能向一个方向流动。如果需要双方互相传递数据,则需要建立两个管道;
  • 只能在父子进程或兄弟进程这些具有亲缘关系的进程之间进行通信;
  • 匿名管道对于管道两端的进程而言,就是一个只存在于内存的特殊文件;
  • 一个进程向管道中写的内容被管道另一端的进程读取。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读取数据。

匿名管道的局限性主要有两点:一是由于管道建立在内存中,所以它的容量不可能很大;二是管道所传送的是无格式字节流,这就要求使用管道的双方实现必须对传输的数据格式进行约定。

例子:在父子进程之间利用匿名管道通信。

#include <unist.h>
#include <string.h>
#include <wait.h>
#include <stdio.h>

#define MAX_LINE 80

int main()
{
	int testPipe[2], ret;
	char buf[MAX_LINE + 1];
	const char * testbuf = "主程序发送的数据";

	if (pipe(testbuf) == 0) {
		if (fork() == 0) {
			ret = read(testPipe[0], buf, MAX_LINE);
			buf[ret] = 0;
			printf("子程序读到的数据为:%s", buf);
			close(testPipe[0]);
		}else {
			ret = write(testPipe[1], testbuf, strlen(testbuf));
			ret = wait(NULL);
			close(testPipe[1]);
		}
	}
	
	return 0;
}

 

命名管道

由于匿名管道没有名称,因此,它只能在一些具有亲缘关系的进程之间进行通信,这使它在应用方面受到极大的限制。

命名管道是在实际文件系统上实现的一种通信机制。由于它是一个与进程没有“血缘关系”的、真正且独立的文件,所以它可以在任意进程之间实现通信。由于命名管道不支持诸如lseek()等文件定位操作,严格遵守先进先出的原则进行传输数据,即对管道的读总是从开始处返回数据,对它的写总是把数据添加到末尾,所以这种管道也叫做FIFO文件。

同样,由于需要由管道自身来保证通信进程间的同步,命名管道也是一个只能单方向访问的文件,并且数据传输方式为FIFO方式。

也就是说,命名管道提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,在文件系统中产生一个物理文件,其他进程只要访问该文件路径,就能彼此通过管道通信。在读数据端以只读方式打开管道文件,在写数据端以只写方式打开管道文件。

FIFO文件与普通文件的区别:

  • 普通文件无法实现字节流方式管理,而且多进程之间访问共享资源会造成意想不到的问题;
  • FIFO文件采用字节流方式管理,遵循先入先出原则,不涉及共享资源访问。

操作流程为:mkfifo -> open -> read(write) -> close ->unlink。

 

2019-08-09 21:52:46 sexyluna 阅读数 197
  • 2016 Linux培训教程-Linux基础入门学习

    本课程全面介绍了从linux基础内容,一步步,手把手掌握linux基础知识。让你从课堂即实战,全面了解Linux基础入门课程,学习完可轻松驾驭。 本课程为马哥10年以上积累总结的课程,已经成为业内第一优质课程,多家机构进行模仿,从未被超越。课程中穿插大量企业实战案例,请学员按照linux学习路线图进度逐步学习,学习的同时不要忘记跟着做下linux练习,有不明白的地方建议学习几次。

    13492 人正在学习 去看看 马永亮

1. 多命令顺序执行

在这里插入图片描述

  • ;举例
  • 即使中间有一个命令报错,其它命令也会继续执行
[cds@iz2zedek646lh3ouoyvdsmz ~]$ dir;ll;pwd
a.txt  b.txt
total 8
-rw-rw-r-- 1 cds cds 129 Aug  9 20:14 a.txt
-rw-rw-r-- 1 cds cds 141 Aug  9 21:08 b.txt
/home/cds

  • &&

    • a命令 && b命令
    • 当a命令正确执行的后,执行b命令!
  • ls && echo yes || echo no

    • 如果ls执行输出yes
    • 没执行输出no
[cds@iz2zedek646lh3ouoyvdsmz ~]$ ls && echo yes || echo no
a.txt  b.txt
yes

2. 管道符

在这里插入图片描述

  • 将命令1的输出 作为命令2的输入
  • 举例
    • ll | more
    • ls -l命令输出的信息分页展示
    • 相当与简化ls -l > amore a这二个联合命令

Linux管道的理解

阅读数 118

【Linux】管道

阅读数 529

没有更多推荐了,返回首页