大学linux实验汇总

实验目的:

1.学会在操作系统安装之前,根据硬件配置情况,制定安装计划。

2.掌握多操作系统安装前,利用硬盘分区工具(如PQMagic、分区助手6.3等)为Linux准备分区。

3.掌握Linux操作系统的安装步骤。

4.掌握Linux系统的简单配置方法。

5.掌握Linux系统的启动、关闭步骤。

实验内容:
1.安装并使用硬盘分区工具(如PQMagic、分区助手6.3等),为Linux安装准备好分区。
2.安装Linux系统(如普华Linux桌面版V4.0)。
3.配置Linux 系统运行环境。
4.正确地启动、关闭系统。
主要实验步骤:
1.制订安装计划。
2.若在计算机上已安装了Windows系统,没有给Linux 预备硬盘分区,则安装硬盘分区工具(如PQMagic、分区助手6.3等),运行它,为Linux划分出一块“未分配”分区。
3.在光驱中放入Linux 系统安装盘,启动系统。按照屏幕提示,选择/输入相关参数,启动安装过程。
4.安装成功后,退出系统,取出安装盘。重新开机,登录Linux系统。
5.对Linux系统进行配置,包括显示设备、打印机等。
6.安装软件工具和开发工具。

如图所示,已经使用VMware虚拟机正确在安装好linux系统。

掌握Linux一般命令格式

掌握有关文件和目录操作的常用命令

掌握有关进程操作的常用命令

熟练使用man命令

1. 正确地进入登出系统,
2,熟悉date  cal.who、 echo、clear、 passwd 命令
3.在用户主目录下对文件进行如下操作;复制一个文件显示内容,查找指定内容、排序、 文件比较、文件删除等,
4.对目录进行管理:创建和制除子目录、改变和显示工作目录、列出和更改文件权限
、链接文件等。
5.利用man命令显示date、echo等命令的手册页
6.  显示系统中的进程信息,

学习使用vi 编辑器建立,编辑,显示及加工处理文本文件。

进入和退出vi

利用文本插入方式建立一个文件

在新建的文本文件上移动光标位置

对该文件执行删除,复原,修改,替换等操作

输入vim file.c进入文本编辑器,输入相应c++代码

Esc再输入:wq保存退出文件

输入sudo yum install gcc-c++下载c++

输入g++ file.c -o file

无报错

再输入./file进行运行

输入man date >file10

X命令删除当前字,dd实现向后删除,u命令是还原操作,c,r,s命令都可以进入insert模式,/xxxx进行对于xxxx文字的检索,光标会跳转至检索文字的位置。

了解shell的特点和主要种类

掌握shell脚本的建立和执行方式

掌握bash的基本语法

学会编写shell脚本

#!/bin/bash

# 1. 包含常用命令的脚本

echo "当前日期:"

date

echo "当前日历:"

cal

echo "当前工作目录:"

pwd

echo "当前目录内容:"

ls

# 2. 配置历史命令环境

echo "配置历史命令环境..."

HISTSIZE=1000

HISTFILESIZE=2000

HISTCONTROL=ignoredups:erasedups

HISTTIMEFORMAT="%F %T "

# 3. 命令补齐功能

echo "启用命令补齐功能..."

source /etc/bash_completion

# 4. 定义别名

echo "定义别名..."

alias ll='ls -al'

alias grep='grep --color=auto'

# 5. 编辑并执行自 shell 脚本

echo "编辑并执行自 shell 脚本..."

vi myscript.sh

chmod +x myscript.sh

./myscript.sh

# 6. 按习题4.14要求编写脚本并执行

echo "按习题4.14要求编写脚本并执行..."

vi myscript2.sh

chmod +x myscript2.sh

./myscript2.sh

# 7. 按习题4.18要求编写脚本并执行

echo "按习题4.18要求编写脚本并执行..."

vi myscript3.sh

chmod +x myscript3.sh

./myscript3.sh

# 8. 运行例4.20的程序

echo "运行例4.20的程序..."

vi example.sh

chmod +x example.sh

./example.sh

# 9. 测试取消"e"选项的情况

