管道
Linux 进程间通信-管道
进程是一个独立的资源分配单位,不同进程之间的资源是相互独立的,没有关联,不能在一个进程中直接访问另一个进程中的资源。但是,进程不是孤立的,不同的进程之间需要信息的交换以及状态的传递,因此需要进程间数据传递、同步与异步的机制。
此篇博文记录管道。
管道pipe
管道是进程间通信的主要手段之一。一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端。管道是一种特殊的文件,它不属于某一种文件系统,而是一种独立的文件系统,有其自己的数据结构。根据管道的适用范围将其分为:无名管道和命名管道。
管道分类
● 无名管道
主要用于父进程与子进程之间,或者两个兄弟进程之间。在linux系统中可以通过系统调用建立起一个单向的通信管道,且这种关系只能由父进程来建立。因此,每个管道都是单向的,当需要双向通信时就需要建立起两个管道。管道两端的进程均将该管道看做一个文件,一个进程负责往管道中写内容,而另一个从管道中读取。这种传输遵循“先入先出”(FIFO)的规则。
● 命名管道
命名管道是为了解决无名管道只能用于近亲进程之间通信的缺陷而设计的。命名管道是建立在实际的磁盘介质或文件系统(而不是只存在于内存中)上有自己名字的文件,任何进程可以在任何时间通过文件名或路径名与该文件建立联系。为了实现命名管道,引入了一种新的文件类型——FIFO文件(遵循先进先出的原则)。实现一个命名管道实际上就是实现一个FIFO文件。命名管道一旦建立,之后它的读、写以及关闭操作都与普通管道完全相同。虽然FIFO文件的inode节点在磁盘上,但是仅是一个节点而已,文件的数据还是存在于内存缓冲页面中,和普通管道相同。
管道的读写
写入管道的数据按到达次序排列。如果管道满,则对管道的写被阻塞,直到管道的数据被读操作读取。对于写操作,如果一次write调用写的数据量小于管道容量,则写必须一次完成,即如果管道所剩余的容量不够,write被阻塞直到管道的剩余容量可以一次写完为止。如果write调用写的数据量大于管道容量,则写操作分多次完成。如果用fcntl设置管道写端口为非阻塞方式,则管道满不会阻塞写,而只是对写返回0。
读操作按数据到达的顺序读取数据。已经被读取的数据在管道内不再存在,这意味着数据在管道中不能重复利用。如果管道为空,且管道的写端口是打开状态,则读操作被阻塞直到有数据写入为止。一次read调用,如果管道中的数据量不够read指定的数量,则按实际的数量读取,并对read返回实际数量值。如果读端口使用fcntl设置了非阻塞方式,则当管道为空时,read调用返回0。
如果管道的读端口关闭,那么在该管道上的发出写操作调用的进程将接收到一个SIGPIPE信号。关闭写端口是给读端口一个文件结束符的唯一方法。对于写端口关闭后,在该管道上的read调用将返回0。
无名管道
用于父进程与子进程之间,或者两个兄弟进程之间。
函数说明
/* 管道 */ #include <unistd.h> /* 建立管道 */ int pipe(int filedes[2]); /* filedes[0]为管道里的读取端 filedes[1]为管道里的写入端 返回值: 成功返回-1,错误返回-1,错误存于errno中 错误代码: EMFILE 进程已用完文件描述词最大量 ENFILE 系统已无文件描述符可用 EFAULT 参数filedes数组地址不合法 */ /* 阻塞设置 */ fcntl(filedes[0], F_SETFL, O_NONBLOCK); fcntl(filedes[1], F_SETFL, O_NONBLOCK); /* 读写 */ read(fd, buf, sizeof(buf)); write(fd, buf, sizeof(buf));
实例
#include <unistd.h> #include <stdio.h> int main() { int p[2]; char buf[20]; char buf0[20]; pid_t pid; strcpy(buf,"Hello World!"); pipe(p); if((pid = fork()) > 0){ printf("This is father process. "); write(p[1], buf, strlen(buf)); close(p[1]); close(p[2]); } else{ printf("This is child process. "); read(p[0], buf0, sizeof(buf0)); printf("%s ", buf0); close(p[0]); close(p[1]); } return 0; }
命名管道
能用于近亲进程之间通信。
函数说明
/* 命名管道 */ #include <sys/types.h> #include <sys/stat.h> /* 建立管道 */ int mkfifo(const char * pathname, mode_t mode); /* pathname 路径,创建管道的位置 mode 打开函数open中的Mode */ /* 打开管道 */ int open(const char *pathname,int oflag,... /* mode_t mode */); /* mode 可选:O_RDONLY | O_WRONLY | O_NONBLOC */ /* 读写 */ int read(int fd, void *buf, int nbyte) int write(int fd, void *buf, int nbyte) /* 关闭 */ close(int handle)
实例
/* fifo_write.c */ #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> int main() { const char* pathname = "m_fifo"; int pipe_fd = -1; int bytes; char buf[20]; strcpy(buf, "hello world!"); mkfifo(pathname, 0777); pipe_fd = open(pathname, O_WRONLY); printf("Process %d result %d ", getpid(), pipe_fd); write(pipe_fd, buf, strlen(buf)); printf("Process %d finished! ", getpid()); return 0; }
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { const char* pathname = "m_fifo"; char buf[20]; int pipe_fd; pipe_fd = open(pathname, O_RDONLY); printf("Process %d result %d ", getpid(), pipe_fd); read(pipe_fd, buf, sizeof(buf)); close(pipe_fd); printf("Process %d finished, read content: %s ", getpid(), buf); return 0; }
参考
http://blog.csdn.net/myarrow/article/details/9037135
http://blog.sina.com.cn/s/blog_4e9440910100yihb.html
http://blog.sina.com.cn/s/blog_67b7d7e401018dvz.html
http://blog.csdn.net/guxch/article/details/6828452
Linux的crontab
如果要让计算机unix系统重复,定期做一件事,我们就会用到crontab.
实质上真正去执行每一个重复任务的是cron,cron是的unix家族的一个后台常驻程序,cron是由cron文件来驱动的,crontab只是用来管理cron文件的,比如给cron file里面添加任务,删除任务,文件里记录了要执行的任务,以及其"时间规则"
crontab的作用,正如crontab的man文档中写的: maintain crontab files for individual users
crontab提供给我们的接口
我们是不需要去直接编辑cron file,修改查看cron file都应该使用crontab
查看当前用户的cron任务
>>> crontab -l
如下是我的cron文件,当然写的比较刻意,关于@hourly , @daily,参考下一节
编辑当前用户的cron任务
>>> crontab -e
crontab会去查看EDITOR环境变量,用这个环境变量指定的编辑器来编辑现有的crontab任务,如果这个环境变量为空,就调用默认的/usr/bin/editor,在我的ubuntu机器上,默认的editor指向了/bin/nano,GNU nano也是GNU的一个编辑器,但是我用不太习惯,把它改成了vim.
>>> export EDITOR="/usr/bin/vim"
这样进去以后就是我熟悉的vim了.
移除cron file
>>> crontab -r
我们可以手动做一下,移除cron file后又装回去,安装使用命令
>>> crontab file
crontab为每一个用户维护自己的cron file,没有root权限的用户只能查看和修改自己的cron任务.
如果有root权限,那么要查看用户wangyu的cron任务,使用 -u 参数:
>>> crontab -u wangyu -l
cron任务,编写cron表达式
cron file中的每一行代表一个任务,也就是一个cron表达式.
cron表达式语法:
# * * * * * command to execute # ┬ ┬ ┬ ┬ ┬ # │ │ │ │ │ # │ │ │ │ │ # │ │ │ │ └───── day of week (0 - 6) (0 to 6 are Sunday to Saturday) # │ │ │ └────────── month (1 - 12) # │ │ └─────────────── day of month (1 - 31) # │ └──────────────────── hour (0 - 23) # └───────────────────────── min (0 - 59)
前5个field指示时间,最后一个表示要用shell执行的command.
举例:
0 1 * * * echo > /var/log/lq1990/error.log
就是一天中00:01的时候,清除error.log,而且是一个月的每一天,一年的每一个月,一周的每一天。
假如我们有个python测试脚本/srv/test.py,想每两个小时执行一次,并且是整点,那么应该这么做
0 */2 * * * /srv/test.py
[例子]
0 * * * * 代表什么?
表示 每个小时的开始第0分钟都执行任务,对于这个时间,有一个预先定义的简写:@hourly. 也就是一个任务语句可以这么写
@hourly command
0 0 * * * ?
表示 每一天的00:00重复执行任务.对于这个时间定义也有预定义的简写:@daily.
[思考]
除了以上两个,还有三个预定义表示,分别是@weekly,@monthly,@yearly,其对应的表示是什么?
和crontab/cron有关的配置文件
对于每个系统用户,如果你指定了相应的cron语句,都会在/var/spool/cron/crontabs/下有一个自己的文件,里面就是你的 cron语句,比如对于用户wangyu,就会有这么一个文件
/var/spool/cron/crontabs/wangyu
我们是不要去直接查看和修改这个文件的,使用crontab.
cron.deny和cron.allow.
两个文件都在/etc/下,默认情况下这两个文件都是没有的.
cron.deny指定了不允许使用crontab的用户,比如我在/etc/下创建cron.deny,并且加上一行wangyu,我的用户名,那当我再使用crontab的时候,出现如下提示:
但当我创建/etc/cron.allow,并且把我的用户名放进去以后,我又可以使用crontab了
当然把系统的用户放进/etc/cron.deny以后,用户只是不能使用crontab了,之前这个用户添加的cron任务还是会一直执行的,可能因为cron和crontab是分开的,cron是不管哪个系统用户设置的任务的.
------------
参考:
http://en.wikipedia.org/wiki/Cron
cron 的 man page.