ZUCC OS实验复习指南(进程、线程、进程通信、消息队列、信号、信号量、共享内存、管道)
ZUCC OS复习指南
(进程、线程、进程通信、消息队列、信号、信号量、共享内存、管道)
os实验是上机pta,极少是操作系统原理的理论,基本上是实验手册
复习策略是记住
1.头文件
2.函数
3.进程、线程、进程间的通信(信号、共享内存、信号量、消息队列、管道)
1. 进程的创建和管理(fork)
头文件<sys/types.h>、<sys/wait.h>
#include<stdio.h>
#include<sys/types.h>//fork在这
#include<sys/wait.h>//wait在这
int main()
{
pid_t fork(void);
/*创建一个新进程,返回两次,返回子进程的ID给父进程,
返回0给子进程,错误返回负值,常考因printf的缓冲机制,
不加换行符会导致输出异常*/
pid_t getpid(void);
/*获取当前进程ID*/
pid_t getppid(void);
/*获取当前进程的父进程的ID*/
pid_t wait(int *status);
/*用于父进程阻塞,等待子进程的结束例如wait(0)*/
void exit(int *status);
/*用于本函数的结束常用于子进程,返回status,父进程中wait接收*/
}
2. List item
头文件:<pthread.h>
#include<stdio.h>
#include<pthread.h>
int main()
{
pthread_create(t1,NULL,p_msg,(void *)""hello"");
/*用于创建一个进程,第一个属性t1是线程ID,在别的函
数使用该进程只要输入线程ID,第二个NULL指的是线程的
属性,默认且大多为NULL,第三个就是函数的起始地址,
就是说,这线程创建是干什么的,执行什么函数,第四
个hello就是该函数所需要的参数*/
pthread_join(t1,NULL);
/*用阻塞的方式等待t1进
程的结束,第二个属性是
t1结束的返回值只要记住
常是NULL*/
pthread_mutex_t mutex;//互斥量
pthread_mutex_init(&mutex,NULL);
/*互斥量的初始化,必要*/
pthread_mutex_lock(&mutex);
/*相当于P操作,使信号量--,锁
住该进程,别的进程不能阻塞*/
pthread_mutex_unlock(&mutex);
/*相当于V操作,使信号量++,释放
锁,别的进程可以执行*/
/*PV操作下面再举例讲解*/
pthread_exit(0);
/*释放该进程,0或NULL为返回值,类似于进程的exit函数*/
}
3 通信
进程之间的通信方式有信号、共享内存、信号量、消息队列、管道
这里先说明管道,因为管道是比较简单的。
- 管道
头文件<unistd.h> 有两种管道,有名FIFO,和无名管道简称管道。
单向通信(半双工),是指在一个时间内只能读或写,
父子进程都可读都可写 考试只考无名管道
管道只能在父子进程中执行
先建立管道再fork子进程
#include<unistd.h>
int main()
{
int pipe(fd[2]);
/*建立管道*/
/*fd数组装的是文件
描述词,意思是你是
写还是读,0读1写,
建立成功函数返回0失
败返回-1,下面给函数
返回值基本相似,如相同不
再赘述*/
close(fd[1]);
/*关闭读或写端口*/
popen();
/*是库函数的建立管道,其内部也调用了pipe*/
pclose();
/*与popen对应的关闭管道*/
}
- 消息队列
头文件<sys/types>、<sys/ipc.h>、<sys/msg.h>.
有用户区和内核区,发送就是把用户区的数据放到内核区,接收则相反 且消息队列是个链表
允许一个或多个进程写入或读取消息 双向通信(解决了读写进程的同步和阻塞问题) 管道的不同之处是,必须关闭当前读写的端口
消息内容不仅可以是字符也可以是其他类型 不同于共享内存,内核完成向消息队列中发送消息后,
会唤醒等待消息的进程,同样的接受消息后,会删除消息 同样的唤醒发送进程
msqid_ds 是一个数据结构,里面登记了消息的最后 发送进程的PID,和最后接收进程的PID msgid和msqid不一样q代表队列
#include<sys/types.h>
#include<sys/msg.h>
#include<sys/ipc.h>
int main()
{
int msgid;
/*消息队列的ID*/
msgid=msgget((key_t)1234,0666|IPC_CREAT);
/*创建消息队列,1234是一个键值key,
返回值为消息队列的ID(标识),收发可通过此ID进行*/
msgrcv(masgid,(void *)&some_data,BUFSIZ,msg_to_receive,0);
/*接收消息,masgid就是上面的消息队列ID,some_data是一个指针
指向的是消息内容存放的地址,BUFSIZ就是这个消息内容的大小,是一个数字
指定接收什么消息,最后的0就是能够唤醒发送进程*/
msgsnd(msgid,(void *)&some_data,MAX_TEXT,0);
/*用于发送消息,可以说和接收消息非常的类似,
只需要记住MAX_TEXT是正文的字节数,最后唤醒接受进程*/
}
- 信号
头文件<sinal.h> 信号是软中断,和中断一样是异步,也就是会,有信号时, 会停下工作去处理信号
signal_不属于Linux的文件
信号没有有优先级,中断有优先级,也就是说,所有的信号都是平等的
四个事件体现三个功能 事件:1.信号注册 2.信号发送 3.信号处理 4.信号注销 功能:1.发送信号 2.预设定信号的处理3.根据相应信号产生相应的处理事件
发送信号涉及的函数: alarm(),kill(),sigqueue(),raise()
头文件中定义了64种信号,其中kill信号-9,15终止信号比较重要
#include<signal.h>
int main()
{
signal(SIGUSR1,p_action);
/*为SIGUSR1信号绑定处理函数p_aciton*/
kill(pid,sig);
/*根据pid的值决定发给谁,可以是多个人,sig是信号*/
sigaction();
/*和signal类似但更复杂*/
sigqueue(pid,sig,sigval);
/*和sigaction相对的发送信号的函数,
针对实时信号,比kill传更多内容,但是只能给一个进程*/
pause();/*暂停进程等信号*/
raise(sig);/*向自身进程发送一个信号*/
/*以下不重要*/
alarm(time);/*定时发送sigalarm(signal.h文件中有包含)*/
gettitimer();/*获取time这个定时器的状态*/
setitimer();/*设置定时器*/
}
- 共享内存
头文件<sys/ipc.h><sys/shm.h><sys/types.h>
划定存储区允许两个或多个进程共享
不需要拷贝(那需要拷贝的通信方式有消息队列和管道,其他方式不传送数据),效率更高
两个进程可以用一个相同的key共享一块内存区,但不同的进程调用shmget的size可以不一样。
操作流程一般是
shmget建立共享内存
fork子进程
shmat子进程连接共享内存
shmdt写完后断开连接
shmctl父进程获得共享内存状态信息
shmat父进程连接共享内存
shmdt读完后断开连接
shmctl父进程销毁共享内存
#include<sys/shm.h>
#include<sys/types.h>
#include<sys/ipc.h>
int main()
{
int shmid;/*共享内存的ID*/
shmid=shmget(key,SIZE,IPC_CREAT|0600);
/*建立共享内存
key为键值,SIZE为共享内存大小,IPC_CREAT
返回共享内存的ID*/
shmaddr=char(*)shmat(shmid,NULL,0);
/*NULL处如果有值就是指定共享内存出现再进程内存地址的什么地方,
如果没有就内核自己决定,0为读写模式*/
/*返回地址*/
shmdt(shmaddr);/*断开共享内存*/
shmctl(shmid,IPC_STAT,&buf);
/*共享内存管理,IPC_STAT意思是得到共享内存状态,复制到buf
还有IPC_SET改变共享内存状态,
IPC_RMID删除这片共享内存(上文的销毁)*/
}
- 信号量
其实就是理论中PV操作在进程中的实现,同步和互斥。
理解了就非常的简单。
先分别说明POSIX信号量和SYSTEM V
POSIX信号量
分为有名和无名。
有名,保存在文件,用于线程和所有进程同步。
无名,保存在内存,用于线程或相关进程同步。
考无名。无名信号量必须是多个进程的共享变量。
#include<stdio.h>
#include<pthread.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
/*保护变量的意思是,用pv操作对其进行控制*/
sem_t sem_id;/*信号量变量*/
sem_init(&sem_id,0,1);
/*sem_id为信号变量,指明类型,
0为在相关进程共享,其他为当前进程的所有进程共享
1为信号量的初始值*/
sem_wait(&sem_id);
/*p操作,信号量--*/
sem_post(&sem_id);
/*v操作,信号量++*/
}
重要的是理解PV操作
SYSTEM V
头文件<sys/types.h><sys/ipc.h><sys/sem.h>
是信号量的集合,用于进程间同步
SYSTEM V其实只有三个函数,但是每个函数都有一个集合体
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/types.h>
int main()
{
sem_id=semget((key_t)1234,1,1066|IPC_CREAT);
/*创建信号量*/
/*1为信号量个数*/
semop(sem_id,sops,1);
/*pv操作
sem_id为信号集的位置,
sops为信号量操作集合的位置,
1为进行操作的信号量的个数*/
semctl(sem_id,,semnum,cmd,arg);
/*用于信号量集合的控制,类似信号
sem_id信号量的集合位置,
semnum信号量集合的下标指向哪个信号量,
cmd是控制信息
可能是cmd所有的参数结构*/
}
system V应该把注意还是放在PV操作上