echo "测试取消'e'选项的情况..."

set -e

echo "这是一条测试语句"

echo "如果前一条命令失败,这条语句将不会执行"

echo "脚本执行完毕。"

3、实验结果分析:

(1)利用vi建立脚本文件ex1,运用三种方法执行脚本

(2)运行history命令,并配置历史命令环境,使bash保存500条历史命令

(3)利用tab键补全bash命令

(6)编辑脚本ex3,输入斐波那契数列前十项和

掌握Linux操作系统内核编译的方法,加深对Linux系统整体结构的理解。

编译内核的解决方案:

1安装编译内核所需的软件包

2下载源代码

3解压缩

4给内核打补丁

5配置内核

6编译内核

7安装内核

实验步骤

本实验将在Ubuntu 16下动手编译并安装一个新的Linux内核。该发行版的下载地址为

http://mirrors.aliyun.com/ubuntu-releases/16.04/ubuntu-16.04.7-server-amd64.iso

用uname –r命令,可以查看当前内核的版本号

使用root用户登录,修改/etc/ssh/sshd_config文件,把

PermitRootLogin without-password

改为PermitRootLogin Yes

保存退出,重启。

这样可远程以root用户身份登录ubuntu系统

1安装编译内核所需的软件包。

apt-get update

apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison

2下载源代码

Linux内核源代码可以从官方网站The Linux Kernel Archives中下载。本实验,选择国内镜像网站下载。

wget http://mirrors.aliyun.com/linux-kernel/v5.x/linux-5.16.12.tar.xz

3解压缩

xz -d l linux-5.16.12.tar.xz

tar -xvf linux-5.16.12.tar

cp linux-5.16.12 /usr/src -rf

4配置内核

cp -v /usr/src/linux-headers-$(uname -r)/.config /usr/src/linux-5.16.12

cd /usr/src/linux-5.16.12/

运行命令make menuconfig,保存退出

使用文本编辑器,修改Makefile文件,将版本号中的第3位改为学生学号的后3位(例如023),例如

5编译内核

cd /usr/src/linux-5.16.12/

nohup make >xxx 2>&1 &

上述命令是在后台运行,进程结束后,运行结果保存在xxx文件中。

在后台运行时,会出现该进程的PID值。

可以用ps –ef命令查看PID在不在了,如果不在了,就表明编译结束了

也可以在前台执行如下命令

make

无论是在前台编译,还是在后台编译,整个编译过程需要花费较多时间,机器性能不同,编译时间也不同。在一台双核的机器上,可以使用make –j4,使得make最多允许4条编译命令同时执行。在一台4核的机器上,可以使用make –j8,使得make最多允许8条编译命令同时执行,从而更有效的利用CPU资源。

7 安装内核

cd /usr/src/linux-5.16.12

make modules_install

make install

重启动之后,用uname –r命令查看内核当前的版本号,可以看到目前Ubuntu的内核已经更换为新的内核版本了。

1.更新软件源并安装所需软件包

sudo yum groupinstall "Development Tools"

sudo yum install ncurses-devel elfutils-libelf-devel openssl-devel bc

2. 下载最新稳定版Linux内核源码

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.16.18.tar.xz3.

3.解压内核源码

xz -d linux-5.16.18.tar.xz

tar -xvf linux-5.16.18.tar

4复制源码到内核源码目录:

sudo cp -r linux-5.16.18 /usr/src/kernels

5. 进入内核源码目录并复制配置文件:

cd /usr/src/kernels/linux-5.16.18

cp /boot/config-$(uname -r) .config

在CentOS 7上安装ncurses开发库的命令:

sudo yum install ncurses-devel

安装更新的软件源:

sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

安装GCC最新版本:

sudo yum install gcc gcc-c++

安装CentOS软件集合(SCL)源:

sudo yum install centos-release-scl

安装devtoolset-9软件包,它包含了GCC 9.3.1版本:

sudo yum install devtoolset-9

启用GCC 9.3.1:

sudo scl enable devtoolset-9 bash

5.切换到内核源代码目录,并使用新版GCC重新编译:

cd /usr/src/kernels/linux-5.16.18

make clean

