欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ZUCC OS实验复习指南(进程、线程、进程通信、消息队列、信号、信号量、共享内存、管道)

程序员文章站 2024-02-11 14:02:16
...

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 通信
进程之间的通信方式有信号、共享内存、信号量、消息队列、管道
这里先说明管道,因为管道是比较简单的。

  1. 管道

头文件<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对应的关闭管道*/ 
	
}
  1. 消息队列

头文件<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是正文的字节数,最后唤醒接受进程*/
}
  1. 信号

头文件<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()/*设置定时器*/ 
	
	
} 
  1. 共享内存

头文件<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删除这片共享内存(上文的销毁)*/ 
} 
  1. 信号量

其实就是理论中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操作上