管道
无名管道(pipe)
有名管道(fifo)
信号
消息队列 POSIX /system v
信号量 POSIX /system v
共享内存 POSIX /system v
socket(unix域协议)
Pipe:
利用文件系统调用的接口,在内核中实现一种类似水管的
进程间通讯方式,它的特点:
1. 有两端,一端读,一端写
2. 按顺序读,不支持lseek
3. 内容读走了,就没了
4. 它随内核持续性(相应的读写端都关闭了才销毁)
5. 无名管道只能用于具有"亲缘关系"的进程间通讯
("亲缘关系", fork产生的子进程,几乎拷贝了父进程的所有,如果无这种拷贝关系,则无亲缘关系)
一个东西的生命周期:
(0) 随代码块({},函数)持续性(局部变量)
(1) 随程序持续性(随进程持续性)(全局变量)
(2) 随内核持续性(如无名管道)
(3) 随文件系统持续性(文件)
"文件系统调用接口"(read/write,....),前提的是: 文件描述符
1. pipe/pipe2用来在内核创建一个无名管道的
pipe, pipe2 - create pipe
SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);
DESCRIPTION
pipe() creates a pipe, a unidirectional data channel that can be used
for interprocess communication. The array pipefd is used to return two
file descriptors referring to the ends of the pipe. pipefd[0] refers
to the read end of the pipe. pipefd[1] refers to the write end of the
pipe. Data written to the write end of the pipe is buffered by the
kernel until it is read from the read end of the pipe. For further
details, see pipe(7).
If flags is 0, then pipe2() is the same as pipe(). The following val‐
ues can be bitwise ORed in flags to obtain different behavior:
O_NONBLOCK Set the O_NONBLOCK file status flag on the two new open
file descriptions. Using this flag saves extra calls to
fcntl(2) to achieve the same result.
O_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the two new file
descriptors. See the description of the same flag in
open(2) for reasons why this may be useful.
RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is
set appropriately.
NAME
pipe, pipe2 - create pipe
SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);
pipefd: 整数数组。有两个元素,
pipefd[0]:保存是读的那个文件描述符
pipefd[1] : 保存的写的那个文件描述符
返回值:
成功返回0,
失败返回-1, errno被设置
pipe创建的无名管道默认读写方式是阻塞的。
读(如果没有数据,或者写端不存在),会阻塞
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
pipe2同样是用来创建无名管道的,只不过它
在创建这个管道时,可以指定读写方式为
非阻塞
int pipe2(int pipefd[2], int flags);
pipefd:与pipe一样
flags:
O_NONBLOCK:非阻塞方式
0:
FIFO(named pipe, 有名管道)
"有名":在文件系统中有一个路径名(名字), 它是不是就一定存在于硬盘文件系统中呢???
不是的。fifo,它只是在文件系统中有一个名字,它实际存在于内核。
fifo是为了没有亲缘关系的进程间通信的,而在pipe(无名管道)的基础上改进而来的。
man 7 fifo
NAME
fifo - first-in first-out special file, named pipe
DESCRIPTION
A FIFO special file (a named pipe) is similar to a pipe, except that it
is accessed as part of the filesystem. It can be opened by multiple
processes for reading or writing. When processes are exchanging data
via the FIFO, the kernel passes all data internally without writing it
to the filesystem. Thus, the FIFO special file has no contents on the
filesystem; the filesystem entry merely serves as a reference point so
that processes can access the pipe using a name in the filesystem.
The kernel maintains exactly one pipe object for each FIFO special file
that is opened by at least one process. The FIFO must be opened on
both ends (reading and writing) before data can be passed. Normally,
opening the FIFO blocks until the other end is opened also.
A process can open a FIFO in nonblocking mode. In this case, opening
for read-only will succeed even if no-one has opened on the write side
yet, opening for write-only will fail with ENXIO (no such device or
address) unless the other end has already been opened.
Under Linux, opening a FIFO for read and write will succeed both in
blocking and nonblocking mode. POSIX leaves this behavior undefined.
This can be used to open a FIFO for writing while there are no readers
available. A process that uses both ends of the connection in order to
communicate with itself should be very careful to avoid deadlocks.
fifo - first-in first-out special file, named pipe
DESCRIPTION
FIFO(有名管道)与PIPE(无名管道)类似,除了它在文件系统中有一个名字外。
在能够传输数据前, FIFO必须两端(读端和写端)都被打开。
通常情况下,打开一个fifo会阻塞直到该fifo的另外一端也被打开。
一个进程也可以以非阻塞方式(O_NONBLOCK)打开一个fifo,在这种情况下,
如果你是只读打开该fifo,那么会成功(即使写端没有打开),
如果你是只写打开该fifo,那么会失败(errno == ENXIO), 除非读端已经被打开。
fifo的系统调用接口:
mkfifo:
NAME
mkfifo - make a FIFO special file (a named pipe)
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
DESCRIPTION
mkfifo() makes a FIFO special file with name pathname. mode specifies
the FIFO's permissions. It is modified by the process's umask in the
usual way: the permissions of the created file are (mode & ~umask).
A FIFO special file is similar to a pipe, except that it is created in
a different way. Instead of being an anonymous communications channel,
a FIFO special file is entered into the filesystem by calling mkfifo().
Once you have created a FIFO special file in this way, any process can
open it for reading or writing, in the same way as an ordinary file.
However, it has to be open at both ends simultaneously before you can
proceed to do any input or output operations on it. Opening a FIFO for
reading normally blocks until some other process opens the same FIFO
for writing, and vice versa. See fifo(7) for nonblocking handling of
FIFO special files.
RETURN VALUE
On success mkfifo() returns 0. In the case of an error, -1 is returned
(in which case, errno is set appropriately).
NAME
mkfifo - make a FIFO special file (a named pipe)
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
pathname: 要创建的有名管道在文件系统中的
文件名(带路径)
mode:创建的有名管道的权限,有两种方式指定:
(1) 0664
(2) S_IRUSR S_IWUSR S_IXUSR
S_IRGRP, S_IWGRP, S_IXGRP
S_IROTH, S_IWOTH, S_IXOTH
返回值:
如果成功返回0,
如果失败返回-1, errno被设置
signal:
在程序执行过程中,进程可以发送信号给别的进程,也可以收到别的进程
发过来信号。信号在实现时,是用软中断来实现的。在实现时,信号就是
一个整数值,不同的信号值不同,含义也不一样。man 7 signal
Standard signals:
Signal Value Action Comment
────────────────────────────────────────
SIGHUP 1 Term 控制终端检测到挂起操作或控制进程死亡产生该信号
SIGINT 2 Term CTRL+C
SIGQUIT 3 Core CTRL+Z
SIGILL 4 Core 遇到非法指令时产生,Illegal Instruction
SIGABRT 6 Core 调用abort函数产生,Abort signal from abort(3)
SIGFPE 8 Core 浮点运算出错,Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core 内存非法引用时产生,Invalid memory reference
SIGPIPE 13 Term 当pipe是非阻塞方式打开时,如果往管道里写数据时,
没有进程读时,产生该信号。 Broken pipe: write to pipe with no readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term 终止信号。Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign(忽略) 子进程停止或终止运行时产生。
Child stopped or terminated
SIGCONT 19,18,25 Cont 在停止状态继续运行时产生该信号Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background process
The signals SIGKILL and SIGSTOP cannot be caught, blocked,
or ignored.
一个信号的处理方式有三种:
(1) SIG_IGN: ignore 忽略
(2) SIG_DFL: default默认
(3) 用户自己定义的处理函数。这种方式,我们称为"捕捉"信号 catch signal
( 运用函数指针,回调)
在信号上的操作接口:
1. 发送信号
kill
NAME
kill - send signal to a process
SYNOPSIS
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
kill(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
DESCRIPTION
The kill() system call can be used to send any signal to any process
group or process.
If pid is positive, then signal sig is sent to the process with the ID
specified by pid.
If pid equals 0, then sig is sent to every process in the process group
of the calling process.
If pid equals -1, then sig is sent to every process for which the call‐
ing process has permission to send signals, except for process 1
(init), but see below.
If pid is less than -1, then sig is sent to every process in the
process group whose ID is -pid.
If sig is 0, then no signal is sent, but error checking is still per‐
formed; this can be used to check for the existence of a process ID or
process group ID.
For a process to have permission to send a signal it must either be
privileged (under Linux: have the CAP_KILL capability), or the real or
effective user ID of the sending process must equal the real or saved
set-user-ID of the target process. In the case of SIGCONT it suffices
when the sending and receiving processes belong to the same session.
(Historically, the rules were different; see NOTES.)
RETURN VALUE
On success (at least one signal was sent), zero is returned. On error,
-1 is returned, and errno is set appropriately.
NAME
kill - send a signal to a process or a group of processes
kill用来发送一个信号给一个进程或一组进程
SYNOPSIS
#include <signal.h>
int kill(pid_t pid, int sig);
pid: 指定信号的接收者(可能是多个进程)
pid > 0: 接收者进程ID
pid == 0: 发送信号给与调用进程同组的所有进程
pid == -1: 发送信号给调用进程有权限发送的所有进程
pid < -1: 发送信号给组ID等于pid绝对值的所有进程
sig:要发送的信号(如:SIGINT, SIGUSR1, ...)
返回值:
如果成功(至少有一个进程成功接收到信号)返回0
如果失败返回-1, errno被设置
raise 发信号给自己 int raise(int sig); <=> kill(getpid(), sig);
NAME
raise - send a signal to the caller
SYNOPSIS
#include <signal.h>
int raise(int sig);
DESCRIPTION
The raise() function sends a signal to the calling process or thread.
In a single-threaded program it is equivalent to
kill(getpid(), sig);
In a multithreaded program it is equivalent to
pthread_kill(pthread_self(), sig);
If the signal causes a handler to be called, raise() will return only
after the signal handler has returned.
RETURN VALUE
raise() returns 0 on success, and nonzero for failure.
alarm :
NAME
alarm - set an alarm clock for delivery of a signal
SYNOPSIS
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
DESCRIPTION
alarm() arranges for a SIGALRM signal to be delivered to the calling
process in seconds seconds.
If seconds is zero, any pending alarm is canceled.
In any event any previously set alarm() is canceled.
RETURN VALUE
alarm() returns the number of seconds remaining until any previously
scheduled alarm was due to be delivered, or zero if there was no previ‐
ously scheduled alarm.
NAME
alarm - set an alarm clock for delivery of a signal
SYNOPSIS
#include <unistd.h>
每一个进程都有一个闹钟(同一个时刻只有一个闹钟起作用),
你可以设置闹钟的超时时间,一旦你设置的时间到了 ,
闹钟就会闹,(发送一个SIGALRM给自身进程)
unsigned int alarm(unsigned int seconds);
seconds:你要设置的闹钟超时时间
seconds== 0表示cancels alarm,取消闹钟
返回值:
成功返回上一个闹钟的剩余时间(秒数)
alarm(5);
sleep(1);
r = alarm(3);
2. 等待信号的到来
pause
NAME
pause - wait for signal
SYNOPSIS
#include <unistd.h>
int pause(void);
DESCRIPTION
pause() causes the calling process (or thread) to sleep until a signal
is delivered that either terminates the process or causes the invoca‐
tion of a signal-catching function.
RETURN VALUE
pause() returns only when a signal was caught and the signal-catching
function returned. In this case pause() returns -1, and errno is set
to EINTR.
NAME
pause - wait for signal
SYNOPSIS
#include <unistd.h>
int pause(void);
pause用来等待一个信号的到来。 (要收到一个信号并处理它,pause 才返回)
该函数会使调用进程(或线程)休眠直到一个信号的到来
(信号到来,要么终止你的进程,要么执行你的信号处理函数)
3. 捕捉信号(改变信号的处理方式)
signal
sigaction
捕捉信号(改变信号的处理方式)
信号处理方式是可以被遗传的(fork后子进程会继承父进程的信号处理方式)
NAME
signal - ANSI C signal handling
SYNOPSIS
#include <signal.h>
typedef void (*sighandler_t)(int); //新定义了一个类型
sighandler_t: 函数指针。指向的函数是什么类型?指向的函数
返回值为void,带一个int的参数
sighandler_t指向的函数类型,就是你要写的信号处理函数的类型。
sighandler_t signal(int signum, sighandler_t handler);
signum:信号。表示要捕捉的信号
handler:函数指针,指向的函数即为信号signum的新的处理
函数。
返回值:
返回该信号上一次的处理方式(上一个处理函数的指针)
失败返回SIG_ERR
====
NAME
sigaction - examine and change a signal action
struct sigaction结构体表示跟一个信号相关联的处理操作
struct sigaction表示一个信号处理方式的信息的结构体。
struct sigaction {
void (*sa_handler)(int); //指向信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
SYNOPSIS
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_handler;
sigaction(SIGINT, &sa, NULL);
NOTE:
信号是通过软中断来实现的,你的信号处理函数,是在中断上下文(context),
故在信号处理函数中调用的函数必须是线程安全的(可重入的函数).