make menuconfig

缺少libelf开发库

sudo yum install elfutils-libelf-devel

缺少OpenSSL开发库

sudo yum install openssl-devel

6. 修改内核版本号(将最后三位改为你的学号,如2130110259):

7. 开始编译内核(并行任务数可根据CPU数调整):

make bzImage -j4

8. 安装模块和内核:

make modules_install

sudo make install

9. 配置内核引导项:

sudo grub2-mkconfig -o /boot/grub2/grub.cfg

10. 重启系统并检查内核版本:

reboot

uname -r

  1. 掌握 C 语言编译的基本用法。
  2. 掌握 gdb 调试工具的基本用法。
  3. 理解 make 工具的功能,学会编制 makefile 的方法。

  1. 利用 gcc 编译 C 语言程序,使用不同选项,观察并分析显示结果。
  2. 用 gdb 调试一个编译后的 C 语言程序。
  3. 编写一个由多个文件构成的 C 语言程序,编制 makefile,运行 make 工具进行维护。

Linux系统大致可分为三层:

靠近硬件的底层是内核,即Linux操作系统常驻内存部分。

中间层是内核之外的shell层,即操作系统的系统程序部分。

最高层是应用层,即用户程序部分

从结构上看,Linux操作系统是采用单块结构的操作系统。

一般说来,可以将操作系统划分为内核和系统程序两部分。

●进程控制系统用于进程管理、进程同步、进程通信、进程调度和内存管理等。

●内存管理控制内存分配与回收。

●文件系统管理文件、分配文件空间、管理空闲空间、控制对文件的访问并为用户检索数据。

●Linux系统支持三种类型的硬件设备:字符设备、块设备和网络设备。

●核心底层的硬件控制负责处理中断以及与机器通信。

Linux的各个发行版本(distribution),都是使用Linus主导开发并发布的同一个Linux内核,因此在内核层不存在什么兼容性问题。每个版本都不一样的感觉,只是在发行版本的最外层才有所体现,而绝不是Linux本身特别是内核不统一或是不兼容。SUSE、RedHat、Ubuntu、Slackware等直接说成是Linux是不确切的,它们是Linux的发行版本,更确切地说,应该叫做“以Linux为核心的操作系统软件包”。

1. 利用-I搜索头文件路径,利用-D声明DOPTION,之后编译运行hello.c

#include <stdio.h>

#define fatal "please call Lawrence for help"

int main() {

    /* testing CPP option */

    printf("display -D variable %s\n", "testing -D ");

    printf("display overwrite fatal=%s\n", fatal);

    printf("Hello, everybody!!\n");

    return 0;

}

2. 初步尝试编译时产生问题,进入gdb调试后发现p只分配了30字节内存而后续访问p[50],导致内存越界访问,同时在释放p后再次尝试访问p[0],修改代码后再次编译运行

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

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

    char *p;

    if ((p = malloc(31 * sizeof(char))) == NULL) {

        fprintf(stderr, "Memory allocation failed\n");

        return 1;

    }

    strncpy(p, "not 30 bytes", 30);

    p[30] = '\0';

    printf("p=<%s>\n", p);

    if (argc == 2) {

        if (strcmp(argv[1], "-b") == 0) {

            if (strlen(p) < 29) {

                p[strlen(p)] = 'a';

                p[strlen(p) + 1] = '\0';

            }

        } else if (strcmp(argv[1], "-f") == 0) {

            free(p);

            p = NULL;

        }

    }

    if (p != NULL) {

        free(p);

    }

    return 0;

}

3. 编译后进入gdb,尝试运行成功

#include <stdio.h>

#include <stdlib.h>

int make_key(void);

int get_key_num(void);

int number(void);

int main(void) {

    int ret = make_key();

    printf("make_key returns %d\n", ret);

    exit(EXIT_SUCCESS);

    return 0; // 可选,因为 exit 已经结束了程序

}

int make_key(void) {

    int ret = get_key_num();

    return ret;

}

int get_key_num(void) {

    int ret = number();

    return ret;

}

int number(void) {

    return 10; // 返回一个具体的数值

}

