Linux C语言 学习之 进程(2)

进程间通讯

  • 易用性:消息队列>域套接字>管道>共享内存(信号量一起使用)

  • 效率:共享内存>域套接字>管道>消息队列

  • 常用:共享内存、域套接字

1. 无名管道:

特点:只能用于具有亲缘关系的进程之间的通信(父子进程、兄弟进程)

单工通信。具有固定的读端和写端

创建时会返回两个文件描述符,分别用于读写管道

int pipe(int pipefd[2]);

  pid[0]用于读、pid[1]用于写

  一个进程只能使用一个(不能自己写自己读)

  • 读管道

        管道中有数据,read返回实际读到的字节数

        管道中无数据,写端全部被关闭,read返回0;写端没全被关闭,read堵塞等待。

  • 写管道

        管道读端全被关闭,程序异常终止(也可以捕捉SIGIPE信号,是进程不终止)

        管道读端没有全被关闭,管道已满,write堵塞;管道未满,write写入,返回实际写入的字节数

2. 有名管道

特点:可以使非亲缘的两个进程进行通信

通过路径名来操作,文件系统中可见,但内存存在内存中

文件IO来操作有名管道,但不支持leek操作

遵循先进先出的原则

  • int mkfifo(const char*path,mode t mode);mode 文件权限

  • open(const char*path,O_RDONLY)只读

  • open(const char*path,O_RDONLY|O_NONBLOCK)只读 非堵塞

  • open(const char*path,O_WRONLY)只写

  • open(const char*path,O_WRONLY|O_NONBLOCK)只写

不可以通过读写方式打开有名管道

以阻塞方式打开,如果只有一个进程以只读(只写)方式打开,则open堵塞,直到有一个进程以只写(只读)方式打开 一读一写

非堵塞方式则不会堵塞

如果要写入数据小于等于4K,要不全部写入,要不都不写入

3. 信号

信号是在软件层对中断机制的一种模拟,是一种异步通信方式

linux内核通过信号通知用户进程,不同信号代表不同的事件

进程对信号的响应方式:缺省方式、忽略方式、捕捉信号

信号产生

按键、系统调用函数产生、硬件异常、命令行产生(kill)、软件产生(被0除、访问非法内存)

  • SIGHUP:信号在用户终端关闭时产生,通常发给和该终端关联的会话内的所有进程

  • SIGINT:在用户键入INTR字符(ctrl-c)时产生。内核发送此信号到当前终端的所有前台进程

  • SIGQUIT:和SIGINT类似,但是由QUIT字符(ctrl-\)产生

  • SIGILL:在一个进程企图执行一个非法指令时产生

  • SIGSEV:在访问非法内存产生,如野指针、缓冲区溢出

  • SIGPIPE:当进程中往没有读端的管道中写入时产生,代表管道破裂

  • SIGKILL:用来结束进程,且不能被捕捉和忽略

  • SIGSTOP:用来暂停进程,且不能被捕捉和忽略

  • SIGTSTP:用来暂停进程,用户可以键入(ctrl-Z)发出

  • SIGCONT:让进程进入运行态

  • SIGALRM:用于通知进程定时器时间已到

  • SIGUSR1/2:保留给用户程序使用

  • SIGCHLD:子进程状态改变,发给父进程

shell命令:kill [-sig] pid kill -l killall

3.1 发送信号

int kill(pid,sig);

        pid >0指定进程号;0代表同组进程;-1代表所有进程 ;<-1:取绝对值,发给绝对值所对应进程组的所有组成员

int raise(sig); 给自己发信号

3.2 定时器函数

一个进程只能设定一个定时器,时间到产生SIGALRM信号

unsigned int alarm(unsigned int seconds);

        成功返回上个定时器返回时间,失败返回EOF;

useconds_t ualarm(useconds_t usecs, useconds_t interval);(循环发送)

        在usecs微秒后,将SIGALRM信号发送给进程,并且之后每隔interval微秒再发送一次SIGALRM信号。

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);

        (定时发送alarm信号)

  struct itimerval {

          struct timeval it_interval; /* next value */ 周期触发

          struct timeval it_value; /* current value */ 第一次触发};

  struct timeval {

          time_t tv_sec; /* seconds */

          suseconds_t tv_usec; /* microseconds */};

  • which:间歇计时器类型,有三种选择

    • ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。

    • ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。

    • ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。

  • value:负责设定timeout时间

  • ovalue:存放旧的timeout时间,通常用NULL

3.3 pause();

使调用进程(或线程)休眠,直到接受到终止进程的信号,或者接收到信号并从信号捕获函数中返回时,pause才返回

  • 如果信号默认处理过程是终止进程,则进程终止,pause函数没有机会返回

  • 如果信号默认处理过程是忽略,进程继续处于挂起状态,pause函数不返回

  • 如果信号处理过程是捕捉,则调用完信号处理函数之后,pause返回-1

  • pause收到的信号如果被屏蔽,那么pause就不能被唤醒

int sigsuspend(const sigset_t *mask);

将进程的屏蔽字替换由mask给出的信号集,然后挂起进程

3.4 信号捕捉:

