抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

操作系统(02327)实践环节是操作系统专业课的上级测试部分,考核目标是具有熟练使用 Linux 系统常用命令的能力以及初步掌握进程的创建、控制及进程间通信程序的编写方法。运行环境是通过 putty 远程登录工具远程连接 Linux 服务器,在练习时我仍旧使用系统默认终端(zsh)连接云服务器使用。以下为我在学习和实战练习过程中所做的笔记,可供参考。

一、Linux 系统常用命令的使用

登陆命令

建立 SSH 连接:ssh root@zhuangzhihao.top

清屏:ctrl + l 或者 clear

终止当前运行:ctrl + c

执行 exit 命令退回到原来用户:exitlogoutctrl + d

文件处理命令

显示当前目录下的文件:ls

显示当前目录下的所有文件,包括隐藏文件:ls -a

列表显示详细信息:ls -l

在此目录下创建一个 dir 目录:mkdir dir

递归创建,如果没有 dir 目录的话会报错,如果加上 -p 则不会:mkdir -p dir/subdir

同时创建 dir/subdirdir2 三个目录:mkdir -p dir/subdir dir2

切换路径到 root 目录下:cd /root

切换到上一目录:cd ./

切换到下一目录:cd ../

删除当前目录下,一个空白目录 dirrmdir ./dir

显示当前路径:pwd

install.sh 文件,复制到 dir 目录下:cp install.sh dir

dir 目录,复制到 dir2 目录下:cp -r dir dir2

dir 这个目录,复制到 dir2 目录下,如果没有 newdir 目录,那么将 dir 的名字改为 newdircp -r dir dir2/newdir

install.sh 文件,复制到 dir2 目录下,但保证 install.sh 文件属性不会改变,例如最后一次修改的时间不会改变:cp -p install.sh dir2

dir 目录剪切到 dir2 目录:mv dir dir2 (如果没有 dir2 这个目录,呢么就是将 dir 的名字改为 dir2,相当于改名。)

删除 install.sh 文件,出现提示是否删除确认:rm install.sh

删除 install.sh 文件,-f 不出现提示:rm -f install.sh

直接删除 dir 目录,-r 删除目录:rm -rf dir

直接删除此文件夹下的所有文件目录:rm -rf *

创建一个空文件 hello.ctouch hello.c

同时创建 hellohello.c 两个文件:touch hello hello.c

创建一个文件名为 hello hello.c 的文件:touch "hello hello.c"

显示 hello 文件内容:cat hello

显示 hello 文件内容,并显示行数:cat -n hello

