当前位置: 首页 > news >正文

Linux操作系统分析实验-多线程与内核模块编程,实验一

实验一  多线程与内核模块编程

一、实验目的

1、理解Linux下进程的结构;

2、理解Linux下产生新进程的方法(系统调用—fork函数);

3、掌握如何启动另一程序的执行;

4、理解Linux下线程的结构;

5、理解Linux下产生新线程的方法;

6、理解Linux系统下多进程与多线程的区别

7、了解什么是管道

8、熟悉UNIX/LINUX支持的管道通信方式

9、理解内核模块的编写和装载方法

二、实验内容

1、利用fork函数创建新进程,并根据fork函数的返回值,判断自己是处于父进程还是子进程中;

2、在新创建的子进程中,使用exec类的函数启动另一程序的执行;分析多进程时系统的运行状态和输出结果;

3、利用最常用的三个函数pthread_create,pthread_join和pthread_exit编写了一个最简单的多线程程序。理解多线程的运行和输出情况;

4、利用信号量机制控制多线程的运行顺序,并实现多线程中数据的共享;

5、分析Linux系统下多进程与多线程中的区别。

6、编写一个HelloWorld内核模块,并进行装载和卸载操作。

三、实验原理

1、Linux系统下多进程编程:

(1)理解Linux下进程的结构:

Linux系统中一个进程在用户内存里有三部份的数据,就是“数据段”,“堆栈段”和“代码段”,一般的CPU, 如I386,都有上述三种段寄存器,以方便操作系统的运行。“代码段” 就是存放了程序代码的数据,堆栈段存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。而数据段则存放程序的全局变量,常数以及动态数据分配的数据空间(比如用malloc之类的函数取得的空间)。系统如果同时运行数个相同的程序,它们之间就不能使用同一个堆栈段和数据段。

(2)利用fork()函数创建进程和利用exec类的函数来启动进程的执行

     一个程序调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。

    一个进程一旦调用exec类函数,它本身就“死亡”了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。

这样就实现了多进程编程和运行了。

2、Linux系统下多线程编程

(1)为什么使用多线程?

      使用多线程的理由之一是和进程相比,它是一种非常“节俭”的多任务操作方式。在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种“昂贵”的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右。

     使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

多线程程序作为一种多任务、并发的工作方式,还有以下的优点:

1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。

2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。

3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

(2)多线程编程

    Linux系统下的多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。

利用函数pthread_create来创建一个线程,利用函数pthread_join来等待一个线程的结束。多线程编程实例见实验指导书。

3、管道通信

所谓管道,是指能够连接一个写进程和一个读进程的、并允许它们以生产者—消费者方式进行通信的一个共享文件,又称为pipe文件。由写进程从管道的写入端(句柄1)将数据写入管道,而读进程则从管道的读出端(句柄0)读出数据。

(1)有名管道

一个可以在文件系统中长期存在的、具有路径名的文件。用系统调用mknod( )建立。它克服无名管道使用上的局限性,可让更多的进程也能利用管道进行通信。因而其它进程可以知道它的存在,并能利用路径名来访问该文件。对有名管道的访问方式与访问其他文件一样,需先用open( )打开。

(2)无名管道

一个临时文件。利用pipe( )建立起来的无名文件(无路径名)。只用该系统调用所返回的文件描述符来标识该文件,故只有调用pipe( )的进程及其子孙进程才能识别此文件描述符,才能利用该文件(管道)进行通信。当这些进程不再使用此管道时,核心收回其索引结点。

二种管道的读写方式是相同的,本实验只用无名管道。

(3)pipe文件的建立

分配磁盘和内存索引结点、为读进程分配文件表项、为写进程分配文件表项、分配用户文件描述符

(4)读/写进程互斥

内核为地址设置一个读指针和一个写指针,按先进先出顺序读、写。

为使读、写进程互斥地访问pipe文件,需使各进程互斥地访问pipe文件索引结点中的直接地址项。因此,每次进程在访问pipe文件前,都需检查该索引文件是否已被上锁。若是,进程便睡眠等待,否则,将其上锁,进行读/写。操作结束后解锁,并唤醒因该索引结点上锁而睡眠的进程。

四、实验步骤

   要求写出实验过程和思路(用文字表述,或画流程图,或写出伪代码都可以)。

1、Linux中的fork()函数是用于创建一个新进程的系统调用。父进程和子进程的区别在于fork()函数的返回值不同,父进程中fork()函数返回子进程的进程ID,而子进程中fork()函数返回0。因此,我们可以通过判断fork()函数的返回值来确定当前进程是父进程还是子进程。

伪代码:

初始化进程ID变量 pid 和 pid2,以及返回值变量 retpid

获取当前进程ID并赋值给 pid

输出 "before fork:pid=" 并连接 pid 的值

