Linux服务器编程——Linux系统编程之进程通信

进程通信又称IPC

IPC方法

方法:管道(最简单)

            信号(开销最小)

            共享映射区/共享内存(无血缘关系)

            本地套接字(最稳定)

Linux文件类型: -   文件

                               d  目录 

                                l   符号链接    

                                s   套接字        伪文件

                                b    块设备       伪文件 

                                c字符设备       伪文件

                                p管道               伪文件

管道

其本质是:

                    1、伪文件,实质为内核缓冲区

                    2、由两个文件描述符表示,一个表示读端,一个表示写端

                    3、数据从读端流入,从写端流出

管道原理:内核使用环形队列机制,借助内和缓冲区实现

管道局限性:1、数据自己读不能自己写

                         2、数据一旦被读走,便在管道中不存在,不可反复读取

                         3、由于管道采用半双工通信方式,数据只能在一个方向上流动

                          4、只有公共祖先的进程间才能使用管道

匿名管道

适用于:只有公共祖先的进程

pipe函数
作用:创建管道
头文件
函数模型
#include <unistd.h>
int pipe(int pipefd[2])
pipefd[2]:传出参数
读端、写端由自己决定
返回值:0   成功
        非0   错误代码
管道读写规则:
读管道:当管道中有数据,read返回实际读到的字节数
        当管道中没有数据,若写端全关闭,read函数返回0
                          若任有写端打开,阻塞等待
写管道:当读端全关闭时,进程异常终止(SIGPIPE信号)
        有读端打开,若管道未满,写数据,返回写入字节数
                    若管道已满,阻塞(少见)
获取管道缓冲区大小:命令 ulimit -a

fpathconf函数
作用:获取管道缓冲区大小
头文件
#include <unistd.h>

函数原型
long int fpathconf(int fd,int parameter)
paramter:__PC_PIPE_BUF

#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>

int pipe2(int pipefd[2], int flags)

优点:实现手段简单
缺点:单向通信,只有血缘关系进程间使用 

有名管道

命名管道是一种特殊类型的文件。

命名管道和匿名管道区别:

         管道应用的一个限制就是只能在具有亲缘关系的进程间通信,命名管道可以再不相关的进程间交换数据。

mkfifo函数
作用:创建一个FIFO文件
头文件
#include <unistd.h>

函数原型
int mkfifo(const char *filename, mode_t mode)
mode:权限
返回值:成功  0
        -1  失败
可多读端,多写端

共享内存

命名映射区

mmap函数
作用:
头文件:
#include <sys/man.h>

函数原型
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 <sys/mman.h>

函数原型
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)

mmap无血缘关系进程间通信

信号

基本的属性

     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 <sys/types.h>
#include <unistd.h>

函数原型
int kill(pid_t pid, int sig) 

参数:
pid:>0   发送信号给指定进程
    =0   发送信号给 调用kill函数进程属于同一进程组的所有进程
    <0   取pid的绝对值发给对应进程
    =-1  发给进程 有权限发送的系统中所有进程

raise函数
作用:给当前进程发送信号
头文件
#include <sys/types.h>
#include <unistd.h>

函数模型
int raise(int sig)

abort函数
作用:给当前进程发送  异常终止信号   SIGABRT
头文件
#include <sys/types.h>
#include <unistd.h>

函数原型
int abort(void)

软件条件产生信号

alarm函数
每个进程有且只有唯一一个定时器
头文件:
#include <unistd.h>

函数原型
unsigned int alarm(unsigned int seconds)
返回值:
返回0或剩余的秒数,通常alarm(0)返回闹钟剩余秒数,无失败,支持秒级,与进程状态无关

setitimer函数
作用:设置定时器,精度us级
头文件
#include <unistd.h>

函数原型
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 <signal.h>

函数原型
typedef void (*sighandler_t)(int)
sighandler_t signal(int signum, sighandler_t handler)
返回值:SIG_ERR    错误

sigaction函数
头文件
#include <signal.h>

函数原型
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 <unistd.h>

函数原型
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、守护进程退出处理




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值