4. 编写当前目录下makefile文件,建立需要的a.c b.c c.c d.c assmb.s,之后在终端进行make指令,ls查看当前目录下成功生成proc文件,最后使用make clean指令清除编译过程中产生的.o文件。

Makefile文件

# 定义对象文件变量,以.c和.s后缀的源文件名作为键

OBJECTS = a.o b.o c.o d.o assmb.o

# 定义默认的目标(可执行文件)

prog: $(OBJECTS)

    gcc -o prog $(OBJECTS) -L/home/user/lib -lm

# 规则来生成C语言源文件的对象文件

%.o: %.c defs.h

    gcc -c $< -o $@

# 规则来生成汇编语言源文件的对象文件

%.o: %.s

    as $< -o $@

# 清理生成的对象文件

clean:

    rm -f $(OBJECTS) prog

# 默认的目标(当执行 make 时没有指定目标时)

.PHONY: all clean

all: prog

5.

   /* main.c */

#include <stdlib.h>

#include "a.h"

extern void function_two();

extern void function_three();

int main()

{

    function_two();

    function_three();

    exit (EXIT_SUCCESS);

}

接着,用vi编辑器创建文件2.c

/* 2.c */

#include "a.h"

#include "b.h"

void function_two() {

}

然后,用vi编辑器创建文件3.c

/* 3.c */

#include "b.h"

#include "c.h"

void function_three() {

}

头文件实际上都是空文件,可以用touch命令来创建它们:

$ touch a.h

$ touch b.h

$ touch c.h

用vi编辑器创建如下文件,名为Makefile1

myapp: main.o 2.o 3.o

    gcc -o myapp main.o 2.o 3.o

main.o: main.c a.h

    gcc -c main.c

2.o: 2.c a.h b.h

    gcc -c 2.c

3.o: 3.c b.h c.h

    gcc -c 3.c

  1. 理解系统调用和库函数的异同。
  2. 学会用系统调用和库函数进行编程。
  3. 掌握一些常用的系统调用和库函数的功能及应用。

  1. 使用系统调用对文件进行操作。
  2. 使用系统调用对进程进行控制。
  3. 使用管道机制进行 1/0。
  4. 使用信号机制进行进程通信。

3、实验结果分析:

  1. 编写程序ex7.3.c,编译运行时接收两个文件,分别打开两个文件,用read函数读取第一个文件内容,存取到缓冲区后利用write函数写入第二个文件中

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <string.h>

#define BUFFER_SIZE 4096

/**

 * Copy the contents of the source file to the destination file.

 * @param source_file Path to the source file.

 * @param dest_file Path to the destination file.

 * @return 0 on success, -1 on failure.

 */

int copy_file(const char *source_file, const char *dest_file) {

    int source_fd = open(source_file, O_RDONLY);

    if (source_fd == -1) {

        fprintf(stderr, "Failed to open source file: %s\n", source_file);

        return -1;

    }

    int dest_fd = open(dest_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);

    if (dest_fd == -1) {

        fprintf(stderr, "Failed to open destination file: %s\n", dest_file);

        close(source_fd);

        return -1;

    }

    char buffer[BUFFER_SIZE];

    ssize_t bytes_read, bytes_written;

    while ((bytes_read = read(source_fd, buffer, BUFFER_SIZE)) > 0) {

        bytes_written = write(dest_fd, buffer, bytes_read);

        if (bytes_written != bytes_read) {

            fprintf(stderr, "Failed to write to destination file: %s\n", dest_file);

            close(source_fd);

            close(dest_fd);

            return -1;

        }

    }

    if (bytes_read == -1) {

        fprintf(stderr, "Failed to read from source file: %s\n", source_file);

        close(source_fd);

        close(dest_fd);

        return -1;

    }

    close(source_fd);

    close(dest_fd);

    return 0;

}

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

    if (argc != 3) {

        fprintf(stderr, "Usage: %s <source_file> <destination_file>\n", argv[0]);

        return EXIT_FAILURE;

    }

    if (copy_file(argv[1], argv[2]) != 0) {

        return EXIT_FAILURE;

    }

    return EXIT_SUCCESS;

}

