实验、进(线)程同步与通信 一、实验目的 1、掌握Linux系统用户界面中键盘命令的使用。 2、学会一种Linux下的编程环境。
二、预备知识 1、Linux下的信号灯及其P、V操作。 在Linux中信号灯是一个数据集合(信号量数组),可以单独使用这一集合的每个元素。 有关的系统调用命令: 1)semget:返回一个被内核指定的整型的信号灯索引。 2)semop:执行对信号灯集的操作。 3)semctl:执行对信号灯集的控制操作。
信号灯的定义: 数据结构的原型是semid_ds,在linux/sem.h中定义: struct semid_ds{ struct ipc_perm sem_perm; /*permissions..seeipc.h*/ time_t sem_otime; /*last semop time*/ time_t sem_ctime; /*last change time*/ struct sem *sem_base;/*ptr to first semaphore in array*/ struct wait_queue *eventn; struct wait_queue *eventz; struct sem_undo *undo; /*undo requests on this array*/ ushort sem_nsems; /*no. of semaphores in array*/ };
信号量的创建: 使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集. 原型:int semget(key_t key, int nsems, int semflg); 返回值:如果成功,则返回信号量集的IPC标识符。如果失败,则返回-1. key:通过相同的key值调用semget semflg:标记集合,类似open函数的标记。 一般用:IPC_CREAT | 0666 nsems:信号灯的个数
信号量的操作:. 系统调用semop(); 调用原型:int semop(int semid,struct sembuf 信号量的操作: 系统调用semop(); 调用原型:int semop(int semid,struct sembuf *sops,unsigned nsops); 返回值:0—成功。-1—失败 semid:semget函数返回的信号量标识符 sops:指向结构数组的指针: struct sembuf{ ushort sem_num;/*将要处理的信号量的下标*/ short sem_op; /*要执行的操作*/ short sem_flg; /*操作标志,IPC_NOWAIT */ } nsops:操作次数,一般为1
P操作 void P(int semid,int index) { struct sembuf sem; sem.sem_num = index; sem.sem_op = -1; sem.sem_flg = 0; //操作标记:0或IPC_NOWAIT等 semop(semid,&sem,1); //1:表示执行命令的个数 return; }
V操作 void V(int semid,int index) { struct sembuf sem; sem.sem_num = index; sem.sem_op = 1; sem.sem_flg = 0; semop(semid,&sem,1); return; }
信号量的赋值:系统调用semctl() 原型:int semctl(int semid,int semnum,int cmd,union semun arg); 返回值:如果成功,则为一个正数。 ·IPC_RMID将信号量集从内存中删除(不用时删除)。 ·SETALL设置信号量集中的所有的信号量的值。 ·SETVAL设置信号量集中的一个单独的信号量的值(第四个参数指定)。 arg.val=1; semctl(semid,1,SETVAL,arg)
2、线程 1)线程创建 pthread_create(pthread_t. thread, pthread_attr_t 2、线程 1)线程创建 pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),void *arg); 2)pthread_join(pthread_t th, void **thread_retrun); 作用:挂起当前线程直到由参数th指定的线程被终止为止。
3、共享内存 使用共享内存是运行在同一计算机上的进程进行进程间通信的最快的方法。 int shmget(key_t key,int size,int shmflg) shmflg:IPC_CREAT|0666 int shmat(int shmid, char *shmaddr, int shmflg) shmid:共享内存句柄,shmget调用的返回值 shmaddr:一般用NULL shmfg:SHM_R|SHM_W S = (char *)shmat(shmid1,NULL,SHM_R|SHM_W) 一旦绑定,对共享内存的操作即变为对局部变量S的操作
共享存储区的控制shmctl:对其状态信息进行读取和修改。 int shmctl(int shmid, int cmd, struct shmid_ds * buf); 其中,buf是用户缓冲区地址,cmd是操作命令。 (1)用于查询有关共享存储区的情况。 (2)用于设置或改变共享存储区的属性。 (3)对共享存储区的加锁和解锁命令。 (4)删除共享存储区标识符等。 如shmctl(shmid,IPC_RMID,0)
4、进程控制 fork与execv系统调用 pid_t p1 if ((p1=fork()) == 0) { //sub puts("get created\n"); execv("./get",NULL); } else { //main ……… }
5、编译、编辑、调试 cc –o test -g test.c –lpthread cc –o sub1 sub1.c vi gdb
Linux文件编辑 vi Linux古老的、功能强大的全屏幕编辑器 启动方式: --$vi 文件名 打开已有的文件或编辑新文件 --$vi 先编辑,以后命名存盘
Linux文件编辑(续) Vi的三种状态 命令态 Command mode:接受编辑命令 插入态 Insert mode: 做文字输入,按Esc键可到 Command mode 。 最后一行态Last line mode:将文件写入或离开编辑器,亦可设定编辑环境, 如寻找字串、列出行号..等。
Linux文件编辑(续) 命令态 Command mode i : 插入, 从目前光标所在之处插入所输入之文字。 #x : 例, 3x 表删除 3 个文字。 dd : 删除光标所在之行。
Linux文件编辑续 Last line mode 在命令态按 ‘:’ 或 ‘/’ 即可进入最后一行态Last line mode。Vi底部出现:或/。 --列出行号 :set nu 取消行号 :set nonumber 多行拷贝 :3,10 co 20 存盘 :w 存盘退出:wq 强制存盘退出:wq! --寻找字串 /word (由首至尾寻找)
Linux进程查看 ps命令 该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等 语法格式如下: ps [选项] 常用选项说明: -e 显示所有进程。 -f 全格式。
三、实验内容 1、线程的同步与互斥 2、进程的同步与互斥
实验一的程序要求: 两个线程,共享公共变量a 线程1负责计算(+1) 线程2负责打印 程序结构
线程同步: #include 头文件pthread.h、sys/types.h、linux/sem.h等 P、V操作的函数定义: void P(int semid,int index) void V(int semid,int index) 信号灯、线程句柄定义: int semid; pthread_t p1,p2; 线程执行函数定义:void *subp1();void *subp2();
void. subp1() { for (. ) { P(……. ); 打印; V(… void *subp1() { for (......) { P(…….); 打印; V(…..); } return; subp2负责计算,如何定义?
主函数:main() { 创建信号灯; 信号灯赋初值; 创建两个线程subp1、subp2; 等待两个线程运行结束; 删除信号灯; }
进程同步 问题:在Linux下编程实现誊抄问题,通过多个进程合作将源文件复制到目标文件中 程序的组成:main、get、copy、put四个可执行程序。 main:初始化信号灯、缓冲区,创建三个子进程,等待子进程运行结束后做善后处理; get :读源文件,送入缓冲区S; copy:从S复制信息到T; put :将T中信息写入目标文件
main程序的结构: if ((p1=fork()) == 0) { //sub execv("./get",NULL); }else { //main if ((p2=fork())==0) { //sub execv("./copy",NULL); }else { //main if ((p3=fork()==0)) { execv("./put",NULL); }}} 等待get/copy/put结束; 删除信号灯/共享内存等; } main程序的结构: <linux/shm.h>等; main () { 创建4个信号灯并赋初值; 创建共享缓冲区S、T并赋初值;
get程序的结构: #include 头文件 变量定义; main() { 4个信号灯申请,不赋初值; 缓冲区S申请,初值? 打开源文件; while (…….) { P(….); 读入S; V(….); 判断结束标记; ……… } }
#include ... copy程序的结构: main() { 4个信号灯申请,不赋初值; 缓冲区S申请,初值? 缓冲区T申请、初值; while (1) { P(…);P(…); T=S; V(….);V(…..); 判断是否结束; }
put程序的结构 #include ..... main() { 信号灯申请; 缓冲区T的申请; 打开目标文件; while(… ) { P(….); 写目标文件; V(….); 判断是否结束; } }