实验名称:实验0:linux命令和利用API编程

实验目的

  • 1、熟悉Linux命令
  • 2、熟悉系统API并编程

实验内容

  1. LINUX下联机命令练习
  2. 操作系统功能相关的API函数应用
    • 进程和线程实验
    • 进程间通信实验
    • 文件操纵实验

实验环境

  1. VMware
  2. Linux

实验作业

一、linux命令

1. 登录到系统

用root帐号(超级用户)注册,没有口令。注册成功出现#号或$号(系统提示符)

![image.png](https://img-blog.csdnimg.cn/img_convert/572c5e071230e871398c3c32b27a1aad.png#clientId=ua2873e44-2097-4&from=paste&height=148&id=ud83defcc&margin=[object Object]&name=image.png&originHeight=197&originWidth=413&originalType=binary&ratio=1&size=82652&status=done&style=none&taskId=uea273c86-9b9f-434e-a90c-006ce68d0fc&width=310)
2、注销(退出)系统: logout 或exit
3.练习使用命令ls(注意Linux命令区分大小写。)

使用ls 查看当前目录内容;
使用ls 查看指定目录内容,如/目录,/etc目录
使用ls –all 查看当前目录内容;使用dir 查看当前目录内容
#ls   –l            以长列表形式显示当前目录内容
文件类型    链接数     注册名  组名  文件大小  创建时间  文件名
(d:目录文件,--普通文件)

![image.png](https://img-blog.csdnimg.cn/img_convert/a368556fc959e0eb061c762dffa16500.png#clientId=ua2873e44-2097-4&from=paste&height=261&id=u7e5573c9&margin=[object Object]&name=image.png&originHeight=522&originWidth=934&originalType=binary&ratio=1&size=465352&status=done&style=none&taskId=u8f25f801-f5ae-4ac4-a2bd-e1d6b0bc325&width=467)
4.使用cd改变当前目录

cd ..   回到上层目录  ;
cd /   回到根目录

![image.png](https://img-blog.csdnimg.cn/img_convert/bf25a92660d0c520ce6a5d27e4fb7618.png#clientId=ua2873e44-2097-4&from=paste&height=79&id=u634304ee&margin=[object Object]&name=image.png&originHeight=158&originWidth=387&originalType=binary&ratio=1&size=46908&status=done&style=none&taskId=uef8fc08c-4df1-419b-adad-d8c58ca2eb5&width=193.5)
5.pwd 显示当前路径
![image.png](https://img-blog.csdnimg.cn/img_convert/8ca2543ad9179ff0fd9c9a65d1ae177b.png#clientId=ua2873e44-2097-4&from=paste&height=35&id=BzJEW&margin=[object Object]&name=image.png&originHeight=70&originWidth=323&originalType=binary&ratio=1&size=17556&status=done&style=none&taskId=u19333d48-9d1e-4232-9475-12a27a856ac&width=161.5)
6.建立目录mkdir

mkdir  目录名 ;
mkdir  /home/s2001/newdir

![image.png](https://img-blog.csdnimg.cn/img_convert/395ebc04ed580ad2d99b9a708f270073.png#clientId=ua2873e44-2097-4&from=paste&height=65&id=ub96595f1&margin=[object Object]&name=image.png&originHeight=130&originWidth=691&originalType=binary&ratio=1&size=69822&status=done&style=none&taskId=u7d38bfac-6ff0-42a7-ba2c-ec969ac0a3d&width=345.5)
7.删除目录:rmdir;
![image.png](https://img-blog.csdnimg.cn/img_convert/a4c889bfcc0f6c9a361789aa34713802.png#clientId=ua2873e44-2097-4&from=paste&height=66&id=uddbf3b8c&margin=[object Object]&name=image.png&originHeight=132&originWidth=574&originalType=binary&ratio=1&size=57000&status=done&style=none&taskId=ue506a245-6b45-4611-a147-efbf0d4fc37&width=287)
8.复制文件cp:

cp  文件名1  文件夹名2

![image.png](https://img-blog.csdnimg.cn/img_convert/985d9bbd1c74e44fe6a4bfca2535db04.png#clientId=ua2873e44-2097-4&from=paste&height=160&id=uae0ab212&margin=[object Object]&name=image.png&originHeight=319&originWidth=807&originalType=binary&ratio=1&size=215488&status=done&style=none&taskId=udf2ce03e-1896-455e-9687-eb172276e23&width=403.5)
9.移动文件或目录: mv
10.删除文件 rm
![image.png](https://img-blog.csdnimg.cn/img_convert/019ca8256b1977bded61ab19acfe5a41.png#clientId=ua2873e44-2097-4&from=paste&height=66&id=uf141d4ae&margin=[object Object]&name=image.png&originHeight=131&originWidth=527&originalType=binary&ratio=1&size=61251&status=done&style=none&taskId=uc7970ed7-9e89-42d1-bf25-f7b023ca86c&width=263.5)
11、链接文件 ln file1 /usr/wang/file1
12. 显示文件内容:more (分页显示);

显示文件:cat 文件名
建立文件:cat >文件名,ctrl+d结束输入

![image.png](https://img-blog.csdnimg.cn/img_convert/9aca261b46b91decfd8164a00dcdb144.png#clientId=ua2873e44-2097-4&from=paste&height=306&id=uab524b4b&margin=[object Object]&name=image.png&originHeight=408&originWidth=476&originalType=binary&ratio=1&size=170376&status=done&style=none&taskId=u092ee9b4-3c95-48e7-ad67-dabebac6ef2&width=357)
![image.png](https://img-blog.csdnimg.cn/img_convert/be40bea6b46bb62813e3bc7dcc6034e5.png#clientId=ua2873e44-2097-4&from=paste&id=ufff262dd&margin=[object Object]&name=image.png&originHeight=97&originWidth=274&originalType=binary&ratio=1&size=20362&status=done&style=none&taskId=ubdd90ab0-6931-47ca-a276-ce8f5131feb)
![image.png](https://img-blog.csdnimg.cn/img_convert/ba3b4c3d8f7cef88db8c4012208a3a3c.png#clientId=ua2873e44-2097-4&from=paste&height=188&id=uc91a5301&margin=[object Object]&name=image.png&originHeight=251&originWidth=504&originalType=binary&ratio=1&size=22509&status=done&style=none&taskId=u7e29113a-d990-430c-823a-a9d78d7d716&width=378)
13、使用联机帮助

man 命令名

![image.png](https://img-blog.csdnimg.cn/img_convert/e0b7744ea16b7dbf8a28fecdfbe38fa1.png#clientId=ua2873e44-2097-4&from=paste&height=278&id=u2cc44c56&margin=[object Object]&name=image.png&originHeight=556&originWidth=1148&originalType=binary&ratio=1&size=334854&status=done&style=none&taskId=u320e13be-238d-4c5f-a2d6-20582ef3b73&width=574)

二、进程相关API函数

POSIX中的进程基本操作:

     - fork()函数----创建进程- execl()函数---加载程序- wait()函数---等待进程- exit()函数---终止进程

fork()函数使用

函数通过系统调用创建一个与原来进程几乎完全相同的进程,这个新产生的进程称为子进程。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。需要注意的一点:就是调用fork函数之后,一定是两个进程同时执行的代码段是fork函数之后的代码,而之前的代码以及由父进程执行完毕。
在调用fork()函数创建子进程后,父子进程的执行顺序由操作系统来决定,相互之间没有任何时序上的关系,所以在我们没有加入进程同步机制的代码的情况下试图靠着调整语句的先后顺序来控制父子进程的先后循序是不可能的,所以在运行得到的结果中,我们看到每次运行的结果都有可能与上次的运行结果不同。 而关于多次运行出现的顺序不同则是因为:在调用fork()函数之后,父进程与子进程之间的执行顺序是不确定的,这个取决于内核的调度算法。
fork()返回值意义如下:
=0:在子进程中
>0:在父进程中
<0:创建失败
测试代码:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{pid_t pid1, pid2;        //进程标识符pid1 = fork();     //创建一个新的进程if (pid1 < 0){printf("创建进程失败!");exit(1);}else if (pid1 == 0)   //如果pid为0则表示当前执行的是子进程{printf("子进程1,进程标识符是%d\n", getpid());}else          //否则为父进程{pid2 = fork();//創建一個新的進if (pid2 < 0){printf("创建进程失败!");exit(1);}else if (pid2 == 0)   //如果pid为0则表示当前执行的是子进程{printf("子进程2,进程标识符是%d\n", getpid());}else          //否则为父进程{printf("父进程,进程标识符是%d\n", getpid());}}return 0;
}
测试图:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25457727/1640096885149-24991c5d-a0c1-42ec-aed5-f4f05309d019.png#clientId=ua2873e44-2097-4&from=paste&height=208&id=u8ec6d66a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=416&originWidth=567&originalType=binary&ratio=1&size=98271&status=done&style=none&taskId=u81cb9cc3-ff82-47e9-a882-e17552fe637&width=283.5)<br />

execl()—加载函数

用exec函数可以把当前进程替换为一个新进程,且新进程与原进程有相同的PID。exec名下是由多个关联函数组成的一个完整系列,
int execl(const char *path, const char *arg, ...);
path参数表示你要启动程序的名称包括路径名
arg参数表示启动程序所带的参数,一般第一个参数为要执行命令名,不是带路径且arg必须以NULL结束
返回值:成功返回0,失败返回-1
测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{printf("entering main process---\n");execl("/bin/ls", "ls", "-l", NULL);printf("exiting main process ----\n");return 0;
}
运行图:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25457727/1640097036068-5917050f-2ff9-4581-bb63-e02cfa6afa90.png#clientId=ua2873e44-2097-4&from=paste&height=391&id=u3c9a497c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=521&originWidth=702&originalType=binary&ratio=1&size=301729&status=done&style=none&taskId=u957a00b1-a387-48c6-8ef1-7f71c5a44de&width=527)<br />解释:<br />由于利用execl将当前进程main替换掉,所以最后那条打印语句不会输出 。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25457727/1640097074420-035d23d8-0ed7-49a7-940f-e337a09272ae.png#clientId=ua2873e44-2097-4&from=paste&height=219&id=u4c39d012&margin=%5Bobject%20Object%5D&name=image.png&originHeight=292&originWidth=605&originalType=binary&ratio=1&size=73536&status=done&style=none&taskId=u872f73ba-65e9-431f-b4f2-54126e39e56&width=454)<br />

wait()函数—等待进程

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid函数获取这些信息,然后彻底清除掉这个进程
函数功能:父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:
1. 阻塞等待子进程退出;
2. 回收子进程残留资源;
3. 获取子进程结束状态(退出原因)。
wait一旦被调用,就会一直阻塞在这里,直到有一个子进程退出出现为止 。
测试代码:
在主程序中创建子查询并等待子进程结束后继续进行,代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
int main()
{pid_t pid;pid = fork();if (pid < 0){printf("%sfork error\n", __FUNCTION__);return 1;}else if (pid == 0){//childprintf("child is run,pid is: %d\n", getpid());sleep(5);exit(1);}else{int status = 0;pid_t ret = 0;do{ret = waitpid(-1, &status, WNOHANG);printf("father do other thing\n");if (ret == 0)printf("child is runing\n");sleep(1);} while (ret == 0);if (WIFEXITED(status) && ret == pid)printf("wait child 5s success,child return cod is:%d\n", WEXITSTATUS(status));else {printf("wait child failed,return\n");return 1;}}return 0;
}

运行图:
![image.png](https://img-blog.csdnimg.cn/img_convert/6645bb6e3f77924a6916a3d942414132.png#clientId=ua2873e44-2097-4&from=paste&height=274&id=u3396d2fa&margin=[object Object]&name=image.png&originHeight=366&originWidth=439&originalType=binary&ratio=1&size=117523&status=done&style=none&taskId=u3cf625af-cd40-49a8-a7b1-efa4070a506&width=329)

exit()函数—终止进程

进程的退出:linux下进程数量太多会导致系统崩溃,在使用完一个进程之后要及时终止它。
进程退出一般有三种方法:
1、在main函数中使用 return关键字 ,使用 return 后系统会调用 exit()函数来终止进程。
2、手动调用 exit() 来终止进程。
3、调用 _exit() 来终止进程。
_exit()
os立刻把管理内存的结构体、虚拟内存释放掉。
exit()
不会马上干掉结构体,先看先看当前进程有没有文件缓存区,若有则会先处理缓存区中的数据,然后销毁结构体。

测试代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{pid_t res;res = fork();if (res == -1){printf("fork err\r\n");}if (res == 0){//不加回车换行符,让输出的字符串数据保留在io缓存区里面printf("son");//退出状态设置为0,表示正常退出。_exit(0);}else if (res > 0){printf("parent");exit(0);}
}

运行图:
![image.png](https://img-blog.csdnimg.cn/img_convert/47af06cc649a26589d38250b11a9b6e4.png#clientId=ua2873e44-2097-4&from=paste&height=63&id=uc5862f0e&margin=[object Object]&name=image.png&originHeight=84&originWidth=378&originalType=binary&ratio=1&size=25293&status=done&style=none&taskId=uf1ee4de1-2933-44e8-91a5-939f7089442&width=284)

三、进程通信相关API函数

信号signal()与kill()

信号 ( signal ) 机制是 UNIX 系统中最为古老的进程间通信机制,很多条件可以产生一个信号.
信号的产生:
1,当用户按下某些按键时,产生信号.
2,硬件异常产生信号:除数为 0 ,无效的存储访问等等.这些情况通常由硬件检测到,将其通知内核, 然后内核产生适当的信号通知进程,例如,内核对正访问一个无效存储区的进程产生一个 SIGSEGV 信号.
3,进程用 kill 函数 将信号发送给另一个进程.
4,用户可用 kill 命令将信号发送给其他进程.
信号处理:
当某信号出现时,将按照下列三种方式中的一种进行处理.
1,忽略此信号:大多数信号都按照这种方式进行处理,但有两种信号却决不能被忽略.它们是:SIGKILL 和 SIGSTOP . 这两种信号不能被忽略的原因是:它们向超级用户提供了一种终止或停止进程的方法.
2,执行用户希望的动作:通知内核在某种信号发生时,调用一个用户函数,在用户函数中,执行用户希望的处理.
3,执行系统默认动作: 对大多数信号的系统默认动作是终止该进程.
当系统捕捉到某个信号时,可以忽略该信号或是使用指定的处理函数来处理该信号,或者使用系统默认的方式.
信号处理的主要方法有两种,一种是使用简单的 signal 函数,另一个是使用信号集函数.
信号发送 kill 和 raise :
信号发送的主要函数有 kill 和 raise .
区别:kill 既可以向自身发送信号,也可以向其他进程发送信号,与 kill 函数不同的是,raise 函数是向自身发送信号。

#include < sys/types.h >
#include < signal.h >
int kill ( pid_t  pai, int signo )
int  raise ( int  signo )
kill 的 pid 参数有四种不同情况:
1, pid  > 0
将信号发送给进程 ID 为 pid 的进程.
2,pid = 0
将信号发送给同组的进程.
3,pid < 0
将信号发送给其进程组 ID 等于 pid 绝对值的进程.
4,pid = -1
将信号发送给所有进程.

测试代码:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void my_func(int sign_no)
{if (sign_no == SIGINT)printf("I have get SIGINT\n");else if (sign_no == SIGQUIT)printf("I have get SIGQUIT\n");
}
int main()
{printf("Waiting for signal SIGINT or SIGQUIT \n ");/*注册信号处理函数*/signal(SIGINT, my_func);signal(SIGQUIT, my_func);int ret = getpid();printf("pid is :%d\n", ret);//kill(ret,SIGINT);pause();exit(0);
}
运行图:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/25457727/1640097820670-5d44765f-5630-453c-8d2b-50c4d4386e0d.png#clientId=ua2873e44-2097-4&from=paste&height=79&id=u15b5f588&margin=%5Bobject%20Object%5D&name=image.png&originHeight=105&originWidth=396&originalType=binary&ratio=1&size=18734&status=done&style=none&taskId=u1fa5fec8-4905-4a0d-8017-b0f3e411fe1&width=297)![image.png](https://cdn.nlark.com/yuque/0/2021/png/25457727/1640097823983-e388ef57-7a5d-4523-9210-afa49b39d4c6.png#clientId=ua2873e44-2097-4&from=paste&height=55&id=u01ab3fd0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=74&originWidth=335&originalType=binary&ratio=1&size=14856&status=done&style=none&taskId=uc13a042d-6a00-4aac-96a0-fb05ab8da2d&width=251)<br />

shmget、shmat、shmdt、shmctl

共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。
Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件sys/sem.h中。

shmget()函数

![image.png](https://img-blog.csdnimg.cn/img_convert/ec2ea375a0df8cdd1859410e8c45c041.png#clientId=uf1e03f0f-e2f4-4&from=paste&id=u042cc1aa&margin=[object Object]&name=image.png&originHeight=847&originWidth=871&originalType=binary&ratio=1&size=90663&status=done&style=none&taskId=ua10176b8-30a1-47a8-a5cc-a724e04f3de)

shmat()函数

![image.png](https://img-blog.csdnimg.cn/img_convert/fd43236c736a7bde85bf9981cdb5f11d.png#clientId=uf1e03f0f-e2f4-4&from=paste&id=uf771a377&margin=[object Object]&name=image.png&originHeight=617&originWidth=874&originalType=binary&ratio=1&size=59755&status=done&style=none&taskId=u2d08d20e-cf77-4292-842f-6a5bc5513bb)

shmdt()函数

![image.png](https://img-blog.csdnimg.cn/img_convert/bccbca1f735efc1eff4fc91e2edba57a.png#clientId=uf1e03f0f-e2f4-4&from=paste&id=u32318b73&margin=[object Object]&name=image.png&originHeight=471&originWidth=860&originalType=binary&ratio=1&size=38266&status=done&style=none&taskId=ubccc8468-8b1d-4803-a9d1-ca6d6f24607)

shmctl()函数

![image.png](https://img-blog.csdnimg.cn/img_convert/389c58fc320fac96d32b507f66352976.png#clientId=uf1e03f0f-e2f4-4&from=paste&id=ucb637e49&margin=[object Object]&name=image.png&originHeight=648&originWidth=862&originalType=binary&ratio=1&size=56545&status=done&style=none&taskId=u6b12bb1d-a73b-4aa9-b78c-b9dac8b38e3)
测试代码:
建立共享内存,并存入信号。

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
typedef struct {char name[8];int age;
} people;
int main(int argc, char** argv)
{int shm_id, i;key_t key;char temp[8];people *p_map;char pathname[30];strcpy(pathname, "/tmp");key = ftok(pathname, 0x03);if (key == -1){perror("ftok error");return -1;}printf("key=%d\n", key);shm_id = shmget(key, 4096, IPC_CREAT | IPC_EXCL | 0600);if (shm_id == -1){perror("shmget error");return -1;}printf("shm_id=%d\n", shm_id);p_map = (people*)shmat(shm_id, NULL, 0);memset(temp, 0x00, sizeof(temp));strcpy(temp, "test");temp[4] = '0';for (i = 0; i < 3; i++){temp[4] += 1;strncpy((p_map + i)->name, temp, 5);(p_map + i)->age = 0 + i;}shmdt(p_map);return 0;
}
在共享内存中读取信号
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct {char name[8];int age;
} people;
int main(int argc, char** argv)
{int shm_id, i;key_t key;people *p_map;char pathname[30];strcpy(pathname, "/tmp");key = ftok(pathname, 0x03);if (key == -1){perror("ftok error");return -1;}printf("key=%d\n", key);shm_id = shmget(key, 0, 0);if (shm_id == -1){perror("shmget error");return -1;}printf("shm_id=%d\n", shm_id);p_map = (people*)shmat(shm_id, NULL, 0);for (i = 0; i < 3; i++){printf("name:%s\n", (*(p_map + i)).name);printf("age %d\n", (*(p_map + i)).age);}if (shmdt(p_map) == -1){perror("detach error");return -1;}return 0;
}

运行图:
![image.png](https://img-blog.csdnimg.cn/img_convert/38dd105546f498a90623d19aa98f7817.png#clientId=ua2873e44-2097-4&from=paste&height=72&id=u76372c7f&margin=[object Object]&name=image.png&originHeight=96&originWidth=343&originalType=binary&ratio=1&size=10864&status=done&style=none&taskId=ua38c1454-e7ce-42f9-a19b-3109d2751d8&width=257)
![image.png](https://img-blog.csdnimg.cn/img_convert/89608465c46ca71f4326ad6bc321b2e4.png#clientId=ua2873e44-2097-4&from=paste&height=251&id=uf05a24ae&margin=[object Object]&name=image.png&originHeight=334&originWidth=502&originalType=binary&ratio=1&size=45169&status=done&style=none&taskId=u71ffa29f-9332-4e4c-9412-f4f9fffe21e&width=377)

Msgget()、Msgsend()、Msgrcv()

msgget()函数

int msgget(key_t, key, int msgflg);
该函数用来创建和访问一个消息队列。它的原型为:
与其他的IPC机制一样,程序必须提供一个键来命名某个特定的消息队列。msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。
它返回一个以key命名的消息队列的标识符(非零整数),失败时返回-1.

msgsnd()函数

该函数用来把消息添加到消息队列中。它的原型为:int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
msgid是由msgget函数返回的消息队列标识符。
msg_ptr是一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定消息的类型。所以消息结构要定义成这样:

struct my_message {long int message_type;/* The data you wish to transfer */
};

msg_szmsg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。
msgflg用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。
如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1.

msgrcv()函数

该函数用来从一个消息队列获取消息,它的原型为int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
msgid,msg_ptr, msg_st 的作用也函数msgsnd()函数的一样。
msgtype 可以实现一种简单的接收优先级。如果msgtype为0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。如果它小于零,就获取类型等于或小于msgtype的绝对值的第一个消息。
msgflg 用于控制当队列中没有相应类型的消息可以接收时将发生的事情。
调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1。

msgctl()函数

该函数用来控制消息队列,它与共享内存的shmctl函数相似,它的原型为:int msgctl(int msgid, int command, struct msgid_ds *buf);
command是将要采取的动作,它可以取3个值,
IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值
IPC_RMID:删除消息队列
buf是指向msgid_ds结构的指针,它指向消息队列模式和访问权限的结构。msgid_ds结构至少包括以下成员:

struct msgid_ds
{uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};

成功时返回0,失败时返回-1.

测试代码:
建立消息对列,写入消息:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>
#include <errno.h>
#define MAX_TEXT 512
struct msg_st
{long int msg_type;char text[MAX_TEXT];
};
int main(int argc, char **argv)
{struct msg_st data;char buffer[BUFSIZ];int msgid = -1;// 建立消息队列msgid = msgget((key_t)1234, 0666 | IPC_CREAT);if (msgid == -1){fprintf(stderr, "msgget failed error: %d\n", errno);exit(EXIT_FAILURE);}// 向消息队里中写消息,直到写入endwhile (1){printf("Enter some text: \n");fgets(buffer, BUFSIZ, stdin);data.msg_type = 1; // 注意2strcpy(data.text, buffer);// 向队列里发送数据if (msgsnd(msgid, (void *)&data, MAX_TEXT, 0) == -1){fprintf(stderr, "msgsnd failed\n");exit(EXIT_FAILURE);}// 输入end结束输入if (strncmp(buffer, "end", 3) == 0){break;}sleep(1);}exit(EXIT_SUCCESS);
}

读取消息对列中的消息:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
struct msg_st
{long int msg_type;char text[BUFSIZ];
};
int main(int argc, char **argv)
{int msgid = -1;struct msg_st data;long int msgtype = 0;   // 注意1// 建立消息队列msgid = msgget((key_t)1234, 0666 | IPC_CREAT);if (msgid == -1){fprintf(stderr, "msgget failed width error: %d\n", errno);exit(EXIT_FAILURE);}// 从队列中获取消息,直到遇到end消息为止while (1){if (msgrcv(msgid, (void *)&data, BUFSIZ, msgtype, 0) == -1){fprintf(stderr, "msgrcv failed width erro: %d", errno);}printf("You wrote: %s\n", data.text);// 遇到end结束if (strncmp(data.text, "end", 3) == 0){break;}}// 删除消息队列if (msgctl(msgid, IPC_RMID, 0) == -1){fprintf(stderr, "msgctl(IPC_RMID) failed\n");}exit(EXIT_SUCCESS);
}

运行图:
![image.png](https://img-blog.csdnimg.cn/img_convert/3d3f9e74f11cbf6b084cfdce919a9446.png#clientId=uf1e03f0f-e2f4-4&from=paste&height=143&id=u5cc4fd44&margin=[object Object]&name=image.png&originHeight=191&originWidth=430&originalType=binary&ratio=1&size=24926&status=done&style=none&taskId=u7d62debb-9ecb-4d89-a656-66daeb03e09&width=323)
![image.png](https://img-blog.csdnimg.cn/img_convert/e89b35a884e8f62c01d8e3c4543b25a7.png#clientId=uf1e03f0f-e2f4-4&from=paste&height=140&id=ue01661ee&margin=[object Object]&name=image.png&originHeight=187&originWidth=359&originalType=binary&ratio=1&size=21087&status=done&style=none&taskId=u22c49e41-a3e7-4c37-9190-a10121a2ea9&width=269)

管道Pipe()

管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
1. 其本质是一个伪文件(实为内核缓冲区)
2. 由两个文件描述符引用,一个表示读端,一个表示写端。
3. 规定数据从管道的写端流入管道,从读端流出。
管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限性:
① 数据自己读不能自己写。
② 数据一旦被读走,便不在管道中存在,不可反复读取。
③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
④ 只能在有公共祖先的进程间使用管道。
创建管道
int pipe(int pipefd[2]);
成功:0;失败:-1,设置errno
函数调用成功返回r/w两个文件描述符。无需open,但需手动close。规定:fd[0] → r; fd[1] → w,就像0对应标准输入,1对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?通常可以采用如下步骤:
1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。

测试代码:父子进程使用管道通信,父写入字符串,子进程读出并,打印到屏幕。

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>void sys_err(const char *str)
{perror(str);exit(1);
}int main(void)
{pid_t pid;char buf[1024];int fd[2];char *p = "test for pipe\n";if (pipe(fd) == -1)sys_err("pipe");pid = fork();if (pid < 0) {sys_err("fork err");}else if (pid == 0) {close(fd[1]);int len = read(fd[0], buf, sizeof(buf));write(STDOUT_FILENO, buf, len);close(fd[0]);}else {close(fd[0]);write(fd[1], p, strlen(p));wait(NULL);close(fd[1]);}return 0;
}

运行图:
![image.png](https://img-blog.csdnimg.cn/img_convert/679b2cd422e72a93a0a422cf03bbc725.png#clientId=uf1e03f0f-e2f4-4&from=paste&id=u84dd2bac&margin=[object Object]&name=image.png&originHeight=90&originWidth=377&originalType=binary&ratio=1&size=14314&status=done&style=none&taskId=u4eadd3dd-ee27-46bb-93ea-3a658da0d7d)

四、POSIX中的进程基本操作:

概念:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
文件描述符主要用于read、write、close、lseek

open()函数----打开一个文件

int open(const char *pathname,int flag,/*int mode*/);
pathname:指定打开的文件的路径+文件名
flag:操作模式:以O_RDONLY(只读),O_WRONLY(只写),O_RDWR(读写),O_CREAT(创建)文件不存在就创建
mode:指定新创建文件的权限,只有在O_CREAT时,才会需要
功能描述:用于打开或创建文件,在打开或创建文件时可以指定文件的属性及用户的权限等各种参数。
返回值:-1,出错

read()函数—读文件

int read (int fd,void*buff,int size);
fd:将要读取数据的文件描述符
buff:指定获取的数据在程序中存储的起始位置
size:本次最多读取的字节个数,一般为缓冲区的大小
返回值:-1,出错 成功:返回读到的字节个数
功能描述: 从文件读取数据。

write()函数—写文件

int write ( int fd,void *buff ,int datalen);
fd:写数据的文件描述符
buff:指定需要写入数据的起始位置
datalen:写入数据的长度
返回值:-1,出错 成功:返回写入的字节个数
功能描述:向文件写入数据

close()函数—关闭一个文件

int close(int fd);
fd:文件描述符
返回值:0成功,-1出错
功能描述:用于关闭一个文件

lseek()函数—标记

int lseek(int fd,int pos,int flag);
pos:位置
flag:标记
SEEK_SET 开始,当前位置为文件的开头,新位置为偏移量的大小
SEEK_CUR 当前,当前位置为指针的位置,新位置为当前位置加上偏移量
SEEK_END 尾/结束,当前位置为文件的结尾,新位置为文件大小加上偏移量的大小
返回值:失败返回-1,成功返回当前位移
测试代码:

#include <error.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<stdio.h>
#include<unistd.h>
int main(int argc, char ** argv)
{int fd = open("createst.txt", O_RDWR | O_CREAT, 0666);if (fd == -1)printf("open error\n");char buffer[] = "my used to test\n";char buffer2[5] = { 0 };int n = 0;n = write(fd, buffer, sizeof(buffer));if (n == 0)printf("write o\n");n = close(fd);if (n == 0)printf("close o\n");fd = open("createst.txt", O_RDWR | O_CREAT, 0666);n = read(fd, buffer2, sizeof(buffer));if (n == -1)printf("read error\n");//write(fd,buffer2,n);printf("%s\n", buffer2);n = close(fd);if (n == 0)printf("close o\n");return 0;
}

![image.png](https://img-blog.csdnimg.cn/img_convert/f9e1f191887faa75553b2026fce84726.png#clientId=uf1e03f0f-e2f4-4&from=paste&height=170&id=u88511151&margin=[object Object]&name=image.png&originHeight=227&originWidth=404&originalType=binary&ratio=1&size=24805&status=done&style=none&taskId=uc16760f0-37f9-4308-a7fa-a6715b5778b&width=303)
![image.png](https://img-blog.csdnimg.cn/img_convert/bb6455da23bf87ca173eac52cc19a66e.png#clientId=uf1e03f0f-e2f4-4&from=paste&height=125&id=u42fd339e&margin=[object Object]&name=image.png&originHeight=167&originWidth=537&originalType=binary&ratio=1&size=10910&status=done&style=none&taskId=u1d19952c-f4c3-4395-968b-826b2d302d8&width=403)
![image.png](https://img-blog.csdnimg.cn/img_convert/1d8675589aea612d81dddac8e86922a0.png#clientId=uf1e03f0f-e2f4-4&from=paste&height=210&id=u55b2e07d&margin=[object Object]&name=image.png&originHeight=280&originWidth=659&originalType=binary&ratio=1&size=20155&status=done&style=none&taskId=u77d71c2a-762a-4c0d-b104-0ffb8f4ce85&width=494)

实验体会

通过本次试验我熟练掌握了使用info命令来获取相应命令的联机说明,学会使用目录和文件操作命令,同时还会用文件权限管理命令,熟练运行通配符、输入输出重定向和管道命令,受益匪浅。

操作系统leb0实验报告相关推荐

  1. 《操作系统》实验报告——熟悉Linux基础命令及进程管理

    理论知识 Linux--进程管理 Linux--Linux C语言编程基础知识 手把手教你安装Linux虚拟机 一.实验目的 (1)加深对进程概念的理解,明确进程和程序的区别. (2)进一步认识并发执 ...

  2. 进程同步算法实现实验报告Linux,操作系统进程同步实验报告.doc

    操作系统进程同步实验报告 实验三:进程同步实验 一.实验任务: (1)掌握操作系统的进程同步原理: (2)熟悉linux的进程同步原语: (3)设计程序,实现经典进程同步问题. 二.实验原理: (1) ...

  3. 东北大学软件学院操作系统v实验报告

    课程编号:B080000070     <操作系统>实验报告             姓名   学号   班级   指导教师   实验名称 <操作系统>实验 开设学期 2016 ...

  4. 课程linux实验报告,Linux操作系统课程实验报告.doc

    Linux操作系统课程实验报告.doc Linux操作系统课程实验报告班级姓名学号指导老师田丽华完成时间2014年7月目录一.实验目的1二.实验要求1三.实验内容1[第一题]1[第二题]2[第三题]4 ...

  5. linux课程实验报告,Linux操作系统课程实验报告

    Linux操作系统课程实验报告 Linux操作系统 课程实验报告 班级: 姓名: 学号: 指导老师:田丽华 完成时间:2014年7月 目录 一.实验目的1 二.实验要求1 三.实验内容1 [第一题]1 ...

  6. 操作系统 作业调度实验报告

    题目要求 一. 实验目的 用高级语言编写和调试一个或多个作业调度的模拟程序,以加深对作业调度算法的理解. 二. 例题 为单道批处理系统设计一个作业调度程序. 由于在单道批处理系统中,作业一投入运行,它 ...

  7. 《操作系统》实验报告——主存空间的分配与回收

    理论知识 Linux--Linux C语言编程基础知识 一.实验目的 采用可变式分区管理,使用最佳适应算法实现主存的分配与回收. 通过本次实验,帮助学生理解在可变式分区管理方式下,如何实现主存空间的分 ...

  8. 计算机系统结构实验报告Linux,计算机操作系统体系结构实验报告.doc

    操作系统实验报告 实验目的: 随着操作系统应用领域的扩大,以及操作系统硬件平台的多样化,操作系统的体系结构和开发方式都在不断更新,目前通用机上常见操作系统的体系结构有如下几种:模块组合结构.层次结构. ...

  9. 操作系统进程同步实验报告

    华侨大学计算机科学与技术学院 操作系统实验报告 进程同步 课程名称:操作系统实验 实验项目名称:进程同步 学    院:计算机科学与技术学院 专业班级: 姓    名: 学    号: 目录 1.描述 ...

最新文章

  1. SP5971 LCMSUM - LCM Sum(莫比乌斯反演 ,推柿子,经典)
  2. 是我太天真之被BUG按在地上疯狂摩擦
  3. explain mysql 调优_explain mysql性能优化
  4. C++ Primer 5th笔记(chap 16 模板和泛型编程)模板类型别名
  5. OpenCV:在imshow() 之前使用namedWindow() 的必要性讨论?
  6. Jupyter Notebook——设置Jupyter Notebook默认目录
  7. 【Python】main函数 if __name__=='__main__' 详解
  8. Neo4j HA环境配置
  9. azure db 设置时区_使用Azure Cosmos DB开始您的旅程
  10. ANSYS APDL学习(6):ANSYS APDL部分命令流介绍
  11. u 只读 盘 突然_u盘变成只读方式了,怎么办
  12. 摄像头远程监控精灵 4.65
  13. 扫描仪服务器正在运行中,针对使用 Acrobat 时出现的扫描仪问题的故障排除提示...
  14. 原来这个世界还有这么纯洁的爱情!
  15. 北斗时钟服务器(GPS卫星同步时钟)应用电子政务系统
  16. 装了oracle 开机卡黑屏,开机黑屏进不了系统,手把手教你解决电脑开机黑屏进不了系统...
  17. 无线龙物联网STM32+ZigBee实验箱智能家居多种传感器开发平台
  18. H3C 路由器智能选路NQA策略
  19. echarts风向图
  20. java jms 框架_Apache RocketMQ之JMS基本概念及使用

热门文章

  1. 【计算机系统1 】1 LC-3仿真器安装和使用
  2. Java自带(Timer)
  3. android编程权威指南 的PhotoGallery项目Flickr 不能访问的替代解决方法
  4. 完全卸载node.js
  5. java怎么调epass3003_兴业银行U盾下载码是什么
  6. [博创智联]创新创客智能硬件平台——温湿度传感器
  7. 云计算和大数据区别和联系
  8. 搜索引擎营销推广技巧
  9. 天梯赛 L1-025 正整数A+B (15 分) 小坑不断
  10. 织梦模板中php代码,织梦模板内怎么加入php代码