二:先在父进程中创建子进程,父进程打印自己PID和子进程PID,随后等待直到子进程自己销毁,子进程打印自己和其父PID并输入当前在子进程中,之后销毁回到父进程,输入当前所在父进程中

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <string.h>  
#include <sys/wait.h>  
#define FILE_NAME "output.txt"  
#define BUFFER_SIZE 100

int main() {  
 int fd, bytesWritten; 
 char buffer[BUFFER_SIZE];  
 pid_t pid = fork();  
 if (pid < 0) {  
  perror("Fork failed");  
  exit(EXIT_FAILURE);  
}    
 fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0644);  
 if (fd == -1) {  
  perror("Open file failed");  
  exit(EXIT_FAILURE);  
}  
 if (pid > 0) { 
  snprintf(buffer, BUFFER_SIZE, "Parent PID: %d, PPID: %d\n", getpid(), getppid());  
 bytesWritten = write(fd, buffer, strlen(buffer));  
 if (bytesWritten < 0)
  perror("Parent write failed");
 wait(NULL);    
 snprintf(buffer, BUFFER_SIZE, "This is written by the parent process.\n");  
 bytesWritten = write(fd, buffer, strlen(buffer));  
 if (bytesWritten < 0)  
  perror("Parent write failed");   
}  
 else { 
  snprintf(buffer, BUFFER_SIZE, "Child PID: %d, PPID: %d\n", getpid(), getppid());  
  bytesWritten = write(fd, buffer, strlen(buffer));  
  if (bytesWritten < 0) 
   perror("Child write failed");  
  snprintf(buffer, BUFFER_SIZE, "This is written by the child process.\n");  
  bytesWritten = write(fd, buffer, strlen(buffer));  
  if (bytesWritten < 0)  
   perror("Child write failed");
  fflush(NULL);  
  close(fd);
}
if (pid > 0) {  
  close(fd);  
}     
  return 0;  
}

三:先在程序中创建一个管道,利用write函数写入数据到管道中,之后利用read函数读取管道内容

/* pipedemo.c */

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h> // 包含exit函数的声明

#include <string.h> // 包含strlen函数的声明

#include <errno.h>

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

    static const char mesg[] = "Happy New Years to you!";

    char buf[BUFSIZ];

    size_t rcount, wcount; // 声明wcount

    int p_fd[2];

    size_t n;

    if (pipe(p_fd) < 0) {

        fprintf(stderr, "%s: pipe failed: %s\n", argv[0], strerror(errno));

        exit(1);

    }

    printf("Read end=fd %d, write end=fd %d\n", p_fd[0], p_fd[1]);

    n = strlen(mesg);

    if ((wcount = write(p_fd[1], mesg, n)) != n) {

        fprintf(stderr, "%s: write failed: %s\n", argv[0], strerror(errno));

        exit(1);

    }

    if ((rcount = read(p_fd[0], buf, BUFSIZ)) != wcount) {

        fprintf(stderr, "%s: read failed: %s\n", argv[0], strerror(errno));

        exit(1);

    }

    buf[rcount] = '\0';

    printf("Read <%s> from pipe\n", buf);

    close(p_fd[0]);

    close(p_fd[1]);

    return 0;

}

四:首先创建一个队列,输入获取的消息,把消息加到队列中,之后利用msgctl函数提取队列显示消息

#include <unistd.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define MSGTYPE 1

struct msg {

    long type;

    char text[BUFSIZ];

};

int main(void) {

    int qid;

    key_t key;

    int msgid;

    struct msg msg_snd, msg_rcv;

    char *hello = "Hello, World!";

    key = ftok("somefile", 'a');

    if (key == -1) {

        perror("ftok failed");

        exit(EXIT_FAILURE);

    }

    msgid = msgget(key, 0666 | IPC_CREAT);

    if (msgid == -1) {

        perror("msgget failed");

        exit(EXIT_FAILURE);

    }

    msg_snd.type = MSGTYPE;

    strcpy(msg_snd.text, hello);

    if (msgsnd(msgid, &msg_snd, strlen(msg_snd.text), 0) == -1) {

        perror("msgsnd failed");

        exit(EXIT_FAILURE);

    }

    if (msgrcv(msgid, &msg_rcv, sizeof(msg_rcv.text), MSGTYPE, 0) == -1) {

        perror("msgrcv failed");

        exit(EXIT_FAILURE);

    }

    printf("Received message of type: %ld\n", msg_rcv.type);

    printf("Message text: %s\n", msg_rcv.text);

    if (msgctl(msgid, IPC_RMID, NULL) == -1) {

        perror("msgctl(IPC_RMID) failed");

        exit(EXIT_FAILURE);

    }

    return EXIT_SUCCESS;

}