改变信号的行为

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

  • 返回值:成功时返回原先的信号处理函数,失败时返回SIG_ERR

  • handler:信号处理函数:SIG_DFL代表缺省方式;SIG_IGN代表忽略信号

  • 该函数由 ANSI 定义,由于历史原因在不同版本的 Unix 和不同版本的 Linux 中可能有不同的行为。因此应该尽量避免使用它,取而代之使用 sigaction 函数。

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

  • act:传入参数,新的处理方式。
  • oldact:传出参数,旧的处理方式。

        struct sigaction{

                void (*sa_handler)(int);

                void (*sa_sigaction)(int, siginfo_t *, void *);

                sigset_t sa_mask;

                int sa_flags;

                void (*sa_restorer)(void);};

  • sa_restorer:该元素是过时的,不应该使用,POSIX.1 标准将不指定该元素。(弃用)
  • sa_sigaction:当 sa_flags 被指定为 SA_SIGINFO 标志时,使用该信号处理程序。(很少使用)

重点掌握:

① sa_handler:指定信号捕捉后的处理函数名(即注册函数)。

② sa_mask: 调用信号处理函数时,所要屏蔽的信号集合(信号屏蔽字)。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。

③ sa_flags:通常设置为 0,表使用默认属性;sa_flags :SA_RESTART:表示如果一个信号到来时进程正在执行某个系统调用(read/write等),执行完信号处理函数后返回系统调用,此系统调用会重新调用而不会出错。

3.5 SIGCHLD:

实现回收子进程

产生条件:

  • 子进程终止时;
  • 子进程接收到SIGSTOP信号停止时;
  • 子进程处于停止态,收到SIGCONT唤醒时。
3.6 信号阻塞

信号阻塞是一个开关动作,指的是阻止信号被处理,而不是阻止信号的产生

信号状态:

  •  信号递达:信号的实际处理过程(或略、执行默认动作、捕捉)
  •  信号未决:从产生到递达之间的状态

信号集

sigset set;自定义信号集

int sigemptyset(sigset_t *set);//将set集合置空

int sigaddset(sigset_t *set,int signo);//将signo信号加入到set集合

int sigdelset(sigset_t *set,int signo);//从set集合中移除signo信号

int sigfillset(sigset_t *set); //将所有信号加入set集

int sigismember(const sigset_t *set, int signum); //测试参数signum 代表的信号是否已加入至信号集里

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);//设定对信号集里的信号处理方式

how:

  • SIG_BLOCK : 附加set到阻塞表,原来的保存在到oldset
  • SIG_UNBLOCK:从阻塞表中删除set中的信号,原来的保存到oldset
  • SIG_SETMASK:清空阻塞表并设置为set,原来的保存到oldset
4. 共享内存(内存映射)

优点:实现了用户空间和内核空间的高效交互方式

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

  • - start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址。

  • - length:映射区的长度。

  • - prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算(“|”)合理地组合在一起。 PROT_EXEC //页内容可以被执行;PROT_READ //页内容可以被读取;PROT_WRITE //页可以被写入PROT_NONE //页不可访问

  • - flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体。 MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件;MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联,(血缘关系直接的通信)。

  • - fd:有效的文件描述词。匿名映射填-1

  • - offset:被映射对象内容的起点。文件偏移量

成功执行时,mmap()返回被映射区的指针,munmap()返回0。

失败时,mmap()返回MAP_FAILED[其值为(void *)-1],munmap返回-1。

注:

  • 当MAP_SHARED ,映射区的权限《=文件打开的权限;MAP_PRIVATE 只需要对文件有读权限即可,不会写入磁盘,且不能进行进程直接的通信

  • 映射文件大小不能为0,否则报总线错误,

  • 文件偏移量必须为0或者4K的倍数,不然报非法参数错误

  • 创建映射区的时候,隐含了一次对映射文件的 读取,将文件内容读取到映射区

  • 映射区的释放与文件关闭无关,只要映射成功,文件可以立即关闭

  • 映射大小可以大于文件大小,但只能访问文件的page的内存地址,否则报总线错误(申请实际最大空间为(文件大小/4K+1)*4K)

  • 注意检查返回值

5. 消息队列

是SystemV IPC对象中的一种

消息队列ID来唯一标识

就是一种消息的列表,可以在消息队列中添加、读取消息

可以按照类型来发送、接收消息

发送端:

  • 申请key

  • 打开/创建消息队列 msgget

  • 向消息队列发送消息 msgsend

接收端:

  • 打开/创建消息队列 msgget

  • 从队列中接收消息 msgrcv

  • 控制消息队列 msgctl

申请key

key_t ftok(const char *pathname, int proj_id);

  proj_id :取值是1到255

  打开/创建消息队列

int msgget(key_t key, int msgflg);

  msgflg:IPC_CREAT 创建; IPC_EXCL 检查是否存在; mode 0666

  向消息队列发送消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

  参数msgflg: 是否阻塞   0 阻塞 ;IPC_NOWAIT 不阻塞(队列满时)

  数据的缓冲区必须要定义为如下结构体:消息长度不包括首类型long

          struct msgbuf {

                  long mtype; /*数据类型必须大于0, must be > 0 */

                  char mtext[1]; /*数据,该数组的长度任意定义*/};

  从队列中接收消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

        msgtyp = 0:接收任意类型的数据 ;>0:接收指定类型的数据 ; <0:接收小于等于绝对值类型的数据

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

  cmd 控制命令  IPC_RMID -》删除

  shell命令:ipcs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丶小破孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值