Linux实验报告

更新时间:2024-04-04 06:04:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

课程编号:B080103040

Linux操作系统 实验报告

姓 名 班 级 实验名称 邵春晓 软工1201 学 号 指 导 教 师 Linux操作系统实验 20124670 石 凯 开 设 学 期 实 验 时 间 2014-2015第一学期 第11周——第18周 评定人签字 评 定 日 期 石 凯 评 定 成 绩 东北大学软件学院

实验一 熟悉Linux环境

【实验内容】

一、练习常用的Shell命令。

当用户登录到字符界面系统或使用终端模拟窗口时,就是在和称为shell的命令解释程序进行通信。当用户在键盘上输入一条命令时,shell程序将对命令进行解释并完成相应的动作。这种动作可能是执行用户的应用程序,或者是调用一个编辑器、GNU/Linux实用程序或其他标准程序,或者是一条错误信息,告诉用户输入了错误的命令。 二、熟悉vim编辑器

在编写文本或计算机程序时,需要创建文件、插入新行、重新排列行、修改内容等,计算机文本编辑器就是用来完成这些工作的。

Vim编辑器的两种操作模式是命令模式和输入模式(如图2所示)。当vim处于命令模式时,可以输入vim命令。例如,可以删除文本并从vim中退出。在输入模式下,vim将把用户所输入的任何内容都当作文本信息,并将它们显示在屏幕上。 三、熟悉gcc编译器

GNU/Linux中通常使用的C编译器是GNU gcc。编译器把源程序编译生成目标代码的任务分为以下4步:

a. 预处理,把预处理命令扫描处理完毕 ;

b. 编译,把预处理后的结果编译成汇编或者目标模块;

c. 汇编,把编译出来的结果汇编成具体CPU上的目标代码模块; d. 连接,把多个目标代码模块连接生成一个大的目标模块; 四、熟悉gdb调试工具

LINUX包含了一个叫gdb的GNU调试程序。gdb是一个用来调试C和C++程序的强有力调试器。它使你能在程序运行时观察程序的内部结构和内存的使用情况。它具有以下一些功能: ·监视程序中变量的值;

·设置断点以使程序在指定的代码行上停止执行; ·一行行的执行代码。

五、掌握Linux下C程序编辑运行过程 Linux下编写C程序要经过以下几个步骤: ⑴启动常用的编辑器,键入C源程序代码。 ⑵编译源程序 ⑶运行可执行文件

【实验总结】

在学习linux的过程中间,最主要的就是耐心和细心。linux的命令很多,只有一遍一遍的看一次又一次的用,才能记住很多的命令,才能很好的使用linux。通过这次实验,我对linux的系统以及vim编辑器,gcc编译器,gdb调试工具更加熟悉,这也让我让我能够更加努力学习。

实验二 文件操作

【实验内容】

1.文件的创建(必做题)

编写程序,实现cp命令的功能。被复制的文件名与复制出的新文件由用户指定。调用方法:“你编写的程序名 被复制文件名 复制出的文件名”。要求程序有一定的健壮性,即对用户错误调用及其他错误要有处理和反馈。(提示:可以使用man手册查看具体的系统调用,e.g., man 2 open)。 #include #include #include #include #include