执行 fork 操作,返回值赋值给 retpid

获取当前进程ID并赋值给 pid2

输出 "after fork:pid=" 并连接 pid2 的值

If  pid 等于 pid2,则:

输出 "this is father print=" 并连接 retpid 的值

else: 输出 "this is child print,=" 连接 retpid 的值,后跟 ",", 并连接 getpid() 的值

Return 0

Fork()函数返回值是负数,说明子进程创建失败;返回值是0,则说明处于子进程中;返回值是子进程的进程号,则说明处于父进程中。

伪代码:

* int main()

* int pid = fork()

* 返回值

* -1: 子进程创建失败

* 0: 处于子进程中

* >0: 处于父进程中,返回值是子进程进程号

* switch (pid)

* case -1:

* printf("fork fail!\n")

* exit(1)

* case 0:

* execl("/bin/ls", "ls", "-1", "-color", NULL)

* 子进程用exec()装入命令ls

* exec()后,子进程的代码被ls的代码取代

* printf("exec fail!\n")

* exit(1)

* default:

* wait(NULL)

* 父进程等待子进程结束后才执行

* printf("ls completed !\n")

* exit(0)

2、在新创建的子进程中,使用exec类的函数启动另一程序的执行;分析多进程时系统的运行状态和输出结果;

3、利用最常用的三个函数pthread_create,pthread_join和pthread_exit编写了一个最简单的多线程程序。理解多线程的运行和输出情况;

调用函数pthread_create来创建三个线程(在创建一个线程后,可以调用pthread_exit函数使当前线程终止并返回一个值,该值可由函数pthread_join()获取),如果函数返回值不为0,则说明创建线程失败,直接退出程序。调用函数pthread_join来等待所有线程结束,函数返回值不为0,则说明还有线程没有退出;打印相应的信息,退出程序。

理解多线程的运行和输出情况;

(1)运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。

(2)线程间方便的通信机制。由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。

伪代码:

主程序:

    定义线程标识符 id

    创建线程,指定执行函数为 thread

    如果创建线程失败:

        打印错误消息

        退出程序

    循环3次:

        打印主进程信息

    等待线程结束,获取线程返回值

    打印线程返回的值

线程函数 thread:

    参数:无

    循环3次:

        打印线程信息

        结束线程并返回值8

4、利用信号量机制控制多线程的运行顺序,并实现多线程中数据的共享;

伪代码:

主程序:

    初始化信号量 sem 为 0

    创建线程 t1 执行 HandleData1

    创建线程 t2 执行 HandleData2

    创建线程 t3 执行 ReadData1

    创建线程 t4 执行 ReadData2

    等待线程 t1 结束

ReadData1:

    打开文件 1.dat

    当没有到达文件末尾:

        读取一对整数到缓冲区 stack

        信号量 sem 加一

        缓冲区大小 size 加一

    关闭文件

ReadData2:

    打开文件 2.dat

    当没有到达文件末尾:

        读取一对整数到缓冲区 stack

        信号量 sem 加一

        缓冲区大小 size 加一

    关闭文件

HandleData1:

    无限循环:

        等待信号量 sem

        从缓冲区 stack 读取数据

        执行加法运算并打印结果

        缓冲区大小 size 减一

HandleData2:

    无限循环:

        等待信号量 sem

        从缓冲区 stack 读取数据

        执行乘法运算并打印结果

        缓冲区大小 size 减一

5、编写一个HelloWorld内核模块,并进行装载和卸载操作。

编写内核模块代码

编写初始化函数init_module或者使用宏module_init来声明。这个函数将在模块装载时执行。

编写清理函数cleanup_module或者使用宏module_exit来声明。这个函数将在模块卸载时执行。

在初始化函数中,使用printk函数打印“Hello World”信息到内核日志。

在清理函数中,同样使用printk打印一个信息表示模块被卸载。

编写Makefile

创建一个Makefile来编译内核模块。

使用内核构建系统的make命令来编译模块。

编译内核模块

运行make命令来编译你的内核模块。

装载内核模块

使用insmod命令装载编译好的内核模块。

验证模块是否装载成功

使用lsmod命令来检查模块是否在列表中。

使用dmesg命令来检查内核日志,查看“Hello World”信息是否被打印。

卸载内核模块

使用rmmod命令卸载你的内核模块。

验证模块是否卸载成功

再次使用lsmod检查模块是否从列表中移除。

使用dmesg检查内核日志,确认卸载信息被打印。

五、实验数据及源代码

学生必须提交程序源代码,并有注释

1、利用fork函数创建新进程,并根据fork函数的返回值,判断自己是处于父进程还是子进程中;

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{pid_t pid;pid_t pid2;pid_t retpid;pid = getpid();printf("before fork:pid=%d\n", pid);retpid = fork();pid2 = getpid();printf("after fork:pid=%d\n", pid2);//子进程返回值是0,父进程返回值是子进程pid号if (pid == pid2) {printf("this is father print%d\n", retpid);}else {printf("this is child print,%d,pid=%d\n", retpid, getpid());}return 0;
}