五:先创建一个子进程,输出一行信息表示父进程向子进程发送信号,随后父进程进入等待,子进程接收信号,输出自己的PID和其父的PID,之后终止自己进程,回到父进程结束程序

#include <stdio.h>         
#include <stdlib.h>    
#include <unistd.h>        
#include <fcntl.h>      
#include <string.h>       
#include <sys/wait.h>

int main(void){
 pid_t pid;
 int num;
 if((pid=fork())<0){
  perror("fork failed");
  exit(EXIT_FAILURE);
}
 else if(pid==0){
  printf("Sending SIGCHLD to %d\n",getpid());
  printf("I am the child process, my PID is %d, and my parent&apos;s PID is %d\n", getpid(), getppid());
  exit(0);}
 else{
  waitpid(pid,NULL,0);
  printf("Child process has been killed");
}
 exit(EXIT_SUCCESS);
}

了解Linux线程管理方面的知识,掌握线程创建的用法。

实验背景知识

线程是操作系统能够调度和执行的基本单位,在Linux中也被称之为轻量级进程,LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下)。

进程:独立地址空间,拥有PCB

线程:也有PCB,但没有独立的地址空间(共享)

区别:在于是否共享地址空间。 独居(进程);合租(线程)。

Linux下: 线程:最小的执行单位,调度的基本单位。

进程:最小分配资源单位,可看成是只有一个线程的进程。

类Unix系统中,早期是没有“线程”概念的,80年代才引入,借助进程机制实现出了线程的概念。因此在这类系统中,进程和线程关系密切。

轻量级进程(light-weight process),也有PCB,创建线程使用的底层函数和进程一样,都是clone。从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表是相同的。进程可以蜕变成线程,线程可看做寄存器和栈的集合,在linux下,线程最是小的执行单位;进程是最小的分配资源单位。对于进程来说,相同的地址(同一个虚拟地址)在不同的进程中,反复使用而不冲突。原因是他们虽虚拟址一样,但,页目录、页表、物理页面各不相同。相同的虚拟址,映射到不同的物理页面内存单元,最终访问不同的物理页面。但线程不同,两个线程具有各自独立的PCB,但共享同一个页目录,也就共享同一个页表和物理页面。所以两个PCB共享一个地址空间。

实际上,无论是创建进程的fork,还是创建线程的pthread_create,底层实现都是调用同一个内核函数clone。如果复制对方的地址空间,那么就产出一个“进程”;如果共享对方的地址空间,就产生一个“线程”。因此:Linux内核是不区分进程和线程的。只在用户层面上进行区分。所以,线程所有操作函数 pthread_* 是库函数,而非系统调用。

pthread_create函数:创建一个新线程。其作用,对应进程中fork() 函数。

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

返回值:成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。

参数:

pthread_t:当前Linux中可理解为:typedef  unsigned long int  pthread_t;

参数1:传出参数,保存系统为我们分配好的线程ID

参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。

参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。

参数4:线程主函数执行期间所使用的参数,如要传多个参数, 可以用结构封装。

pthread_join函数  阻塞等待线程退出,获取线程退出状态,其作用对应进程中 waitpid() 函数。

int pthread_join(pthread_t thread, void **retval); 成功:0;失败:错误号

参数:thread:线程ID

retval:存储线程结束状态。

调用该函数的线程将挂起等待,直到id为thread的线程终止。

thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值。

如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。

如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数。

