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 消息队列