倒着显示 hello 文件内容(没有 -n 属性):`tac hello

hello 文件的内容分页显示:more hello(当数据较多时可以使用这命令,使用空格或 f 翻页,q 退出)

hello 文件的内容倒着显示分页显示:less hello

查看 hello 文件前 5 行内容:head -n 5 hello(如果不加行数,默认为 10)

查看 hello 文件后 10 行内容:tail-n hello

链接文件

创建 hello 文件的软连接为 hello.sortln hello ./hello.sort

创建 hello 文件的硬连接为 hello.sortln -s hello ./hello.sort

创建 dir 目录的软连接为 dir.sortln dir ./dir.sort(注意:不允许将硬链接指向目录)

编译链接:gcc -otest.out E1_fork.c(生成可执行文件 test.out

运行当前目录下的可执行文件 test.out./test.out

权限管理命令

改变目录或文件权限:chmod [设置权限的对象]+/-[权限] [文件]

  • 设置权限的对象包括所有者、所属组、其他这三类。u 表示文件的所有者,g 表示文件的所属组,o 代表其他人,+ 代表增加权限,- 代表去掉权限
  • 文件的权限就是读、写和执行,分别用 rwx 表示
  • 多个设置权限可以使用逗号分隔:chmod u+x,g+w abc.txt

abc.txt 文件所属组的权限增加一个写的权限:chmod g+w abc.txt

假设您是名为 myfile 的文件的所有者,并且要设置其权限,以便:user 可以 readwriteexecute 它,且你的 group 的成员可以 readexecute 它,且 others 只能读取它:

1
chmod u=rwx,g=rx,o=r myfile

使用八进制权限表示法的数字权限,4 代表阅读, 2 代表写, 1 代表执行,以及 0 表示无权限。

  • 因此,7 是权限 4 + 2 + 1(读取、写入和执行),5 是 4 + 0 + 1(读取、无写入和执行),4 是 4 + 0 + 0(读取、无写入和未执行)。
  • 数字表示权限,三个数字分别表示所有者,所属组和其他人。
  • 给 user、group 和 others 都赋予读写执行权限:chmod 777 abc.txt

修改 hello.c 文件的所有者为 zhuang:chown zhuang hello.c

修改 hello.c 文件的所属组为 xionggroupchgrp xionggroup hello.c

显示,设置文件的缺省权限:umask -S

文件搜索命令

在当前目录下搜索名字为 myfile 的文件,区分大小写:find ./ -name myfile

在当前目录下搜索名字为 myfile 的文件,不区分大小写:find ./ -i name myfile

在当前目录下搜索名字以 .c 结尾的文件,区分大小写:find ./ -name "*.c"

在当前目录下搜索名字包含 ello 的文件,区分大小写:find ./ -name "*ello*"

在当前目录下搜索名字包含 bb 的目录,区分大小写:find ./ -name "*bb*" -type dd:目录, f:文件, i:软链接)

在文件资料库查找文件 myfile,区分大小写:locate myfile

查询 Linux 系统中命令的位置以及被查询命令帮助文档的位置:whereis cp

查找一个存储所有命令相关信息的数据库,根据命令名返回相关结果:whatis cp

过滤 test.txt 文件中 hello 内容:grep hello test.txt(加 -i 不区分大小写)

统计 test.txthello 内容的行数:grep -c hello test.txt

帮助命令

获得 shell内置命令 date 的帮助信息:help date

获得帮助信息,比命令的 help 选项多了命令的用法示例、命令的描述等内容:man date

用户管理命令

增加一个 zhuang 用户:useradd zhuang

修改 zhuang 用户的密码:passwd zhuang

切换用户为 zhuangsu zhuang

显示目前登入系统的用户信息:who 或者 w

压缩解压命令

myfile 文件压缩为 myfile.gzgzip myfile(只能压缩文件,原文件不保留)

解压 myfile.gz 文件:gunzip myfile.gz

将目录 dir 直接打包并压缩为 name.tar.gz 打包目录:tar -zcf name.tar.gz dir(.tar.gz)

name.tar.gz 打包目录解压:tar -zxf name.tar.gz-x:解包,-c:打包,-v:显示详细信息,-f:指定文件名,-z:同时打包压缩)

将目录 dir 压缩为 name.zipzip -r name dir

name.zip 解压:unzip name.zip

网络命令

给用户 zhuang 发送信息(开启聊天框),按 ctrl+d 结束:write zhuang

给所有在线用户发送广播 helloworldwall helloworld

查看网卡和设置信息:ping

查看网卡和设置网卡信息:ifconfig

列出过去和现在登入系统的用户信息:last

列出特定用户上次登录时间:lastlog

关机重启命令

重启:reboot

立刻关机:haltpoweroff

立刻重启(root 用户使用):shutdown -r now(-c:取消前一个关机命令,-h :关机,-r :重启)

过10分钟自动重启(root用户使用):shutdown -r 10

过10分钟自动关机(root用户使用):shutdown -h 10

在时间为 21:00 时候重启(root用户使用):shutdown -r 21:00

如果是通过 shutdown 命令设置重启或关机的话,可以用 shutdown -c 命令取消重启。

退出登录命令:logout

二、进程的创建、控制及进程间通信

getpid() 获取 PID

getpid 获取当前进程 ID,getppid 获取父进程 ID。

pid_t 是 C 语言中用户自定义类型,在 sys/types.h 中定义,typedef int pid_t;

1
2
3
4
5
#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void);
pid_t getppid(void);

打印当前进程的 PID:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
printf("pid=%x, ppid=%x\n", getpid(), getppid());
return 0;
}

fork() 创建进程

fork 创建一个子进程,父子进程并发运行,子进程复制父进程的如下属性:

  • 代码段、数据段的内容,父子进程拥有相同的代码和数据。
  • 打开文件列表。

fork 不复制进程的 PID 属性,父子的进程的 PID 都是唯一的。

返回值:

  • pid 是进程 ID 的缩写,pid_t 是使用 typedef 定义的进程 ID 类型。
  • 父进程从 fork 返回处继续执行,在父进程中,fork 返回子进程 PID。
  • 子进程从 fork 返回处开始执行,在子进程中,fork 返回 0。
1
2
3
#include <unistd.h>

pid_t fork(void);

创建子进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main()
{
pid_t pid;
int x = 1;

pid = fork(); //创建子进程
if (pid == 0) { /* Child */
printf("child : x=%d\n", ++x);
printf("pid=%d\n", pid);
exit(0);
}
printf("pid=%d\n", pid);
/* Parent */
printf("parent: x=%d\n", --x);
exit(0);
}
/*
输出结果:
parent: x=0
pid=15112
child : x=2
pid=0
*/

fork() 进程有以下几个特点:

  • 调用 1 次,返回 2 次。 一次是在父进程中执行,一次是在刚创建的子进程中执行。 pid 为 0,表示当前在子进程中。
  • 并发执行。 父进程和子进程是并发运行的独立进程。 内核以任意方式交替执行,可能会先执行父进程,也可能会先执行子进程。 程序员应该避免猜测谁先执行来做一下逻辑处理。
  • 拥有相同且独立的地址空间:刚 fork 出的子进程和父进程拥有相同的用户栈、堆,变量。但他们相互独立的。 例如例子中:输出 x 的变量值,child 自增 1 与 parent 自减 1 互不影响。

其他进程控制相关函数

wait():查询子进程的退出状态(即正常退出的退出码或导致异常终止的信号),并回收子进程的内核资源。若成功则返回进程 ID,若出错则返回 -1。

1
2
#include <sys/wait.h>
pid_t wait(int * status);

wait() 函数常用来控制父进程和子进程的同步。在父进程中调用 wait() 函数,则父进程被阻塞,进入等待队列,等待子进程结束。当子进程结束时,会产生一个终止状态字,系统会向父进程发送 SIGCHLD 信号。当接到信号后,父进程提取子进程的终止状态字,从 wait() 函数返回继续执行原程序。

exit():进程结束最常调用的函数,在 main() 函数中调用 return,最终也是调用 exit() 函数。这些都是进程的正常终止。在正常终止时,exit() 函数返回进程结束状态。

1
2
#include<stdio.h>
void exit(int status)

从 OS 角度来看,进程终止会释放进程用户空间的所有资源,而进程描述符并不释放,它将来由其父进程回收。若父进程先于子进程终止,子进程成为孤儿进程,孤儿进程会被 init 进程(进程号为1)领养。若子进程先于父进程终止,子进程成为僵尸进程,父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍占用的资源)。

程序可以执行到 main() 的最后一个 } 的结束,也可以运行 exit(); 结束,也可以运行到 return 0; 结束。

execl():系统调用 execl() 可以将新程序加载到某一进程的内存空间,该进程会从新程序的 main() 函数开始执行。

1
2
3
#include <unistd.h>
int execl (const char * pathname, const char * arg0,…/*(char*)0*/)
// pathname是要加载程序的全路径名,arg0及以后为参数列表。

进程的用户内存空间被替换,而进程的其他属性基本不改变。进程完整的内存空间:正文区、堆区、栈区、数据区都被替换,原内容不存在了。代码替换完后,在 execl() 后面的代码毫无意义。

创建多个进程:

1
2
3
4
5
6
7
8
9
10
11
12
//print1.c
#include<stdio.h>
#define NUM 5
int main(int argc,char *argv[])
{
for(int i=0;i<NUM;i++){
printf("%s\n",argv[1]);
fflush(stdout);
sleep(1);
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//multi_process.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main(void){
pid_t pid[3];
int i;
for(int i=0;i<3;i++){
pid[i]=fork();
if(pid[i]==0){
break;//防止子进程再次创建子进程
}
}
if(pid[0]==0){
execl("print1","print","Good",NULL);
}else{
if(pid[1]==0){
execl("print1","print","Hello",NULL);
}else{
if(pid[2]==0) execl("print1","print","201919044217",NULL);
wait();
wait();
wait();//调用一次,只能回收一个子进程
exit(0);
}
}
return 0;
}

运行:

1
2
3
gcc print1.c –o print1
gcc multi_process.c –o multi
time ./multi #获取到一个程序的执行时间,包括程序的实际运行时间(real time),以及程序运行在用户态的时间(user time)和内核态的时间(sys time)

进程间通信(IPC)

注意:在超级用户(管理员)下才可以进行共享内存进程通信,否则提示段错误。使用 su - 切换。

共享内存实现 IPC 基本步骤:

  1. 获得唯一的 key 值:一般采用 ftok 得到唯一的键值,
  2. 使用 shmget 创建或获得共享内存区,
  3. 使用 shmat 将共享内存区附加到进程中,
  4. 使用共享内存区,
  5. 与共享内存区脱离,但是记住这时共享内在区,依然存在,必须等到内核重启,
  6. 申请的共享内存区的大小是有限制的。

实验内容:应用 Linux 共享内存机制,实现两个进程间相互传递一个学生的记录,包括:

  1. 学号:SID,char 型,8 位
  2. 姓名:NAME,char 型,8 位
  3. 年龄:Age,int 型
  4. 专业:Specialty,char 型,8 位
  5. 班级:Class,char 型,4 位一个记录约 30 个字节。

共享内存函数由 shmgetshmatshmdtshmctl 四个函数组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//shm1
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.n>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

typedef struct{
char name[4];
int age;
}people;

int main(int arge, char *argvll)
{
int shm_id, i;
key_t key;
char temp;
people *p_map;
char *path = "./shm1.c";
/*
使用 ftok 根据 path 和作为项目标识符的单个字符生成 key 值确保进程间使用相同的 key
使用相同 key 值的 shmget 只会在第一次时创建新结构
*/
key = ftok(path, 0); //产生 key 值
if(key == -1){
perror("ftok error \n");
return -1;
}
shm_id = shmget(key, 4096, IPC_CREAT); //创建大小为 4096 字节的共享内存段,IPC_CREAT是创建标志,代表如果不存在就创建,成功则返回共享存储的 ID。
if(shm_id == -1)
{
perror(" shmget error \n");
return - 1;
}
p_map = (people*)shmat(shm_id, NULL, O);
/*将共享内存附加到进程中,允许进程访问
第二个参数用来指定共享段所连接到的地址,为了便于在不同硬件间移植,一般设为 NULL,即不指定共享段所连接到的地址,让系统自己选择合适的地址。
第三个参数 0 指定以读写方式访问共享段。
函数返回实际共享段连接到的地址。
*/
temp = 'a';
for(i=0; i<10; i++)
{
temp += 1; //字符加一为 'b'
memcpy((*(p_map + i)).name, &temp, 1); //*(p_map).name 'b',age1 name2,age2
//从第二个参数指定的位置拷贝一个字节到第一个参数指定的位置中
(*(p_map + i)).age = 20 + i; // 'b',20
}
system("ipcs -m"); //ipcs 命令往标准输出写入一些关于活动进程间通信设施的信息,-m表示为共享内存
if(shmdt(p_map) == -1) //与 shmat 函数相反,用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存,参数为连接共享内存的起始地址。
{
perror("detach error");
}
system("ipcs -m");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//shm2
/*
System V共享内存区是放在内核当中的,因此在内核重新引导之前,对数据的修改是一直保持的,这也是我们的实验能够实现的原因,因为在第二个进程起动时,第一个进程已经运行结束了.
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.n>
#include <stdlib.h>
// 完成从共享内存区的读出
typedef struct{
char name[4];
int age;
}people;

int main(int arge, char *argvll)
{
int shm_id, i;
key_t key;
people *p_map;
char *path = "./shm1.c";
key = ftok(path, 0); //得到一个一样的 key
if(key == -1){
perror("ftok error \n");
return -1;
}
shm_id = shmget(key, 4096, IPC_CREAT); // 得到一样的id
if(shm_id == -1)
{
perror(" shmget error \n");
return - 1;
}
p_map = (people*)shmat(shm_id, NULL, O);
for(i=0; i<10; i++)
{
printf("name:%s\t",(*(p_map + i)).name);
printf("age:%d\n",(*(p_map + i)).age);
}
system("ipcs -m"); //ipcs 命令往标准输出写入一些关于活动进程间通信设施的信息,-m表示为共享内存
if(shmdt(p_map) == -1) //与 shmat 函数相反,用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存,参数为连接共享内存的起始地址。
{
perror("detach error \n");
return -1;
}
system("ipcs -m");
exit(EXIT_SUCCESS);
}

评论



Copyright © 2020 - 2022 Zhihao Zhuang. All rights reserved

本站访客数: 人,
总访问量: