第一周:Linux环境搭建——虚拟机文件共享
① 右键新建终端
② 键入ifconfig
③ 键入ifconfig eth0 up
④ 键入service network restart
⑤ 键入ifconfig,出现图中红框所示部分即配置成功。inet addr为主机号。
⑥ 打开xshell,点击新建连接
⑦ 输入自拟名称及主机号
⑧ 注册用户名及密码(若用root用户登录,密码须与Linux虚 拟机root用户密码相同)
⑨ 点击xftp图标,重复新建步骤
⑩ 出现如图所示界面,即共享成功,双击文件即可传输。
第二周:Linux使用测试
(1)进入用户主目录
cd或者 cd ~ 或者cd #
(2)创建文件hello.c
touchhello.c
(3)在文件hello.c中添加字符串“hello world”
echohello world > hello.c
(4)创建目录test
mkdirtest
(5)进入test
cdtest
(6)将/root/hello.c拷贝在当前目录下,命名为hello1.c
cp /root/hello.c hello1.c
(7)查找hello1.c中是否存在字符串“hello”
grep hello hello1.c
(8)返回用户主目录
cd
(9)为hello.c创建硬链接hello_back.c和符号链接hello.lnk
ln hello.c hello_back.c
ln -s hello.c hello.lnk
(10)将目录test打包为test.tar.gz
tar zcvf test test.tar.gz
(11)删除目录test并解压test.tar.gz。
rm -rf ./test
tar zxvf test.tar.gz
第三周:Shell测试
请编写shell脚本,完成下列题目。
(1)编写menu.sh,请用case实现下列菜单功能。
[N]ewa file _新建文件
[E]dita file _编辑文件
[D]eletea file _删除文件
[Q]uit_退出
答案
#!/bin/bash
echo "Please choose either N,E,D orQ:"
cat<<-ENDIT #依次打印
#[N]ew a file
#[E]dit a file
#[D]elete a file
#[Q]uit
ENDIT
read choice #读取输入字符
case "$choice" in #case语句
N) echo "please input a new filename:"
read value
touch${value} #新建指定名字的文件
;;
E) echo "pleas input a filename:"
read value
vi ${value} #进入指定名字的文件
;;
D) echo "Please input a filename:"
read value
rm ${value} #删除指定名字的文件
;;
Q) TERM=quit
echo "QuitSuccessfully" #退出选项
;;
esac #退出case语句
(2)编写computer.sh,请用至少三种方式计算2*3的结果。
答案
#!/bin/bash
#方法一expr
echo "use the way of expr"
r=`expr 2 \* 3` #“\”为转义字符
echo $r
#方法二将内层括号内运算结果作为外层括号的输出值
echo "use the way of /$ (())"
r=$((2*3))
echo $r
#方法三 $[]直接输出计算值
echo "use the way of /$ []"
r=$[2*3]
echo $r
#方法四let
echo "use the way of let"
let r=2*3
echo $r
(3)编写printNum.sh,请用至少三种方式打印数字1-10。
答案
#!/bin/bash
#方法一 while语句
echo "loop of while"
let "n = 0"
while [ $n -lt 10 ]
do
let "n = n + 1"
echo $n
done
#方法二 until语句
echo "loop of until"
let "n = 0"
until [ $n -eq 10 ]
do
let "n += 1"
echo $n
done
#方法三 for语句
echo "loop of for"
for i in `seq 1 10`
do
echo $i
Done
(4)编写functionEx.sh,用函数调用实现对输入数字平方的计算。 例如:运行./functionEx.sh
5,则输出结果25。
答案
#!/bin/bash
# Scriptname: do_square
function square
{
local sq
let"sq=$1 * $1"
return
}
value_returned=$(square $1)
echo "$value_returned"
(5)请依次输出命令行中所有位置参数。
答案
#!/bin/bash
#方法一 until语句
until [ $# -eq 0 ]
do
echo $1
shift
done
#方法二 while语句
num=$#
i=0
while [$i - lt $num]
do
Echo “$1”
i=$[i+1]
shift
done
exit 0
第四周:U盘挂载
挂载U盘(设定字符集,保证能够正常显示中文)
①新建终端后依次输入一下命令行
②fdisk-l #查看系统硬盘和分区情况
③插入U盘
④fdisk -l
#重新查看文件后,大家可以发现多了一个硬盘/dev/sdb和它的一个分区/dev/sdb1
⑤mkdir -p/mnt/usb #在mnt目录下创建目录usb来作挂接点
⑥mount -o iocharset=cp936 /dev/sdb1 /mnt/usb #进行挂载
#其中,-o iocharset是设定字符集(该字符集不唯一),保证能够正常显示中文。
⑦ls /mnt/usb #成功挂载后使用命令进行查看。
#挂载移动硬盘和U盘完全一样。
⑧umount /dev/sdb1#解除挂载(目的是避免损坏或丢失数据)
第五周:Shell编程实现加减乘除
touch arithmetic.sh #创建存放实现运算函数代码的shell文件
vim arithmetic.sh #进入shell文件进行代码编写
编写内容
#!/bin/bash
add () {
return $(($1 + $2))
} #实现+运算
sub () {
return $(($1 - $2))
} #实现-运算
mul () {
return $(($1 * $2))
} #实现*运算
div () {
return $(($1 / $2))
} #实现/运算
touch test.sh #创建测验文件
vi test.sh
编写内容
#!/bin/bash
. arithmetic.sh #调用函数文件
`add 2 2` #用倒引号``调用加运算
echo $? #用$?输出运算结果
`sub 8 4`
echo $?
`mul 7 2`
echo $?
`div 6 2`
echo $?
第六周:Makefile实现静态库、共享库
touch makefile
vi makefile
编写内容
libstatic: usehello.c libhello.c
gcc-c libhello.c
arcr libstatic.a libhello.o
gcc-o static usehello.c libstatic.a
libshared: usehello.c libhello.c
gcc-c libhello.c
gcc-shared -fPIC -o libshared.so libhello.o
gccusehello.c –o dyLib –lshared –L ./
clean:
rm-f *.so
rm-f *.o
rm-f *.a
静态库运行过程:
#make libstatic
#./static
共享库运行过程:
$make libshared
$ LD_LIBRBRY_PATH= ./ ./dyLib
第七周:半期测验
阅读Makefile文件,回答问题:
#如果注释掉all : libmys.so语句的话,需要输入指定执行规则make libmys.so运行
all : libmys.so
SRC=f1.c f2.c f3.c
TGT=$(patsubst %.c,%.o,$(SRC)) #子串替换
%.o : %.c #模式规则所有.c通过执行gcc指令获得.o文件
cc-c $? #代表依赖文件f1.c f2.c f3.c
libmys.so : $(TGT)
cc-shared -o $@ $(TGT)
clean:
rm-f $(TGT)
回答以下问题:
A卷
(1) 此Makefile文件的主要功能是什么?(20分)
创建名为libmys.so的动态库
(2) 此Makefile文件包含多少个规则?它们分别是什么?(20分)
4个;分别是 all 、 %.o 、 libmys.so、clean
(有几个冒号,就有几个规则)
(3) 使用此Makefile文件可以生成目标文件f2.o吗?为什么?(20分)
可以生成f2.o 因为有%.o : %.c 这一模式规则,该规则 使所有的.c的依赖文件编译生成.o的目标文件。
(4) 此Makefile中,请指出存在的伪目标名称。(20分)
all、clean
(5) 请写出$(TGT)的值。(20分)
$(TGT)的值为:f1.o f2.o f3.o
B卷
(1)此Makefile文件中,可以生成共享库,请指出共享库名字。(10分)
libmys.so
(2)如果测试该生成共享库的源文件test.c,请将它编译生成可执行程序并运行。(20分)
gcc test.c -o test -lmys -L ./
LD_LIBRARY_PATH=./
./test
(3)此Makefile文件中,除了显式规则,还使用了哪种规则,请指出并解释。(30分)
还使用了模式规则:%.o : %.c
cc -c $?
该规则使所有的.c的依赖文件编译生成.o的目标文件。
(4)此Makefile文件中,all表示什么目标?(20分)
伪目标
(5)此Makefile中,请指出使用的函数并解释其功能。(20分) TGT=$(patsubst %.c,%.o,$(SRC)):运用patsubst(替换通配符),将$(SRC)中所有的末尾为.c的字符串替换为末尾为.o的字符串。
C卷
(1) 此Makefile文件的主要功能是什么,如何通过命令执行出结果?(20分)
生成共享库libmys.so,执行指令make 或者make all
(2) 请描述该Makefile文件获得执行结果的过程(20分)
① 声明所需要的文件名
② 为f1.c f2.c f3.c生成.o文件
③ 将所生成的.o文件生成共享库
④ 清除中间生成的文件
(3) $?的值是什么?(20分)
f1.c f2.c f3.c
(4) 此Makefile中,请指出存在的伪目标名称。(20分)
all、clean
(4) 请写出$(TGT)的值。(20分)
f1.of2.o、f3.o
第八周:ls模拟实验
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <dirent.h>
//\033(e)[显示方式;前景色;背景色m\033(e)[0m
void get_mode(struct stat buff)//得到当前的文件类型和三个权限
{
char *ptr;
unsigned int i,mask = 0700;
static char *perm[]={"---","--x","-w-","-wx","r--","r-x","rw-","rwx"};//分别对应0 1 2 3 4 5 6 7
if(S_ISREG(buff.st_mode))
ptr="-";
else if(S_ISDIR(buff.st_mode))
ptr="d";
else if(S_ISLNK(buff.st_mode))//符号连接
ptr="l";
printf("%s",ptr);
for(i=3;i;i--)
{
printf("%3s",perm[(buff.st_mode&mask)>>(i-1)*3]);//我们得到的3位8进制数 从左到右分别是 右边没有参与运算的处理掉
mask >>= 3;//mask是掩码
}
}
void get_name(struct stat buff)//得到属主名字 和 属主所在的组 名字
{
struct passwd *user=NULL;//定义一个用户的结构体
struct group *group=NULL;//定义一个组的结构体
user=getpwuid(buff.st_uid);//文件属主的用户身份标识
printf(" %s",user->pw_name);//得到这个标识结构体中的用户名字
group = getgrgid(buff.st_gid);//文件属主的分组身份标识
printf(" %s",group->gr_name);
}
void get_time(struct stat buff)//最新的内容修改时间
{
char *month[]={"1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"};
struct tm *mptr=NULL;//时间结构体
mptr = localtime(&buff.st_mtime);//文件内容方面的上次被修改时间 atime上次被访问时间 ctime文件权限\属主\分组或内容方面的上次被修改时间 这个函数取得文件的时间
printf(" %4s %2d ",month[mptr->tm_mon],mptr->tm_mday);
printf("%02d:%02d",mptr->tm_hour,mptr->tm_min);
}
void list_info(struct stat buff)//文件类型的就列出各种函数得到的结果
{
get_mode(buff);
printf("%5d",buff.st_nlink);//得到硬链接
get_name(buff);
printf("%12ld",buff.st_size);//文件大小
get_time(buff);
}
void list_dir(char *dir_path)//是目录类型的 就
{
int i;
static int count = 0;
DIR *dirp=NULL;//一个文件目录指针
struct dirent *dp=NULL;//存储目录中的文件信息的结构体
struct stat buff;//存储文件信息的结构体
dirp=opendir(dir_path);//打开一个子目录并建立一个子目录流
chdir(dir_path);//进入这个子目录流
while((dp=readdir(dirp)) != NULL)//读这个子目录流是否有内容readdir函数读取到的文件名存储在结构体dirent的d_name成员中
{
if(dp->d_name[0]=='.')//隐藏文件
continue;
if(lstat(dp->d_name,&buff)==-1)//函数lstat的作用就是获取文件名为d_name的文件的详细信息,存储在stat结构体中
{
printf("fail to get stat\n");
continue;
}
for(i = 0;i<count;i++)//深入的次数
{
printf(" \t",count);
}
list_info(buff);
if(S_ISREG(buff.st_mode))
{
printf(" %s\n",dp->d_name);
}
else if(S_ISDIR(buff.st_mode))
{
printf(" \e[1;34m%s\e[0m\n",dp->d_name);
count++;//再次深入
list_dir(dp->d_name);//自身递归 直到该层的目录中的文件列表读完了 才出来
count--;//递归出来深入减1
}
else if(S_ISLNK(buff.st_mode))//如果是链接
{
char sbuf[128];
memset(sbuf,'\0',128);
if(readlink(dp->d_name,sbuf,128)!=-1)//成功找到源
{
printf(" \e[0;36m%s\e[0m -> %s\n",dp->d_name,sbuf);
}else{
printf("error\n");
}
}
}//一直读取到这一层目录文件列表完成
chdir("..");//返回到上级目录
}
int main(int argc,char *argv[])
{
int i;
struct stat buff;//定义stat的结构体
if(argc<2)//缺少给定的参数文件
{
printf("loss argument\n");
return -1;
}
for(i=argc-1;i>=1;i--)
{
if(lstat(argv[i],&buff)==-1)// lstat,是一种文件描述词。意思是获取一些文件相关的信息,成功执行时,返回0。失败返回-1
{
printf("%s error\n",argv[i]);
continue;
}
if(S_ISREG(buff.st_mode))//文件是一个普通文件
{
list_info(buff);
printf(" %s\n",argv[i]);
}
else if(S_ISDIR(buff.st_mode))//文件是一个子目录
{
list_dir(argv[i]);
}
else if(S_ISLNK(buff.st_mode))//文件是一个符号链接
{
list_info(buff);
char sbuf[128];
memset(sbuf,'\0',128);//用‘\0’填充字符串sbuf
if(readlink(argv[i],sbuf,128)!=-1)//readlink取得符号链接所指的文件readlink()会将参数path 的符号连接内容存到参数sbuf 所指的内存空间, 返回的内容不是以NULL作字符串结尾, 但会将字符串的字符数返回. 若参数bufsiz 小于符号连接的内容长度, 过长的内容会被截断.执行成功则传符号连接所指的文件路径字符串
{
printf(" \e[0;36m%s\e[0m -> %s\n",argv[i],sbuf);
}else{
printf("error\n");
}
}
printf("\n");
}
}
。
第九周:内存纠错实验
示例1:
void GetMemory(char *p, int num)
{
p= (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char*str = NULL;
GetMemory(str,100);
strcpy(str,"hello");
}
错误:
(1)空指针非法操作。因为str与p之间是值传递,在函数GetMemory调用结束后,p分配空间的首地址无法存入str,导致str仍然是null,后续字符串拷贝函数则出现了空指针非法操作。
(2)在函数GetMemory中分配的空间没有回收,导致内存泄露。
改正:
参见示例2和示例3。
示例2:
void GetMemory2(char **p, int num)
{
*p= (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
char*str = NULL;
GetMemory2(&str,100);
strcpy(str,"hello");
cout<
free(str);
}
正确。
示例3
char *GetMemory3(int num)
{
char*p = (char *)malloc(sizeof(char) * num);
returnp;
}
void Test3(void)
{
char*str = NULL;
str= GetMemory3(100);
strcpy(str,"hello");
cout<
free(str);
}
正确
示例4
char *GetString(void)
{
charp[] = "hello world";
returnp;
}
void Test4(void)
{
char*str = NULL;
str= GetString();
cout<
}
错误。
(1)出现乱码打印。因为在函数GetString中,p数组拷贝了字符串常量“hello world”的值,但函数调用结束后,p数组回收导致这段空间内容被篡改,但p数组首地址确能成功返回指针变量str中,所有str可以输出,但内容确不是”hello world”。
改正:
参见示例5。
示例5
char *GetString2(void)
{
char*p = "hello world";
returnp;
}
void Test5(void)
{
char*str = NULL;
str= GetString2();
cout<
}
第十周:进程通信实验-电子表
用alarm(单进程实现)(文件名 clock)
#include
#include
#include
#include
#include
#include
#include
#include
int second=0,minute=0,hour=0;
void time(){
if (second > 59 ){
second=0;
minute++;
}
if (minute > 59 ){
minute=0;
hour++;
}
if (minute > 24 ){
hour=0;
}
printf("\r%02d : %02d :%02d\r",hour,minute,second);
second++;
fflush(stdout);
alarm(1); #区别ualarm
}
int main(){
signal(SIGALRM,time);
alarm(1); #区别ualarm
while(1);
return 0;
}
运行
gcc -o clock clock.c
./clock
[ctrl+c可以快速跳出运行界面]
用ualarm实现(文件名 clock2)
#include
#include
#include
#include
#include
#include
#include
#include
int second=0,minute=0,hour=0;
void time(){
if (second > 59 ){
second=0;
minute++;
}
if (minute > 59 ){
minute=0;
hour++;
}
if (minute > 24 ){
hour=0;
}
printf("\r%02d : %02d :%02d\r",hour,minute,second);
second++;
fflush(stdout);
}
int main(){
signal(SIGALRM,time);
ualarm(1000000,1000000); #区别alarm
raise(SIGALRM); #区别alarm
while(1);
return 0;
}
运行
gcc -o clock2 clock2.c
./clock2
用父子进程实现(文件名 clock3)
#include
#include
#include
#include
#include
void handle(int signo) #必须有一个int类型的参数,接收信号
{
if(signo==SIGALRM) #处理SIGALRM信号
{
staticsecond;
staticminutes;
statichours;
staticday = 22;
second++;
if(second==60)
{
minutes++;
second=0;
if(minutes==60)
{
hours++;
minutes= 0;
if(hours==24)
{
day++;
hours= 0;
}
}
}
//\r表示回到开头,输出的内容会覆盖之前的内容
printf("\r11月%02d号%02d:%02d:%02d",day,hours,minutes,second);
fflush(stdout);
#清空缓存,printf标准输出是行缓冲,没有换行需要清空缓冲区才能输出
}
}
int main()
{
intpid;
signal(SIGALRM,handle);#信号安装,每次产生SIGALRM信号就调用handle
if((pid = fork()) == 0)
{
while(1)
{
kill(getppid(),SIGALRM);#给父进程发送SIGALRM信号
sleep(1);
}
}
while(1);#若没有死循环,父进程先退出,信号只发送了一次
return0;
}
运行
gcc -o clock3 clock3.c
第十一周:进程通信实验-输入输出重定向
代码(文件名 redirection)
#include
#include
#include
#include
#include
int main()
{
intfd; #定义test.txt文件的文件描述符
pid_tpid;
intfiledes[2];
pipe(filedes );
charbuf[80] = {0};
printf("echo内容为:\n" );
if(fork()== 0)
{
charstr[] = "";
dup2( filedes[1], 1 );
close(filedes[0] );
scanf("%[^\n]",str); #输出有空格的字符串
execlp("echo", "echo", str, (char *)0 );
close(filedes[1] );
}
else{
fd= open( "test.txt", O_RDONLY | O_WRONLY | O_APPEND | O_CREAT );
close(filedes[1] );
read(filedes[0], buf, sizeof(buf) );
#将管道读入的内容读到buf中
write(fd, buf, sizeof(buf) );
#将buf中的内容写到文件test.txt中
close(filedes[0] );
exit(0);
}
exit(0);
}
运行
gcc -o redirection redirection.c
./redirection
输入语句,回车运行
cat test.txt(查看是否定向输入成功)
第十二周:进程实验
实验内容:编写一段程序,要求:
(1)使用系统调用fork( )创建两个子进程。
(2)当此程序运行时,在系统中有一个父进程和两个子进程活动。
(3)让每一个进程在屏幕上显示一个字符:父进程显示字符“a”,子进程分别显示字符“b”和“c”。请多次运行后观察屏幕上的显示结果,并分析原因。
代码(文件名Process)
#include
#include
#include
#include
int main(void)
{
pid_t pid;
pid = fork();
if (pid == 0)
{
printf("b");
}
else
{
pid= fork();
if(pid==0)
printf("c");
else
printf("a");
}
exit(EXIT_SUCCESS);
}
第十六周:线程实验
利用Linux多线程编程实现以下功能
(1)创建两个线程;
(2)父线程(生产者线程)依次向缓冲区写入整数0,1,2,...,19;
(3)子线程(消费者线程)暂停3s后,从缓冲区读数,每次读一个,并将读出的数字从缓冲区删除,然后将数字显示出来;
(4)父线程等待子线程(消费者线程)的退出信息“exit now”,当父线程接收到这条退出信息并打印后,则退出。
#include
#include
#include
#include
#include
char globe_buffer[100];
void *read_buffer_thread(void *arg);
int main(void)
{
intres,i;
pthread_tread_thread;
for(i=0;i<20;i++)
globe_buffer[i]=i;
printf("\nxianchengthread : write buffer finish\n");
sleep(3);
res = pthread_create(&read_thread, NULL, read_buffer_thread, NULL);
if(res != 0)
{
printf("ReadThread creat Error!");
exit(0);
}
sleep(1);
printf("waiting for read thread to finish...\n");
res= pthread_join(read_thread, NULL);
if(res != 0)
{
printf("read thread joinfailed!\n");
exit(0);
}
printf("read thread xiancheng OK, havefun!! exit ByeBye\n");
return 0;
}
void *read_buffer_thread(void *arg)
{
inti,x;
printf("Read buffer thread read data : \n");
for(i=0;i<20;i++)
{
x=globe_buffer[i];
printf("%d ",x);
globe_buffer[i]=0;
}
printf("\nread over\n");
}