pause、jobs、setitimer(2)、system v ipc(day12)

一、pause(2)的使用
#include <unistd.h>
int pause(void);
功能:等待信号的到来
返回值:
-1  错误   errno被设置
只有在信号处理函数执行完毕的时候才返回。

利用所学的知识,编码实现sleep函数的功能。
unsigned int psleep(unsigned int seconds);
代码参见    psleep.c

二、信号从产生到处理的全过程
1、进程正在运行,按下ctrl+c键
2、ctrl+c是硬件中断,使用进程切换到内核态。
3、驱动程序将ctrl+c键解释为2号信号
4、在内核态中将进程的PCB的2号信号设置为1.继续执行
5、当进程从内核态回到用户态的时候,检测进程的PCB中有哪些信号到达?如果没有信号到达,直接切换回用户态。如果有信号到达,调用信号的相关处理函数。信号处理函数执行完毕的时候,调用sigreturn(2)返回到内核态。继续第五步。

三、可重入函数
信号处理函数的栈帧是私有的。
信号处理函数和进程的执行是异步的。
如果这两条执行路线出现对共享资源的竞争,这事就大了。
尽量避免竞争。
使我的函数尽量不去访问栈帧以外的资源。
如果函数中使用了全局变量、静态的局部变量、malloc的内存。那么这个函数就是不可重入函数。
可重入函数只能访问栈帧里的内容。如果这个函数只有地洞局部变量,那么这个函数就是可重入函数。
举例说明  信号处理函数和进程竞争共享资源。
代码参见  count.c

四、作业
进程组    有一个或多个进程
父进程    子进程     孙子进程

作业分为前台作业和后台作业,前台作业只有一个,后台作业有多个。
按键产生的信号只能发送给前台作业。

将前台作业转换为后台作业
ctrl+z
后台作业转换为前台作业
fg %作业号
在后台运行作业
bg  %作业号
查看后台作业
jobs
在作业启动的时候,直接将作业放到后台执行
作业&

补充一句:
子进程结束的时候,子进程向父进程发送SIGCHLD信号,父进程收到,就去收尸。

五、使用setitimer(2)设置计时器
系统计时器做了解
系统运行一个进程时候,进程消耗的时间包含三部分
用户时间    进程消耗在用户态的时间
内核时间    进程消耗在内核态的时间
睡眠时间    进程消耗在等待I/O、睡眠等不被调度的时间
内核为系统中的每个进程维护三个计时器
真实计时器   统计进程的执行时间
虚拟计时器   统计进程的用户时间
实用计时器   统计进程的用户时间和内核时间

这三个计时器除了统计功能以外,还可以按照自己的规则,以定时器的方式工作,向进程周期性的发送信号。

利用这个功能设计一个计时器
setitimer(2)
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,
                     struct itimerval *old_value);
功能:设置定时器的间隔值
参数:
which:
ITIMER_REAL:
ITIMER_VIRTUAL: 
ITIMER_PROF:

new_value:指定了定时器的新值
old_value:保存了定时器的旧值
返回值:
0  成功
-1  错误  errno被设置

ITIMER_REAL:真实   SIGALRM
ITIMER_VIRTUAL:虚拟  SIGVTALRM
ITIMER_PROF:实用 SIGPROF

struct itimerval{
     struct timeval it_interval; /* next value */
        struct timeval it_value;    /* current value */
};
struct timeval{
     long tv_sec;                /* seconds */
        long tv_usec;               /* microseconds */

};
秒     微秒
1秒=1000毫秒
1毫秒=1000微秒

举例说明   编写代码实现定时器,起始时间是进程启动3秒,然后每隔0.5秒发送一个SIGALRM信号。
代码参见   timer.c

信号结束了

六、system v  IPC
 消息队列   共享内存     信号量集
 
在内核管理的内存,用于进程间通讯的内存,称为system v ipc object
操作系统需要管理这些对象。
如何查看当前系统里有哪些对象?
ipcs

在操作系统中这些对象,每一个都有自己的id。便于操作系统的管理。
在用户态需要获取这些对象的id。
获取这些对象的id。需要在用户态有一个键值(唯一的)
将键值和id绑定,这样就可以获取到对象的id。

如何获取这个键值?
ftok(3)
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:获取system v ipc的一个键值  key
参数:
pathname:指定一个文件名,这个文件是存在的,可访问的
proj_id:必须是非0.取这个数的有效低8位。
返回值:
-1  错误  errno被设置
返回一个key值。

举例说明  使用ftok(3)获取一个键值
代码参见ftok.c

消息队列
从系统中获取一个消息队列。如果系统里没有,创建消息队列,将这个消息队列的id给我返回。如果有这个消息队列,返回id即可。

msgget(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:获取一个消息队列的id
参数:
key:ftok(3)的返回值
msgflg:
IPC_CREAT:如果不存在创建,存在,不创建
 IPC_EXCL:如果和IPC_CREAT一起指定,存在的时候,报错。
mode:指定了消息队列的权限
返回值:
-1   错误  errno被设置
返回消息队列的id

举例说明  使用msgget(2)从内核获取消息队列
代码参见msgget.c

向消息队列中发送消息和从消息队列获取消息
msgsnd(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, \
        size_t msgsz, int msgflg);
功能:向消息队列发送消息
参数:
msqid:指定了存放消息的消息队列的id。
msgp:指向了消息的地址
msgsz:指定了消息内容的长度
msgflg:
IPC_NOWAIT:非阻塞
0   阻塞
返回值:
成功   0
-1  失败   errno被设置

将一份拷贝追加到消息队列中


ssize_t msgrcv(int msqid, void *msgp,\
     size_t msgsz, long msgtyp,int msgflg);
功能:从消息队列接收消息
参数:
msqid:指定了消息队列的id
msgp:指向了消息的地址
msgsz:指定了消息内容的长度
msgtyp:指定了消息的类型
msgflg:
IPC_NOWAIT:没有消息的时候,立即返回错误   errno被设置
0    没有消息的时候,阻塞等待

返回值:
-1  失败   errno被设置
成功  返回实际拷贝到mtext中的字节数。


需要用户自定义这个类型
struct msgbuf {
        long mtype;       /* message type, must be > 0 */
        char mtext[1];    /* message data */
};
typedef struct msgbuf msgb_t;
msgb_t *st=malloc(sizeof(msgb_t)+strlen(mtext)-3);
st->mtext

举例说明    两个进程通过消息队列实现进程间的通讯。
代码参见   send.c   recv.c

总结:
一、pause(2)函数的使用
二、信号从产生到处理的整个过程
三、可重入函数
四、作业  前台作业和后台作业
五、使用setitimer(2)实现定时器
六、进程间通讯    system v ipc
消息队列

 

posted @ 2017-10-25 21:55  Kernel001  阅读(259)  评论(0编辑  收藏  举报