2、在新创建的子进程中,使用exec类的函数启动另一程序的执行;分析多进程时系统的运行状态和输出结果;

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/wait.h>int main()
{int pid = fork();  //返回值:负数,子进程创建失败;返回值:0,则处于子进程中;返回值是子进程的进程号,则处于父进程中。switch (pid) {case  -1:printf("fork fail!\n"); exit(1); case  0: //子进程在执行execl("/bin/ls", "ls", "-1", "-color", NULL);  //子进程用exec( )装入命令ls ,exec( )后,子进程的代码被ls的代码取代,这时子进程的PC指向ls的第1条语句,开始执行ls的命令代码。printf("exec fail!\n");exit(1); default: //父进程在执行wait(NULL); //父进程等待子进程结束后才执行printf("ls completed !\n");exit(0);}
}

3、利用最常用的三个函数pthread_create,pthread_join和pthread_exit编写了一个最简单的多线程程序。理解多线程的运行和输出情况;

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void thread(void* arg) {for (int i = 0; i < 3; i++) {printf("This is a pthread %d.\n", i + 1);pthread_exit((void*)8); //使线程终止,线程结束会返回一个值,该值可由函数pthread_join()获取}
}
int main(void* arg) {pthread_t id; //线程标识号int ret;ret = pthread_create(&id, NULL, (void*)thread, NULL); //创建一个线程,并使得该线程执行thread函数for (int i = 0; i < 3; i++)printf("This is the main process %d.\n", i + 1);if (ret != 0) {printf("Create pthread error!\n"); //创建线程失败exit(1); //退出程序}void* temp;ret = pthread_join(id, &temp); //用来等待一个线程结束,直到线程退出后才执行下面的代码。if (ret) {printf("The pthread is not exit.\n");return -1;}printf("The pthread exits and returns a value %d.\n", (int)temp);return (0);
}

4、利用信号量机制控制多线程的运行顺序,并实现多线程中数据的共享;

两个线程负责从文件读取数据到公共的缓冲区,另外两个线程从缓冲区读取数据作不同的处理(加和乘运算)。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
#define MAXSTACK 100
int stack[MAXSTACK][2];
int size = 0;
sem_t sem; ////从文件1.dat读取数据,每读一次,信号量加一
void ReadData1(void* arg) {FILE* fp = fopen("1.dat", "r"); //以读打开文件1.datwhile (!feof(fp)) { //函数feof:若遍历到文件结束符则返回true,否则返回falsefscanf(fp, "%d %d", &stack[size][10], &stack[size][1]);sem_post(&sem); //增加信号量sem的值size++; //每读一次,信号量加一}fclose(fp); //关闭
}//从文件2.dat读取数据
void ReadData2(void* arg) {FILE* fp = fopen("2.dat", "r"); //以读的方式打开文件2.datwhile (!feof(fp)) { //函数feof:若遍历到文件结束符则返回true,否则返回falsefscanf(fp, "%d %d", &stack[size][0], &stack[size][1]);sem_post(&sem); //增加信号量sem的值size++; //每读一次,信号量加一}fclose(fp); //关闭文件
}//阻塞等待缓冲区有数据,读取数据后,释放缓冲区空间,继续等待
void HandleData1(void* arg) {while (1) {sem_wait(&sem); //用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一printf("Plus:%d+%d=%d\n", stack[size][0], stack[size][1], stack[size][0] + stack[size][1]);size--; //信号量减一}
}//阻塞等待缓冲区有数据,读取数据后,释放缓冲区空间,继续等待
void HandleData2(void* arg) {while (1) {sem_wait(&sem); //用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一printf("Multiply:%d*%d=%d\n", stack[size][0], stack[size][1], stack[size][0] * stack[size][1]);size--; //信号量减一}
}int main(void* arg) {pthread_t t1, t2, t3, t4;sem_init(&sem, 0, 0); //初始化信号量sem,第二个参数0表示此信号量只能为当前的所有线程共享,若不为0,则在进程间共享;第三个参数0表示信号量的初始值pthread_create(&t1, NULL, (void*)HandleData1, NULL); //创建线程1pthread_create(&t2, NULL, (void*)HandleData2, NULL); //线程2pthread_create(&t3, NULL, (void*)ReadData1, NULL); //线程3pthread_create(&t4, NULL, (void*)ReadData2, NULL); //线程4//防止程序过早退出,等其它线程结束后,在退出程序pthread_join(t1, NULL); //用来等待一个线程的结束
}

5、编写一个HelloWorld内核模块,并进行装载和卸载操作。

   Hello World.c

#include <linux/init.h>	 //包含了宏__init和__exit
#include <linux/kernel.h> //包含了常用的内核函数
#include <linux/module.h> //包含了对模块的版本控制static int __init lkp_init(void) //模块加载函数,当模块被插入到内核时调用它
{printk("<0>" "Hello World from the kernel space...\n"); //模块加载的时候系统会打印return 0;
}static void __exit lkp_cleanup(void) //模块卸载函数,当模块从内核移走时调用它
{printk("<0>" "Good Bye World! leaving kernel space...\n"); //模块卸载的时候系统会打印
}module_init(lkp_init); //模块初始化
module_exit(lkp_cleanup); //模块退出MODULE_LICENSE("GPL");	//模块具有GUN公共许可证
MODULE_AUTHOR("作者");
MODULE_DESCRIPTION("功能描述");

Makefile

obj - m: = HelloWorld.o
CURRENT_PATH : = $(shell pwd)
LINUX_KERNEL : = $(shell uname - r)
LINUX_KERNEL_PATH : = usr / src / linux - headers - $(LINUX_KERNEL)all :make - C $(LINUX_KERNEL_PATH) M = $(CURRENT_PATH) modulesclean :
make - C $(LINUX_KERNEL_PATH) M = $(CURRENT_PATH) cleanobj - m : = HelloWorld.o
PWD : = $(shell pwd)
KVER : = $(shell uname - r)
KDIR : = / lib / modules / $(KVER) / build /all :$(MAKE) - C $(KDIR) M = $(PWD)clean :rm - rf * .o * .mod.c * .mod.o * .ko * .symvers * .order * .a

六、实验结果分析(截屏的实验结果,与实验结果对应的实验分析)

1、实验结果与实验程序、实验步骤、实验原理的对应分析;

2、实验过程中的问题及原因和解决办法。

1、利用fork函数创建新进程,并根据fork函数的返回值,判断自己是处于父进程还是子进程中;

子进程返回值是0,父进程返回值是子进程pid号

2、在新创建的子进程中,使用exec类的函数启动另一程序的执行;

当子进程在执行时,子进程用exec()装入命令ls,exec()后,子进程的代码被ls的代码取代,这时子进程的PC指向ls的第1条语句,开始执行ls的命令代码。

当父进程在执行,父进程等待子进程结束后才执行。

分析多进程时系统的运行状态和输出结果:

第一行 :

top – :系统当前时间

up:服务器连续运行的时间,笔者见过有服务器连续运行一年以上,linux服务器还是非常稳定的。

user:当前有多少用户登录系统

load average:这个边有3个数值分别表示系统在前1分钟,5分钟,15分钟的工作负载

第二行就是显示任务的数量情况,其中zombie表示僵尸进程

第三行表示cpu的运行情况,按下1可以显示每个核的运行情况。

第四行表示内存memory的使用情况。

第五行表示交换空间swap的使用情况。

3、利用最常用的三个函数pthread_create,pthread_join和pthread_exit编写了一个最简单的多线程程序。理解多线程的运行和输出情况;

4、利用信号量机制控制多线程的运行顺序,并实现多线程中数据的共享;

5、分析Linux系统下多进程与多线程中的区别。

对比

多进程

多线程

独立性/共享性

每个进程拥有自己独立的地址空间,一个进程的崩溃不会直接影响到其他进程。但这也意味着进程间通信(IPC)需要特殊的机制,如管道、消息队列、共享内存等。

同一进程内的所有线程共享进程的地址空间,包括数据段、代码段和打开的文件等。这使得线程间通信变得简单和快速。

资源占用

每个进程有自己的一套资源,包括文件描述符、环境变量等。相对于线程,进程的创建、管理和上下文切换的开销较大。

线程比进程更轻量级,它们共享大部分进程资源,线程的创建、管理和上下文切换的开销远小于进程。

创建方式

在Linux中,新进程通常是通过`fork()`系统调用创建的,该调用会复制父进程的地址空间,创建几乎完全独立的子进程。

线程通常是通过专门的线程库(如POSIX线程库 - pthreads)创建的。

内存管理

进程有独立的内存空间,所以内存管理相对简单,但是资源利用率低于多线程。

由于共享内存空间,线程间共享数据非常方便,但是也容易发生同步和数据一致性的问题。

安全性

由于进程间内存是隔离的,因此一个进程的内存错误不会影响到其他进程。

线程间的错误和内存泄露可能会影响同一进程内的其他线程,因此需要更加注意同步和互斥机制。

区别总结:

隔离性:多进程之间相互隔离;多线程之间共享进程资源。

资源需求:多进程比多线程占用更多的资源。

通信:多线程之间的通信通常比多进程之间的通信更高效。

创建和管理开销:创建线程的开销小于创建进程。

故障容忍:进程间不会因为其他进程崩溃而受影响,而线程间可能会相互影响。

开发和维护难度:多线程程序的开发和维护通常比多进程更加复杂,特别是在需要处理同步和共享数据的问题时。

在选择使用多进程还是多线程时,通常需要根据应用程序的特性和需求来决定。如果需要大量的独立任务并且希望隔离故障,可能会选择多进程;如果任务之间需要频繁交换信息或者状态,并且对性能有较高的要求,则多线程可能是更好的选择。

6、编写一个HelloWorld内核模块,并进行装载和卸载操作。

总结:

内核模块至少需要两个基本函数:一个是在模块装载时调用的初始化函数,另一个是在模块卸载时调用的清理函数。通过`module_init`和`module_exit`宏可以分别指定这些函数。使用`printk`函数可以将信息输出到内核日志中,这在调试内核模块时非常有用。Makefile的编写需要遵循内核模块编译的特定格式,正确指向内核源代码树的位置。`insmod`和`rmmod`是装载和卸载模块的关键命令,必须具备相应权限(通常是root权限)才能执行这些操作。`lsmod`命令用于验证模块是否已装载,而`dmesg`命令则用于检查模块的输出信息,这是验证模块正确性的重要步骤。

实验中遇到的问题以及解决:

在完成第三个实验内容时编译时发现了程序代码中存在一些问题,主要是关于线程的处理方式。

经过逐一分析:

错误点:线程函数中的 pthread_exit 的位置

在线程函数 thread 中,pthread_exit 被放置在了循环内部。这意味着在第一次循环结束时,线程就会退出,导致循环不会完成其余的迭代。

解决方法:将 pthread_exit 移到循环之外。

线程函数的参数和返回类型

线程函数的正确签名应该是 void* function_name(void *arg)。原本的线程函数 thread 使用了 void thread(void* arg) 形式,这可能导致在某些编译器或设置下出现问题。

解决方法:更改线程函数的签名为 void* thread(void* arg)。

七、思考题

1、多进程并发执行时各个进程的内存分配情况如何?如何监测其分配情况?

在多进程并发执行时,每个进程都有自己的独立的内存空间。操作系统为每个进程分配内存,并且管理这些内存。每个进程的内存空间包括代码段、数据段、堆和栈等部分。

在Unix/Linux系统中,可以使用top, htop, ps等工具查看进程的内存使用情况。还可以使用valgrind等工具进行内存泄漏检测。

2、多线程中数据是如何共享的?

在多线程中,同一个进程内的所有线程共享该进程的内存空间。这意味着线程之间可以共享全局变量、静态变量、堆上的数据等。但是,线程间的数据共享需要谨慎处理,因为多个线程可能同时访问和修改同一块内存,导致数据不一致或其他并发问题。为了解决这个问题,可以使用互斥锁、条件变量、读写锁等同步机制来确保数据的一致性和线程的安全性。

3、Linux系统下多进程与多线程中的区别是什么?

进程:进程是操作系统分配资源的最小单位。每个进程有自己的内存空间和系统资源。进程间的通信(IPC)需要通过特殊的机制(如管道、消息队列、共享内存等)。

线程:线程是操作系统调度的最小单位。同一个进程的线程共享该进程的内存空间和系统资源。线程间的通信更加简单,可以直接读写共享内存。

4、程序中的sleep(5)起什么作用?

sleep()是一个系统调用,它使调用它的进程暂停执行指定的时间,单位是秒。sleep(5)将使进程暂停执行5秒。这常常用于延迟执行、等待其他进程或简单地暂停一段时间。

5、子进程1和2为什么也能对管道进行操作?

当使用管道进行进程间通信时,通常创建一个管道,然后使用fork()创建子进程。这样子进程会继承父进程的管道文件描述符,因此子进程1和2都能够对管道进行操作。这是因为管道文件描述符在fork()之后被子进程继承,所以子进程可以读写管道,实现进程间通信。

6、在Linux内核源程序中,经常利用内核模块实现的功能有哪些?

设备驱动:为特定的硬件设备提供驱动支持。

文件系统:支持不同的文件系统,如ext4, NTFS, FAT等。

网络协议:实现新的或扩展现有的网络协议。

系统调用:添加新的系统调用来扩展操作系统的功能。

这些只是一些例子,实际上内核模块可以用于实现许多内核相关的功能,而不必修改内核的核心代码。这提供了很大的灵活性和扩展性。

谢谢大家的支持,码字不易,请随手点个赞叭(^-^)

相关文章:

Linux操作系统分析实验-多线程与内核模块编程,实验一

实验一 多线程与内核模块编程 一、实验目的 1、理解Linux下进程的结构&#xff1b; 2、理解Linux下产生新进程的方法&#xff08;系统调用—fork函数&#xff09;&#xff1b; 3、掌握如何启动另一程序的执行&#xff1b; 4、理解Linux下线程的结构&#xff1b; 5、理解…...

AI绘画Stable Diffusion人物背景替换实操教程,让创意无限延伸

大家好&#xff0c;我是灵魂画师向阳 Stable Diffusion以其强大的能力可以实现人物背景的更换。本文将带你深入了解如何利用Stable Diffusion中的Inpaint Anything插件快速且精准地实现人物背景的替换&#xff0c;从而让你的图片焕发新生。 前期准备 本文会使用到Inpaint An…...

linux环境oracle11.2.0.4打补丁(p31537677_112040_Linux-x86-64.zip)

上传补丁及opatch工具 创建目录并上传opatch工具和补丁包 百度网盘地址 链接: https://pan.baidu.com/s/1EA5oerm52aesZUnb5OnQsA 提取码: ib8f [oraclerhel64 ~]$ mkdir /u01/psu [oraclerhel64 ~]$ cd /u01/psu [oraclerhel64 psu]$ ll total 514572 -rw-r--r-- 1 oracle o…...

华为 HCIP-Datacom H12-821 题库 (29)

&#x1f423;博客最下方微信公众号回复题库,领取题库和教学资源 &#x1f424;诚挚欢迎IT交流有兴趣的公众号回复交流群 &#x1f998;公众号会持续更新网络小知识&#x1f63c; 1.运行 IS-IS 的两台路由器&#xff0c;只有 IIH 发送的时间间隔相同才可以建立邻接关系 A、正…...

MySQL 为什么一定要有一个主键

引言 在数据库设计中&#xff0c;主键&#xff08;Primary Key&#xff09;是一个至关重要的概念。MySQL 作为最广泛使用的关系型数据库之一&#xff0c;主键在 MySQL 表设计中扮演着关键角色。主键不仅决定了表中数据的唯一性和完整性&#xff0c;还对数据库性能、数据查询、…...

Pycharm 本地搭建 stable-diffusion-webui

一、下载工程源码 Github官方连接 https://github.com/AUTOMATIC1111/stable-diffusion-webui 二、Pycharm打开工程 1、设置环境 文件-设置-项目-Python解析器-添加解释器-添加本地解释器 Conda环境-创造新环境-Python版本3.10 注意一定要选择Python3.10版本&#xff0c;否…...

python/爬虫技术/lxml工具介绍/XML和HTML解析

1.lxml介绍&#xff1a; lxml 是一个Python库&#xff0c;它提供了非常强大的XML和HTML解析功能。它基于libxml2和libxslt&#xff0c;是处理XML和HTML文档的首选库之一。 2.安装 首先&#xff0c;需要安装lxml库。可以通过pip来安装&#xff0c;在控制台内执行安装命令。 p…...

Kafka技术详解[5]: 集群启动

目录 集群启动 相关概念 代理&#xff1a;Broker 控制器&#xff1a;Controller 启动ZooKeeper 启动Kafka 初始化ZooKeeper 初始化服务 启动任务调度器 创建数据管理器 创建远程数据管理器 创建副本管理器 创建ZK元数据缓存 创建Broker通信对象 创建网络通信对象 注册Brok…...

记一次教学版内网渗透流程

信息收集 如果觉得文章写的不错可以共同交流 http://aertyxqdp1.target.yijinglab.com/dirsearch dirsearch -u "http://aertyxqdp1.target.yijinglab.com/"发现 http://aertyxqdp1.target.yijinglab.com/joomla/http://aertyxqdp1.target.yijinglab.com/phpMyA…...

[Python学习日记-31] Python 中的函数

[Python学习日记-31] Python 中的函数 简介 语法定义 函数的参数 简介 引子&#xff1a; 你是某公司的一个高级程序员&#xff0c;现在老板让你写一个监控程序&#xff0c;需要24小时全年无休的监控公司网站服务器的系统状况&#xff0c;当 CPU、Memory、Disk 等指标的使用…...

Kafak入门技术详解

抱歉&#xff0c;没有太多的时间进行详细校对 目录 一、Kafka简介 1.消息队列 1.1为什么需要消息队列 1.2消息队列 1.3消息队列的分类 1.4P2P和发布订阅MQ的比较 1.5消息系统的使用场景 1.6常见的消息系统 2.Kafka简介 2.1简介 2.2设计目标 2.3 kafka核心的概念 二…...

X-Spreadsheet:Web端Excel电子表格工具库

在数字化时代&#xff0c;数据管理与分析的重要性日益凸显。传统的电子表格软件如Microsoft Excel和Google Sheets在数据处理方面发挥着重要作用&#xff0c;但在Web端&#xff0c;一款名为X-Spreadsheet的工具库正以其独特的优势逐渐崭露头角。本文将详细介绍X-Spreadsheet&am…...

为什么很多APP取消网页版

厂商为了增加用户黏度把所有的内容都放在 APP 上&#xff0c;京东的网页也搜索不到东西了&#xff0c;就算看到东西要跳转过来还需要先登录一下。 对比亚马逊这类的其他的购物网站&#xff0c;基本上都是网页内容和 APP 的内容都是同步的&#xff0c;网页直接看也可以下单&…...

Kubernetes高级功能

资源配额 什么是资源配额 资源配额&#xff0c;通过 ResourceQuota 对象来定义&#xff0c;对每个命名空间的资源消耗总量提供限制。 它可以限制命名空间中某种类型的对象的总数目上限&#xff0c;也可以限制命名空间中的 Pod 可以使用的计算资源的总上限。 资源配额应用 创建的…...

(作业)第三期书生·浦语大模型实战营(十一卷王场)--书生入门岛通关第1关Linux 基础知识

关卡任务 闯关任务需要在关键步骤中截图&#xff1a; 任务描述 完成所需时间 闯关任务 完成SSH连接与端口映射并运行hello_world.py 10min 可选任务 1 将Linux基础命令在开发机上完成一遍 10min 可选任务 2 使用 VSCODE 远程连接开发机并创建一个conda环境 10min 可选任务 3 创…...

【python爬取网页信息并存储】

爬取网页信息并存储是一个常见的任务&#xff0c;通常涉及以下几个步骤&#xff1a; 发送HTTP请求&#xff1a;使用库如requests来发送HTTP请求获取网页内容。解析网页内容&#xff1a;使用库如BeautifulSoup或lxml来解析HTML内容&#xff0c;提取所需信息。存储数据&#xff…...

TCP、UDP

TCP和UDP的区别 是否面向连接&#xff1a;UDP 在传送数据之前不需要先建立连接。而 TCP 提供面向连接的服务&#xff0c;在传送数据之前必须先建立连接&#xff0c;数据传送结束后要释放连接。 是否是可靠传输&#xff1a;远地主机在收到 UDP 报文后&#xff0c;不需要给出任…...

聊聊暖通空调系统的优化控制方法

目录 暖通空调系统的优化控制方法✈️part1 初版回归网络建模✈️part2 更新的回归网络✈️ 聊聊暖通空调系统的优化控制方法 这篇文章简单分享一下暖通空调&#xff08;HVAC&#xff09;领域常常提到的”优化控制“这一概念指的是什么&#xff0c;它控制的是哪些参数&#…...

2024年合肥市职业院校技能大赛(中职组)赛 网络安任务书样题

2024年合肥市职业院校技能大赛--中职组赛 网络安任务书样题 一、竞赛项目简介&#xff1a;二、竞赛注意事项模块A: 理论技能与职业素养模块B: 网络安全事件响应、数字取证调查和应用安全任务一:应急响应任务二:操作系统取证任务三:网络数据包分析任务四:代码审计 模块C:CTF 夺旗…...

制造企业如何提升项目管理效率?惠科股份选择奥博思PowerProject项目管理系统

全球知名的显示方案综合服务商 - 惠科股份有限公司与北京奥博思达成合作&#xff0c;基于奥博思 PowerProject 搭建企业级项目管理平台。满足惠科多产品多业务领域的项目全周期管理。助力企业在技术研发、产品创新等方面继续取得行业领先优势。 同时&#xff0c;PowerProject …...

mTLS(Mutual TLS)即双向传输层安全,是一种安全通信协议,用于在客户端和服务器之间建立双向的身份验证和加密通道。

mTLS&#xff08;Mutual TLS&#xff09;即双向传输层安全&#xff0c;是一种安全通信协议&#xff0c;用于在客户端和服务器之间建立双向的身份验证和加密通道。在传统的TLS&#xff08;Transport Layer Security&#xff09;中&#xff0c;客户端通常只会验证服务器的身份&am…...

HUAWEI WATCH GT 系列安装第三方应用

文章目录 适用机型概述官方文档从源码构建 hap 文件和对源码签名下载和安装DevEco Studio下载和安装首次启动推荐&#xff1a;设置IDE推荐的兼容版本环境&#xff08;可选&#xff09;安装并启用中文菜单插件 使用DevEco Studio打开项目并进行构建构建问题解决一、生成密钥和证…...

Html jquery下拉select美化插件——selectFilter.js

1. Html jquery下拉select美化插件——selectFilter.js jQuery是一个广泛使用的JavaScript库&#xff0c;它简化了DOM操作、事件处理、动画以及Ajax交互&#xff0c;使得开发者能更高效地构建交互式网页。在本案例中&#xff0c;jquery.selectlist.js插件正是基于jQuery构建的&…...

使用ESP8266扫描WiFi列表

一、简介 准备用基于esp8266的nodemcu开发板做一个天气时钟。目前只实现了第一阶段任务的第一点要求。使用arduino编程&#xff0c;在基于esp8266的nodemcu开发板上实现开机自动连接wifi。 这里记录一下使用ESP8266扫描WiFi列表的方法。还需要研究怎么把列表显示在网页上&…...

Java对象访问机制:句柄访问与直接指针访问

在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;对象的访问方式是一个关键的设计选择&#xff0c;它影响着程序的性能和内存管理。JVM规范中只规定了对象引用&#xff08;reference&#xff09;必须指向对象&#xff0c;但并没有定义这个引用应该如何定位和访问堆中对象…...

基于SpringBoot实现QQ邮箱发送短信功能 | 免费短信服务

开发学习过程中有个短信发送功能&#xff0c;阿里云腾讯云等等都要money&#xff0c;听说qq邮箱可以实现免费发送邮箱的功能&#xff08;短信发送的平替&#xff09;&#xff0c;就用这个来实现&#xff01;&#xff01;&#xff01;【找了好多好多方法才成功的啊啊啊啊&#x…...

【MySQL】聚合函数、group by子句

目录 聚合函数 count([distinct] column) sum([distinct] column) avg([distinct] column) max([distinct] column) min([distinct] column) group by子句 1.如何显示每个部门的平均薪资和最高薪资 2.显示每个部门每种岗位的平均薪资和最低薪资 3.显示平均工资低于200…...

详细分析SpringMvc中HandlerInterceptor拦截器的基本知识(附Demo)

目录 前言1. 基本知识2. Demo3. 实战解析 前言 对于Java的基本知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09; 1. 基本知识 HandlerInter…...

阳光能源嵌入式面试及参考答案(2万字长文)

管道能够承载的最大传输数据量是多少? 在嵌入式系统中,管道能够承载的最大传输数据量取决于多个因素。 首先,管道的容量受到操作系统的限制。不同的操作系统对管道的大小有不同的规定。一般来说,管道的容量通常是有限的,并且在不同的操作系统版本和配置下可能会有所不同。…...

P10483 小猫爬山

1. #include<bits/stdc.h> using namespace std; //一个记录小猫的重量&#xff0c;sum记录当前小猫的重量之和 int n, w, a[3000],sum[3000],ans; bool cmp(int a,int b) {return a > b; } //x表示小猫当前的编号&#xff0c;cnt表示缆车的数量 void dfs(int x,int …...

怎么注册公司支付宝账号/seo网站排名优化价格

现在的家庭多多少少都会选择购买一辆汽车&#xff0c;改革开放开始我国的城镇化建设已经得到了很大的加强&#xff0c;对于农村地区的建设我们也要有着很大的发展。而和家电下乡同样的就是汽车下乡了&#xff0c;明年开始新一轮的汽车下乡又要来了&#xff0c;农民买车补贴金额…...

杭州建设网站/常州seo博客

概述 Unix 域套接字是一种客户端和服务器在单主机上的 IPC 方法。Unix 域套接字不执行协议处理&#xff0c;不需要添加或删除网络报头&#xff0c;无需验证和&#xff0c;不产生顺序号&#xff0c;无需发送确认报文&#xff0c;比因特网域套接字的效率更高。Unix 域套接字提供字…...

网站案例展示怎么做/潍坊seo关键词排名

返回&#xff1a;贺老师课程教学链接【项目6-复数模板类】阅读教材例10.1。该例实现了一个复数类&#xff0c;但是美中不足的是&#xff0c;复数类的实部和虚部都固定只能是double型的。可以通过模板类的技术手段&#xff0c;设计Complex&#xff0c;使实部和虚部的类型为定义对…...

福建省人民政府第七办公室/武汉网络推广seo

同步事件1.声明事件//负责传递消息public delegate void MethodCall(string message);public static event MethodCall requestdata; 2.注册事件Page_Load事件中注册事件 requestdata new MethodCall(FormDataGridViewDataTable_requestdata); 3.显式的触发事件private void bu…...

做推广自己找网站/新的营销方式有哪些

为何使用无线网络 WLAN与LAN的比较WLAN通过无线接入点(AP)代替以太网交换机将客户端连接到网络。WLAN连接的是通常由电池供电的移动设备&#xff0c;而不是接到电源插座上的LAN设备。WLAN使用的帧格式与有线以太网LAN不同。由于射频可以覆盖设备的外部&#xff0c;因此 WLAN会…...

做百度移动端网站排名/教程seo推广排名网站

ubuntu 终端下配置无线网卡 2009-11-30 16:26:48| 分类&#xff1a; Ubuntu |举报 |字号 订阅 转贴&#xff1a;http://forum.ubuntu.org.cn/viewtopic.php?f116&t245226&start0注意&#xff1a;这个只是配置的教程&…...