如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。

  1. 理解系统管理的内涵和作用。
  2. 学会对用户和组进行一般管理。
  3. 学会在Linux环境下配置邮件环境。
  4. 学会网络配置的一般方法。

为新用户建立账号和工作组,删除本地用户和组。

在U盘上建立文件系统,并进行安装。

在Linux环境下收发邮件。

        配置网络。

主要实验步骤

1.分别以普通用户和 root 身份登录,看能否建立新用户账号

2.为新用户(如 Zhang)建立账号和工作组,并进行相应配置。以该用户身份登录,修改密码等。最后删除该用户

编写shell脚本,批量创建和删除用户。编写一个shell脚本myadduser.sh,完成下列功能,创建30个用户stu001,stu002,..,stu030,初始密码设置分别为001,002,…,030。

编写一个批量删除用户的脚本mydeluser.sh,完成以下功能,把用户名stu001,…,stu020的这20个用户删除。

3.在U盘上建立文件系统,并进行安装。

4.配置网络信息,浏览校园网信息。

5.配置mail环境,发送和接收邮件。

3、实验结果分析:

一:创建threadtest.c,利用Linux系统使用 pthread 库创建一个新线程

#include <pthread.h>

#include <stdio.h>

void *create(void *arg){

printf("new thread created ...");

}

int main(int argc,char *argv[])

{

pthread_t tidp;

int error;

error=pthread_create(&tidp,NULL,create,NULL);

if(error !=0)

{

printf("pthread_create is not created...");

return -1;

}

printf("pthread_create is created...");

}

因为pthread库不是Linux系统的库,所以在编译的时候要加上-lpthread,否则编译通不过。

二:创建文件thread11.c,利用pthread库创建一个新线程,并打印出主线程和新线程的线程ID和进程ID

pthread_self函数的作用是获取线程ID,其作用对应进程中 getpid() 函数。验证thread11.c

程序

#include <stdio.h>

#include <pthread.h>

#include <unistd.h>

void *create(void *arg){

        printf("new thread...\n");

        printf("thread_tid == %u\n",(unsigned int )pthread_self());

        printf("thread_pid is %d\n",getpid());

        return (void *)0;

}

int main(int arg,char *argv[]){

        pthread_t tid;

        int error;

        printf("Main thread is starting...\n");

        error = pthread_create(&tid,NULL,create,NULL);

        if(error!=0){

                printf("thread is not created...");

                return -1;

        }

        printf("main pid is %d\n ",getpid());

        sleep(1);

        return 0;

}

三:创建文件thread3.c,利用pthread库创建一个新线程,并使用全局变量var来演示线程之间的数据共享和竞争条件。

#include <stdio.h>

#include <pthread.h>

#include <stdlib.h>

#include <unistd.h>

 int var = 100;

 void *tfn(void *arg)

{

       var = 200;

       printf("thread\n");

      return NULL;

}

 int main(void)

{

       printf("At first var = %d\n", var);

      pthread_t tid;

       pthread_create(&tid, NULL, tfn, NULL);

       sleep(1);

      printf("after pthread_create, var = %d\n", var);

      return 0;

}

四:创建文件thread4.c,利用pthread库创建一个新线程,并使用一个全局字符串 message 来演示线程之间的数据共享和修改。

pthread_join的用法,验证如下程序thread4.c

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

void *thread_function(void *arg);

char message[] = "Hello World";

int main()

{

int res;

pthread_t a_thread;

void *thread_result;

res = pthread_create(&a_thread, NULL, thread_function, (void *)message);

if (res != 0)

{

perror("Thread creation failed!");

exit(EXIT_FAILURE);

}

printf("Waiting for thread to finish...\n");

res = pthread_join(a_thread, &thread_result);

if (res != 0)

{

perror("Thread join failed!/n");

exit(EXIT_FAILURE);

}

printf("Thread joined, it returned %s\n", (char *)thread_result);

printf("Message is now %s\n", message);

exit(EXIT_FAILURE);

}

void *thread_function(void *arg)

{

printf("thread_function is running. Argument was %s\n", (char *)arg);

sleep(3);

strcpy(message, "Bye!");

pthread_exit("Thank you for your CPU time!");

}