main(int ac, char *av[]){ int fd1, fd2; int n;

char buf[512];

if(ac!=3){

printf(\ exit(1); }

if((fd1=open(av[1], O_RDONLY))==-1){ perror(\ exit(1); }

if((fd2=creat(av[2], 0777))==-1){ perror(\ exit(1); }

while((n=read(fd1, buf, 512))>0){ write(fd2, buf, n); }

close(fd1); close(fd2); }

2.查看目录内容及文件属性 (1)编写程序,实现ls -l的功能。调用方法:“你编写的程序名”——这时打印出当前目录文件及其属性;“你编写的程序名 目录名”——这时在终端显示给定目录下的文件及其属性。(提示:当用户给定目录后,需要改变进程的当前目录,可使用chdir()。) #include #include #include #include #include #include #include #include #include #include

void mode_to_str(mode_t mode, char *str){ strcpy(str, \

if(S_ISDIR(mode)) str[0]='d';

if(mode & 0400) str[1]='r'; if(mode & 0200) str[2]='w'; if(mode & 0100) str[3]='x';

if(mode & 040) str[4]='r'; if(mode & 020) str[5]='w'; if(mode & 010) str[6]='x';

if(mode & 04) str[7]='r'; if(mode & 02) str[8]='w'; if(mode & 01) str[9]='x'; }

void show_file_info(char * filename){ struct stat buf; char modestr[10]; stat(filename, &buf);

mode_to_str(buf.st_mode, modestr);

printf(\ printf(\

printf(\ printf(\ printf(\

printf(\ printf(\}

main(int ac, char *av[]){ DIR * dirp;

struct dirent * p;

if(1==ac){

dirp=opendir(\ }else{

dirp=opendir(av[1]); }

while((p=readdir(dirp))!=NULL){ if(p->d_name[0]!='.'){

show_file_info(p->d_name); } }

closedir(dirp); }

3. 设备文件操作

在/dev目录下,找到你的鼠标对应的文件。打开这个文件,从该文件循环读出字符,并将字符对应的ascII代码在终端显示出来。

⑷ 该程序段中每个进程退出时都用了语句exit(0),为什么?

答:每个进程退出时都用了语句exit(0),一方面要结束进程,另外向父进程返回结束标志0。 2.修改上面的程序,增加语句signal(SIGINT,SIG_IGN)和语句signal(SIGQUIT,SIG_IGN),再观察程序执行时屏幕上出现的现象,并分析其原因。 # include # include # include int pid1, pid2; int EndFlag=0; pf1=0; pf2=0;

void IntDelete() {

kill(pid1,10); kill(pid2,12); EndFlag=1; }

void Int1() {

printf(“child process 1 is killed by parent !\\n”); exit(0); }

void Int2() {

printf(“child process 2 is killed by parent !\\n”); exit(0); }

main() {

int exitcode;

signal(SIGINT,SIG_IGN); signal(SIGQUIT,SIG_IGN); while((pid1=fork())==-1); if(pid1==0)

{

signal(SIGUSR1,Int1); signal(SIGINT,SIG_IGN); pause(); exit(0);

} else {

while((pid2=fork())= =-1);

if(pid2==0) {

signal(SIGUSR2,Int2); signal(SIGINT,SIG_IGN); pause(); exit(0);

} else {

signal(SIGINT,IntDelete);

waitpid(-1,&exitcode,0); /*等待任何子进程中断或结束*/ printf(“parent process is killed \\n”); exit(0);

}

} }

运行程序并分析结果。 答:

结果分析:由于忽略了中断与退出信号,程序会一直保持阻塞状态而无法退出。

⑶司机售票员问题(选做题)

编程用fork()创建一个子进程代表售票员,司机在父进程中,再用系统调用signal()让父进程(司机)捕捉来自子进程(售票员)发出的中断信号,让子进程(售票员)捕捉来自(司机)发出的中断信号,以实现进程间的同步运行。

创建子进程代表售票员,父进程代表司机,同步过程如下:

售货员捕捉SIGINT(代表开车),发SIGUSR1给司机,司机打印:let us go go go 售票员捕捉SIFQUIT(代表开车),发SIGUSR2给司机,司机:top the bus

司机捕捉SIGTSTP(代表车到总站),发SIGUSR1给售票员,售票员打印:please get off the bus

#include #include #include #include #include int pid1,pid2; void Init1() {

puts(\ }

void Init2() {

puts(\ }

void Init3() {

puts(\ }

void Init4() {

kill(pid1,10); }

void Init5() {

int k=getppid(); kill(k,10); }

void Init6() {

int j=getppid(); kill(j,12); }

int main(void) {

signal(SIGINT,SIG_IGN); signal(SIGQUIT,SIG_IGN); signal(SIGTSTP,SIG_IGN); pid1=fork();

if(pid1>0)//父进程 {

signal(10,Init1); signal(12,Init2);

signal(SIGTSTP,Init4); while(1); exit(0); }

else if(pid1==0) {

signal(SIGINT,Init5); signal(SIGQUIT,Init6); signal(10,Init3); while(1); exit(0); } else {

puts(\

}

return EXIT_SUCCESS; }

【实验总结】

通过这次实验,我了解了信号的概念,知道了:每个进程在运行时,都要通过信号机制来检查是否有信号到达。若有,便中断正在执行的程序,转向与该信号相对应的处理程序,以完成对该事件的处理;处理结束后再返回到原来的断点继续执行。

(三)进程的管道通信实验

【实验内容】

1.编制一段程序,实现进程的管道通信。使用pipe()建立一条管道线。两个子进程p1和p2分别向管道各写一句话:

Child 1 is sending message! Child 2 is sending message!

而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。 <参考程序>

# include # include # include int pid1,pid2; main() {

int fd[2];

char OutPipe[100],InPipe[100]; pipe(fd);

while((pid1=fork())= = -1); if(pid1= =0)

{

lockf(fd[1],1,0); sprintf(OutPipe,“child 1 process is sending message!”);

write(fd[1],OutPipe,50); sleep(5);

lockf(fd[1],0,0); exit(0);

} else {

while((pid2=fork())= = -1); if(pid2= =0)

{

lockf(fd[1],1,0); sprintf(OutPipe,“child 2 process is sending message!”);

write(fd[1],OutPipe,50); sleep(5);

lockf(fd[1],0,0);

exit(0); } else

{

wait(0);

read(fd[0],InPipe,50);

printf(“%s\\n”,InPipe);

wait(0);

read(fd[0],InPipe,50); printf(“%s\\n”,InPipe); exit(0); } } }

实验结果为:

延迟5秒后显示:

child1 process is sending message!

再延迟5秒:

child2 process is sending message!

2.在父进程中用pipe()建立一条管道线,往管道里写一句话,两个子进程接收这句话。 答:实验程序如下:

#include #include #include #include int main() {

int n, fd[2]; pid_t pid, pid1;

char buffer[BUFSIZ+1]; if(pipe(fd) < 0) {

printf(\ exit(1); }

if((pid = fork()) < 0) {

printf(\ exit(1); }

else if(pid > 0) {

if((pid1 = fork()) < 0) {

printf(\ exit(1); }

else if(pid1 >0) {

close(fd[0]);

write(fd[1], \ }

else {

close(fd[1]);

n = read(fd[0], buffer, 6);

write(STDOUT_FILENO,\ write(STDOUT_FILENO, buffer, n); } } else {

close(fd[1]);

n = read(fd[0], buffer, 6);

write(STDOUT_FILENO,\ write(STDOUT_FILENO, buffer, n); }

exit(0); }

【实验总结】

通过这次实验,了解什么是管道,知道了管道是UNIX系统的一大特色。所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。

(四)消息的发送与接收实验

【实验内容】

1.消息的创建、发送和接收。使用系统调用msgget( ),msgsnd( ),msgrev( ),及msgctl( )编制一长度为1k的消息发送和接收的程序。

<参考程序> ①client.c

#include #include #include #define MSGKEY 75 struct msgform { long mtype;

char mtext[1000]; }msg;

int msgqid;

void client() {

int i;

msgqid=msgget(MSGKEY,0777); /*打开75#消息队列*/ for(i=10;i>=1;i--) {

msg.mtype=i;

printf(“(client)sent\\n”);

msgsnd(msgqid,&msg,1024,0); /*发送消息*/ }

exit(0); }

main( ) {

client( ); }

②server.c

#include #include #include #define MSGKEY 75 struct msgform { long mtype;

char mtext[1000]; }msg;

int msgqid;

void server( ) {

msgqid=msgget(MSGKEY,0777|IPC_CREAT); /*创建75#消息队列*/ do {

msgrcv(msgqid,&msg,1030,0,0); /*接收消息*/

printf(“(server)received\\n”);

}while(msg.mtype!=1);

msgctl(msgqid,IPC_RMID,0); /*删除消息队列,归还资源*/ exit(0); }

main( ) {

server( ); }

程序说明:

①为了便于操作和观察结果,编制二个程序client.c和server.c,分别用于消息的发送与接收。 ②server建立一个 Key 为75的消息队列,等待其它进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出server。server每接收到一个消息后显示一句“(server)received。”

③client使用 key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后一个消息,即是 server端需要的结束信号。client 每发送一条消息后显示一句 “(client)sent”。 ④注意: 二个程序分别编辑、编译为client与server。 执行: 实验截图:

2. 在父进程中创建一个消息队列,用fork创建一个子进程,在子进程中将一条消息传送至消息队列,父进程接受队列的消息,并将消息送屏幕显示。

编写程序如下:

#include #include #include #include #include #include #include #include #define MAX_TEXT 512

struct msg_st //消息队列的结构体 {

int my_msg_type;

char msg_text[BUFSIZ]; };

int main(int argc,char **argv) {

pid_t pid; int i = 1; int status;

if( (pid = fork()) == -1) {

perror(\ exit(EXIT_FAILURE); }

else if ( pid == 0) //子进程 {

struct msg_st some_data; int msgid;

char buffer[BUFSIZ];

if((msgid = msgget((key_t)12345,0666|IPC_CREAT)) == -1 ) {

perror(\ exit(EXIT_FAILURE); }

printf(\ fgets(buffer,BUFSIZ,stdin); some_data.my_msg_type = 1;

strcpy(some_data.msg_text,buffer);

if((msgsnd(msgid,(void *) &some_data,MAX_TEXT,0)) == -1) {

perror(\ exit(EXIT_FAILURE); } return 0; }

else //父进程 {

int msgid1;

struct msg_st some_data1; int msg_to_recevie = 0;

if((msgid1= msgget((key_t)12345,0666|IPC_CREAT)) == -1) {

perror(\ exit(EXIT_FAILURE); }

if(msgrcv(msgid1,(void *) &

some_data1,BUFSIZ,msg_to_recevie , 0) == -1) {

perror(\ exit(EXIT_FAILURE); }

printf(\ if(msgctl(msgid1,IPC_RMID,0) == -1) {

fprintf(stderr,\

exit(EXIT_FAILURE); }

return 0; } }

【实验总结】

通过这次实验,了解了消息的概念,即消息(message)是一个格式化的可变长的信息单元。消息机制允许由一个进程给其它任意的进程发送一个消息。当一个进程收到多个消息时,可将它们排成一个消息队列;熟悉了消息传送机理;学习了消息的发送与接收。

实验四 编写Web服务器

【实验内容】

1.服务器设计重点

基于socket的客户/服务器系统大多是类似的。一旦理解了一个socket流的客户/服务器系统,就可以理解大多数其他的系统。 2.三个主要操作

客户和服务器都是进程。服务器设立服务,然后进入循环接收和处理请求。客户连接到服务器,然后发送、接受或者交换数据,最后退出。该交互过程中主要包含了一下三个操作: (1)服务器设立服务 (2)客户连接到服务器 (3)服务器和客户处理事务

客户: 服务器:

建立服务 连接到服务器 接收请求 获取服务 提供服务 挂断连接 挂断连接

3.连接过程

操作1:建立服务器端socket

设立一个服务一般需要如下3个步骤: (1)创建一个 socket

socket = socket ( PF_INET, SOCK_STREAM, 0 ) (2)给 socket 绑定一个地址 bind ( sock, &addr, sizeof(addr) ) (3)监听接入请求 listen ( sock, queue_size )

步骤1:

创建一个服务器端socket

为了避免在编写服务器时重复输入上述代码,将这3个步骤组合成一个函数:make_server_socket。在编写服务器的时候,只需调用该函数就可以创建一个服务端socket。具体如下:

sock=make_server_socket(int portnum)

return -1 if error, or a server socket listening at port ―portnum‖

操作2:建立到服务器的连接

基于流的网络客户连接到服务器包含以下两个步骤: (1)创建一个 socket

socket = socket ( PF_INET, SOCK_STREAM, 0 ) (2)使用该 socket 连接到服务器

connect ( sock, &serv_addr, sizeof ( serv_addr ) )

步骤2:

创建并连接客户socket到服务器

将这两个步骤抽象成一个函数:connet_to_server。当编写客户端程序时,只要调用该函数就可以建立到服务器的连接。具体如下: fd=connet_to_server(hostname, portnum) return -1 if error,

or a fd open for reading and writing connected to the socket at port ―portnum‖ on host ―hostname‖ /*

* socklib.c *

* This file contains functions used lots when writing internet * client/server programs. The two main functions here are: *

* make_server_socket( portnum ) returns a server socket * or -1 if error

* make_server_socket_q(portnum,backlog) *

* connect_to_server(char *hostname, int portnum) * returns a connected socket * or -1 if error */

socklib.c

#include #include #include #include #include #include #include #include

#define HOSTLEN 256 #define BACKLOG 1

int make_server_socket_q(int , int );

int make_server_socket(int portnum) { return make_server_socket_q(portnum, BACKLOG); }

int make_server_socket_q(int portnum, int backlog) { struct sockaddr_in saddr; /* build our address here */ struct hostent *hp; /* this is part of our */ char hostname[HOSTLEN]; /* address */ int sock_id; /* the socket */ sock_id = socket(PF_INET, SOCK_STREAM, 0); /* get a socket */ if ( sock_id == -1 ) return -1; /** build address and bind it to socket **/ bzero((void *)&saddr, sizeof(saddr)); /* clear out struct */ gethostname(hostname, HOSTLEN); /* where am I ? */ hp = gethostbyname(hostname); /* get info about host */ /* fill in host part */ bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length); saddr.sin_port = htons(portnum); /* fill in socket port */ saddr.sin_family = AF_INET ; /* fill in addr family */ if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 ) return -1; /** arrange for incoming calls **/ if ( listen(sock_id, backlog) != 0 ) return -1; return sock_id; }

int connect_to_server(char *host, int portnum) { int sock; struct sockaddr_in servadd; /* the number to call */ struct hostent *hp; /* used to get number */

}

/** Step 1: Get a socket **/

sock = socket( AF_INET, SOCK_STREAM, 0 ); /* get a line */ if ( sock == -1 ) return -1; /** Step 2: connect to server **/

bzero( &servadd, sizeof(servadd) ); /* zero the address */ hp = gethostbyname( host ); /* lookup host's ip # */ if (hp == NULL) return -1;

bcopy(hp->h_addr, (struct sockaddr *)&servadd.sin_addr, hp->h_length); servadd.sin_port = htons(portnum); /* fill in port number */ servadd.sin_family = AF_INET ; /* fill in socket type */ if ( connect(sock,(struct sockaddr *)&servadd, sizeof(servadd)) !=0) return -1; return sock;

操作3:客户/服务器的会话

至此,可以使用专门的函数来建立服务器端,也有专门的函数来连接到服务器。 (1)一般的客户端

网络客户通常调用服务器来获得服务,一个典型的客户程序如下: mian(){ int fd; fd=connect_to_server(host, port); /*call the server*/ if(fd==-1) exit(1); /*or die*/ talk_with_server(fd); /*chat with server*/ close(fd); /*hang up when done*/ }

函数talk_with_server 处理与服务器的对话。具体的内容取决于特定应用。例如,e-mail客户和邮件服务器交谈的是邮件,而天气预报客户和服务器交谈的则是天气。 (2)一般的服务器端 一个典型的服务器如下: main(){ int sock, fd; /*socket and connection*/ if(sock==-1) exit(1); while(1){ fd=accept(sock, NULL, NULL); /*take next call*/

else if ( strcmp(extension, \ content = \ else if ( strcmp(extension, \ content = \ else if ( strcmp(extension, \ content = \ fpsock = fdopen(fd, \ fpfile = fopen( f , \ if ( fpsock != NULL && fpfile != NULL ) { header( fpsock, content ); fprintf(fpsock, \ while( (c = getc(fpfile) ) != EOF ) putc(c, fpsock); fclose(fpfile); fclose(fpsock); } exit(0); }

运行Web服务器:

编译程序,并在某个端口(建议用80)运行它: $cc webserv.c socklib.c –o webserv $./webserv 80 编译成功:

现在可以访问Web服务器,网址为http://yourhostname/。将html文件放到该目录中并用http://yourhostname/filename.html来打开它。创建下面的shell脚本: #!/bin/sh

printf ―Content-type: text/plain\\n\\nhello\\n‖;

将它命名为hello.cgi,用chmod改变权限为755,然后用浏览器调用该程序:http://yourhostname/hello.cgi。

用chmod改变权限为755:

四、实验总结

通过做该实验,我学到了很多课本上没有的知识,增强了自己的动手能力和编程能力。时在课本上看了很多的程序,但是我很少编写过,对书上的程序仅仅有一些浅显的理解。经过做实验,我对程序有了更深一层的理解,更加形象生动的感知到了每个符号的生命和活力。不但了解了并发服务器的运行模式和套接字的作用和使用方法,而且加深了对课本知识的理解。并且掌握了socket编程,以及调试技巧。由于Web服务器要使用多进程(或多线程),让我对进程有了更深入的理解。

本文来源:https://www.bwwdw.com/article/4bvr.html

Top