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

来源:互联网 发布:刷钻平台源码 编辑:程序博客网 时间:2024/05/29 08:00

进程通信又称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 -afpathconf函数作用:获取管道缓冲区大小头文件#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_tint sigemptyset(sigset_t *set);    将某个信号请0int sigfillset(sigset_t *set)      将某个信号集置1int 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函数返回-14、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函数获取指定进程的进程组IDpid_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函数获得进程的会话IDpid_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、守护进程退出处理



 
阅读全文
0 0
原创粉丝点击