五:创建文件thread5.c,利用pthread库创建两个新线程,并让它们分别打印出传递给它们的字符串。

更多的pthread_join的例子,预测它的运行结果thread5.c。

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

void *print_message_function( void *ptr );

main()

{

     pthread_t thread1, thread2;

     char *message1 = "Thread 1";

     char *message2 = "Thread 2";

     int  iret1, iret2;

    /* Create independent threads each of which will execute function */

     iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);

     iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) message2);

     /* Wait till threads are complete before main continues. Unless we  */

     /* wait we run the risk of executing an exit which will terminate   */

     /* the process and all threads before the threads have completed.   */

     pthread_join( thread1, NULL);

     pthread_join( thread2, NULL);

     printf("Thread 1 returns: %d\n",iret1);

     printf("Thread 2 returns: %d\n",iret2);

     exit(0);

}

void *print_message_function( void *ptr )

{

     char *message;

     message = (char *) ptr;

     printf("%s \n", message);

}

六创建文件thread6.c,创建三个线程 A、B 和 C,并设置了一个全局的互斥锁和条件变量。每个线程在打印之前都会等待条件变量,主线程负责按顺序通知这些条件变量,从而控制打印顺序为ABCABC……。

编程thread6.c,创建3个线程。假如三个线程的ID分别为A,B,C。每个线程把自己的ID在屏幕上打印10遍,要求输出结果必须按照ABC的顺序显示,例如ABCABC…..。

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <pthread.h>

#define NUM_THREADS 3

pthread_mutex_t mutex;

pthread_cond_t cond;

int current_thread = 0;

void *thread_function(void *arg) {

    char *thread_id = (char *)arg;

    int i;

    for (i = 0; i < 10; i++) {

        pthread_mutex_lock(&mutex);

        while (current_thread != atoi(thread_id)) {

            pthread_cond_wait(&cond, &mutex);

        }

        printf("%s", thread_id);

        fflush(stdout);

        current_thread = (current_thread + 1) % NUM_THREADS;

        pthread_cond_broadcast(&cond);

        pthread_mutex_unlock(&mutex);

    }

    return NULL;

}

int main() {

    pthread_t threads[NUM_THREADS];

    char *thread_ids[] = {"0", "1", "2"};

    int i;

    pthread_mutex_init(&mutex, NULL);

    pthread_cond_init(&cond, NULL);

    for (i = 0; i < NUM_THREADS; i++) {

        pthread_create(&threads[i], NULL, thread_function, thread_ids[i]);

    }

    for (i = 0; i < NUM_THREADS; i++) {

        pthread_join(threads[i], NULL);

    }

    pthread_mutex_destroy(&mutex);

    pthread_cond_destroy(&cond);

    return 0;

}

3、实验结果分析:

一:分别以普通用户和 root 身份登录,看能否建立新用户账号。

以普通用户身份登录:

普通用户通常没有权限直接添加新用户账号到系统。尝试使用如 useradd 或 adduser 命令时,会收到权限错误,例如:

useradd newuser

-bash: useradd: 权限不够

如果尝试使用 sudo 命令(前提是普通用户已被赋予执行特定命令的sudo权限),则可能成功创建新用户账号:

sudo useradd newuser

以 root 用户身份登录:

作为 root 用户,你拥有系统中最高的权限,可以执行任何操作,包括添加新用户账号。使用 useradd 或 adduser 命令将不会有权限问题:

useradd newuser

你还可以设置密码,为新用户配置其他选项等。

二:为新用户(如 Zhang)建立账号和工作组,并进行相应配置。以该用户身份登录,修改密码等。最后删除该用户。

编写shell脚本,批量创建和删除用户。编写一个shell脚本myadduser.sh,完成下列功能,创建30个用户stu001,stu002,..,stu030,初始密码设置分别为001,002,…,030。

编写一个批量删除用户的脚本mydeluser.sh,完成以下功能,把用户名stu001,…,stu020的这20个用户删除。

三:在U盘上建立文件系统,并进行安装。

四:配置网络信息,浏览校园网信息。

五:配置mail环境,发送和接收邮件。

sudo apt install evolution

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值