进程通信又称IPC
方法:管道(最简单)
信号(开销最小)
共享映射区/共享内存(无血缘关系)
本地套接字(最稳定)
Linux文件类型: - 文件
d 目录
l 符号链接
s 套接字 伪文件
b 块设备 伪文件
c字符设备 伪文件
p管道 伪文件
其本质是:
1、伪文件,实质为内核缓冲区
2、由两个文件描述符表示,一个表示读端,一个表示写端
3、数据从读端流入,从写端流出
管道原理:内核使用环形队列机制,借助内和缓冲区实现
管道局限性:1、数据自己读不能自己写
2、数据一旦被读走,便在管道中不存在,不可反复读取
3、由于管道采用半双工通信方式,数据只能在一个方向上流动
4、只有公共祖先的进程间才能使用管道
适用于:只有公共祖先的进程
pipe函数
作用:创建管道
头文件
函数模型
#include
int pipe(int pipefd[2])
pipefd[2]:传出参数
读端、写端由自己决定
返回值:0 成功
非0 错误代码
管道读写规则:
读管道:当管道中有数据,read返回实际读到的字节数
当管道中没有数据,若写端全关闭,read函数返回0
若任有写端打开,阻塞等待
写管道:当读端全关闭时,进程异常终止(SIGPIPE信号)
有读端打开,若管道未满,写数据,返回写入字节数
若管道已满,阻塞(少见)
获取管道缓冲区大小:命令 ulimit -a
fpathconf函数
作用:获取管道缓冲区大小
头文件
#include
函数原型
long int fpathconf(int fd,int parameter)
paramter:__PC_PIPE_BUF
#define _GNU_SOURCE
#include
#include
int pipe2(int pipefd[2], int flags)
优点:实现手段简单
缺点:单向通信,只有血缘关系进程间使用
命名管道是一种特殊类型的文件。
命名管道和匿名管道区别:
管道应用的一个限制就是只能在具有亲缘关系的进程间通信,命名管道可以再不相关的进程间交换数据。
mkfifo函数
作用:创建一个FIFO文件
头文件
#include
函数原型
int mkfifo(const char *filename, mode_t mode)
mode:权限
返回值:成功 0
-1 失败
可多读端,多写端
mmap函数
作用:
头文件:
#include
函数原型
void* mmap(void *addr, int length , int port , int flag, int fd, off_t offset)
参数:
addr:建立的映射区的首地址,由Linux内核指定,直接传NULL
length:创建映射区的大小
port:映射区的权限
PORT_READ
PROT_WRITE
PROT_READ| PROT_WRITE
flags:标志为参数(常用于设定更新物理区域,设置共享,创建匿名映射区)
MAP_SHARED:会将内存所做的修改反映到硬盘
MAP_PRIVATE
fd:文件描述符
offset:映射文件的便宜为4K倍数
返回值:成功 返回映射区首地址
失败 MAP_FAILD 宏
munmap函数
头文件
#include
函数原型
int munmap(void* addr, size_t length)
使用注意事项:
1、创建映射区过程中,隐含一次对文件的读操作
2、当MAP_SHARED,要求映射区的权限<= 文件打开权限。而MAP_PRIVATE则无所谓,因为mmap中的权限是对内存的限制
3、映射区的释放与文件关闭无关,只要映射建立成功,文件可以立即关闭。
4、当映射文件大小为0时,不能创建映射区。
5、munmap传入的地址一定是mmap的返回地址。坚决抵制自增操作
6、文件偏移量必须是4096的倍数
7、mmap创建映射区出错率非常高,一定要检查返回值,确保映射区建立成功后在操作
映射区的缺陷是,每次都创建映射区时一定要依赖一个文件才能实现,通常要open,unlink,close等比较麻烦,因此可以利用匿名映射来代替。
int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0)
基本的属性
1、简单
2、不能携带大量信息
3、满足某个特定条件才发送
与信号相关的事件和状态
产生信号:
按键产生
系统调用 kill raise abort
软件条件 定时器 alarm
命令条件 kill命令
递答:递送且到达
未决:产生和递答之间状态
信号处理方式:
1、执行默认动作
2、忽略(丢弃)
3、捕捉(由用户处理函数)
信号4要素
1、编号 2、名称 3、时间 4、默认处理动作
查看man 7 signal可以查看帮助
kill函数/命令
kill命令:kill -SIGKILL pid
头文件
#include
#include
函数原型
int kill(pid_t pid, int sig)
参数:
pid:>0 发送信号给指定进程
=0 发送信号给 调用kill函数进程属于同一进程组的所有进程
<0 取pid的绝对值发给对应进程
=-1 发给进程 有权限发送的系统中所有进程
raise函数
作用:给当前进程发送信号
头文件
#include
#include
函数模型
int raise(int sig)
abort函数
作用:给当前进程发送 异常终止信号 SIGABRT
头文件
#include
#include
alarm函数
每个进程有且只有唯一一个定时器
头文件:
#include
函数原型
unsigned int alarm(unsigned int seconds)
返回值:
返回0或剩余的秒数,通常alarm(0)返回闹钟剩余秒数,无失败,支持秒级,与进程状态无关
setitimer函数
作用:设置定时器,精度us级
头文件
#include
函数原型
int getitimer(int which, struct itimerval *curr_value)
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value)
参数:
which:制定定时方式
自然定时 ITIMER_REAL SIGALRM 计算自然时间
虚拟空间计时(用户空间) ITIMER_VIRTUAL SIGVTALRM 只计算进程占用CPU时间
运行时间计时(用户+内核) ITIMER_PROF ITIMER_PROF 计算占用CPU及执行系统调用的时间
struct itimerval{
struct timeval it_interval; //下一次定时 ,,两次任务间隔
struct timeval it_value; //当前定时值,用来定时时长
};
struct timeval{
time_t tv_sec; //秒数
suseconds_t tv_usec; //微秒数
};
sigset_t set; //无符号长整形 typedef unsigned long sigset_t
int sigemptyset(sigset_t *set); 将某个信号请0
int sigfillset(sigset_t *set) 将某个信号集置1
int sigaddset(sigset* set, int signum) 将某个信号加入信号集
int sigdelset(sigset* set, int signum) 将某个信号清除信号集
int sigismember(const sigset_t* set, int signum) 判断某个信号在信号集中
sigprocmask函数
作用:屏蔽信号,解除信号,本质读取或修改进程的信号屏蔽字,严格注意:屏蔽信号:只是将信号处理延后执行,而忽略表示将信号丢弃
头文件
函数原型
int sigprocmask(int how, const sigset_t * set, sigset_t *oldset)
参数;set 传入参数,位图,1表示屏蔽
oldset 传出参数 用来保存旧的信号屏蔽集
how:假设当前信号屏蔽字mask
SIG_BLOCK,set表示需要屏蔽的信号
SIG_BLOCK,set表示需要解除的屏蔽信号
SIG_SETMASK,代替原始屏蔽的新的屏蔽
sigpending函数
作用:读取当前进程的未决信号集
函数原型
int sigpending(sigset_t *set)
参数:set 传出参数
signal函数
头文件
#include
函数原型
typedef void (*sighandler_t)(int)
sighandler_t signal(int signum, sighandler_t handler)
返回值:SIG_ERR 错误
sigaction函数
头文件
#include
函数原型
int sigaction(int signum, const struct sigaction * act, struct sigaction *oldact)
作用:修改信号处理动作
参数
struct sigaction{
void (*sa_handler)(int) //函数名
void (*sa_sigaction)(int, siginfo_t *, void *) //当sg_flags被指定为SA_SIGINFO时,使用该型号处理程序(很少使用)
sigset_t sa_mask //用于 处理函数在被调用时屏蔽生效
int sa_flags //0表示默认属性
void (*sa_restorer)(void) //废弃,已不用
};
1、阻塞的信号不支持排队,产生多次,只记录一次。
pause函数
作用:调用该函数可以使进程主动挂起,等待信号唤醒
头文件
#include
函数原型
int pause(void)
返回值:
1、如果信号的默认处理动作是终止进程,则进程终止,pause函数没有机会返回
2、如果信号的默认处理是忽略,进程继续挂起,pause函数不返回
3、如果信号的处理动作是捕捉,则调用完信号处理函数之后,pause函数返回-1
4、pause收到的信号不能被屏蔽,如果被屏蔽就不能唤醒
sigqueue函数对应kill函数,但可想指定进程发送信号的同时携带参数。
sigqueue函数
头文件
函数原型
int sigqueue(pid_t pid, int sig, cinst union sigval value);
union sigval{
int sigval_int;
void *sival_ptr;
};
sigaction函数
见信号捕捉函数
当注册信号捕捉函数,不适用sa_handler 而使用 sa_sigaction.
getpgrp函数
获得当前进程组ID
pid_t getpgrp(void)
getpgid函数
获取指定进程的进程组ID
pid_t getpgid(pid_t pid)
setpgid函数
改变进程默认的所属进程组,通常加入一个现有进程组或创建型进程组
int setpgid(pid_t pid, pid_t pgid)
创建会话有6点:
1、调用进程不能是进程组租场,新进程变为会长
2、该进程变为新进程组组长
3、需要root权限(Ubuntu不需要)
4、新会话丢弃原有控制终端,该回话没有控制终端
5、该调用进程是组长进程
6、建立会话时,先调用fork,父进程种子,子进程调用setpgid
getsid函数
获得进程的会话ID
pid_t getsid(pid_t pid)
srtsid函数
int setsid()
守护进程/精灵进程:Linux后台服务进程,无终端,周期性执行某种任务或等待处理某些发生的条件。
创建守护进程,最关键的是调用setsid函数创建一个新的session,并且成为session leader
1、创建子进程,父进程退出,所有工作都脱离了控制终端
2、在子进程中创建新的会话
setsid函数
3、改变当前目录为根目录 chdir函数
防止占用可卸载的文件系统,也可以换成其他路径
4、重设文件权限掩码
umask函数
防止继承的文件创建屏蔽字拒绝某些权限,增加灵活性
5、关闭文件描述符
继承的打开文件不会用到,浪费资源,不关闭 0,1,2 而是重定向 > /dev/null dup2()
6、开始执行守护进程核心
7、守护进程退出处理