1.Linux中你常用的命令有哪些?
ls:列出当前目录下的文件和目录。
· cd [目录]:切换到指定的目录。
pwd:现实当前工作目录的路径。
touch[文件名]:如果文件不存在,新建文件。
mkdir[目录名]:创建新的目录;
rm[文件名]:删除指定的文件或目录。
cp [源文件] [目标文件]
:复制文件。
mv [源文件] [目标文件]
:移动或重命名文件。
cat [文件名]
:查看文件内容、创建文件、追加文件内容等功能。
more [文件名]
:分屏显示文件内容。
grep [word] [文件名]
:在文件中搜索指定的文本。
date
:查看或设置系统时间。
cal
:显示日历。
df
:显示磁盘剩余空间。
ps
:查看当前进程的状态。
top
:动态显示运行中的进程并排序。
kill [进程ID]
:结束指定的进程。
2. 进程、线程、协程是什么?有什么区别?
(1)进程:进程是操作系统进行资源分配和调度的一个独立单位,是程序执行的实例。每个进程都有自己的独立内存空间和系统资源。进程是程序资源管理的最小单位。
(2)线程:线程是进程中的一个执行单元,也是处理器任务调度和分派的基本单位。一个进程可以包含多个线程,所有线程共享进程的资源,如内存空间和文件描述符等。线程是资源调度的最小单位。
(3)协程:协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。
区别:进程和线程的主要区别在于它们的内存空间:进程有自己独立的地址空间,每启动一个进程,系统就会为它分配地址空间,一个进程无法直接访问另一个进程的变量和数据结构,需要通过进程间通信IPC机制(如管道、消息队列、共享内存等)来实现;而同一进程下的所有线程共享同一地址空间和系统资源。
线程和协程的主要区别在于协程的调度由用户自己控制,而线程的调度由操作系统内核负责。
3.有了进程,为什么还要有线程?
线程是为了解决进程在并发执行时所付出的时间和空间开销,提高并发性。线程相比进程有以下优点:
(1)资源消耗少:线程是一种非常节俭的多任务操作方式,相比进程需要更少的资源。一个进程的开销大概是线程的30倍左右。
(2)切换效率高:运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需的时间比进程切换所需的时间少,从而提高了系统的效率。
(3)通信方便:线程之间共享程序的公共状态和资源,线程之间通过读-写内存中的公共状态来隐式通信,这使得线程间的通信更为方便,因为它们可以直接访问对方的内存。
(4)利用多处理器系统:多线程可以充分利用多处理器系统。在这样的系统中,每个线程可以在不同的处理器上运行。
线程缺点:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都会死掉,会影响其他线程。所以多进程比多线程更加健壮。
4.何时用多进程、何时用多线程?
对资源管理和保护要求高,不限制开销和效率时,使用多进程。要求效率高,切换频繁时,资源的保护管理要求不是很高时,使用多线程。
5. 进程间通信的方式?
多个进程在用户空间是相互独立的,但是它们的内核空间是共用的。因此多进程通信大多得通过内核空间进行通信。
(1)管道
无名管道:半双工,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。本质是在内存创建一个特殊的文件进行通信,但是在文件系统中是不存在的。
有名管道:命名管道则允许无亲缘关系进程间的通信。它以一种特殊设备文件的形式存在于文件系统路径中。
(2)消息队列:消息队列是将消息放在链表上,链表存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
(3)共享内存:共享内存就是将一段内存映射到多个进程间独立的虚拟内存空间去,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的,因为它减少了系统调用,从而减少了内核空间和用户空间数据的拷贝过程,之前的方式都得通过系统调用进入内核进行通信。
(4)信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。常用的是二值信号量进行PV操作达到进程间同步和互斥的效果。
(5)套接字:套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
6.线程间通信(同步互斥)的几种方式?
线程共享进程的资源,线程间已经可以直接相互访问,所以说线程间通信,更多想要的是线程同步互斥
通信:
(1)临界区:每个线程中访问临界资源的那段代码称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。
同步互斥:
(2)互斥锁:用互斥对象机制,只有拥有锁的线程才可以访问。用于确保同一时间只有一个线程访问共享资源。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
(3)条件变量:通过条件变量通知操作的方式来保持多线程同步。
(4)读写锁:读写锁与互斥锁类似。但互斥量要么是锁住状态,要么就是不加锁状态。读写锁一次只允许一个线程写,但允许多个线程读,这样效率就比互斥锁要高。
(5)自旋锁:效果和互斥锁相似,但是互斥量阻塞以后会让出cpu休眠等待,但是自旋锁阻塞以后不会休眠,会一直等待获取锁,减少了线程从休眠到唤醒的资源时间消耗。但自旋锁不适用于单核cpu。
(6)信号量:信号量是一个计数器,也可以用来控制多个线程对共享资源的访问。它通常作为一种锁机制,防止线程进入临界区。互斥锁和信号量的区别在于,互斥锁只能达到互斥的效果,但是信号量同时能达到互斥和同步的效果,一般互斥锁搭配条件变量达到同步互斥的效果。
7.Linux多进程开发中,怎么创建一个进程?
在Linux多进程开发中,创建一个新的进程通常使用fork()
函数。fork()
函数会创建一个新的进程,这个新进程是当前进程的一个副本。在父进程中,fork()
返回新创建的子进程的ID,而在子进程中,fork()
返回0。因此,我们可以通过fork()
的返回值来判断当前进程是父进程还是子进程。
此外,vfork()
函数也可以用来创建新的进程。与fork()
不同,vfork()
创建的子进程会抢占父进程的资源,导致父进程无法继续运行,只有等到子进程结束后,父进程才能继续运行。
创建新进程后,你可能需要使用exec()
函数族来执行新的程序。exec()
函数族会用一个新的程序替换当前进程的内存空间。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
printf("+++process %d start running!ppid = %d\\n", getpid(), getppid());
pid_t pid = fork();
if (pid) { //父进程
printf("parent:process %d start running!ppid = %d\\n", getpid(), getppid());
//do something
//...
} else { //子进程
printf("child:process %d start running!ppid = %d\\n", getpid(), getppid());
//do something
//...
exit(0);
}
exit(0);
}
8.进程有几种状态?
进程有五种状态:创建、就绪、执行、阻塞、终止。一个进程创建后,被放入队列处于就绪状态,等待操作系统调度执行,执行过程中可能切换到阻塞状态(并发),任务完成后,进程销毁终止。
(1)创建状态(New):当一个进程刚刚被创建时,它处于新建状态。此时,操作系统为进程分配必要的资源,并对其进行初始化。
(2)就绪状态(Ready):当一个进程已经被创建并具备运行所需的所有资源时,但尚未被调度执行,它处于就绪状态。在就绪状态下,进程等待操作系统的调度,以便在合适的时机执行。
(3)运行状态(Running):当操作系统将 CPU 资源分配给处于就绪状态的进程时,进程进入运行状态。在运行状态下,进程正在执行其指令和操作。
(4)阻塞状态(Blocked):当一个进程无法继续执行,因为它正在等待某种事件的发生,比如等待输入/输出、等待资源释放等,此时进程进入阻塞状态。在阻塞状态下,进程会释放 CPU 资源,让其他可运行的进程有机会执行。
(5)终止状态(Terminated):当一个进程完成了其任务或由于某种原因被操作系统终止时,它进入终止状态。在终止状态下,进程释放它所占用的资源,并等待系统回收。
9.Linux多线程开发中,怎么创建一个线程?
在Linux多线程开发中,可以使用pthread_create
函数来创建一个新的线程。下面是一个简单的示例:
pthread_create
函数用于创建一个新的线程。这个函数接收四个参数:
- 第一个参数是一个
pthread_t
类型的指针,用于存储新创建的线程的ID。 - 第二个参数是一个指向
pthread_attr_t
类型的指针,用于设置线程的属性。如果我们传入NULL,那么线程将使用默认的属性。 - 第三个参数是一个函数指针,指向新线程开始执行时要调用的函数。
- 第四个参数是一个
void
指针,可以传递给新线程开始执行时调用的函数。
pthread_create
函数成功时返回0,失败时返回错误号。创建的新线程从指定的函数开始执行。当这个函数返回时,线程就会自动结束。如果我们希望主线程等待新线程结束,可以使用pthread_join
函数。
10. 什么是死锁,死锁产生的条件,如何解决死锁?
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。当多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进,这种情况就是死锁。
死锁产生的四个必要条件:
(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:存在一个进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。
解决死锁的方法主要有以下几种:
(1) 按照顺序加锁:尝试让所有线程按照同一顺序获取锁,从而避免死锁。
(2) 设置获取锁的超时时间:尝试获取锁的线程在规定时间内没有获取到锁,就放弃获取锁,避免因为长时间等待锁而引起的死锁。
(3) 银行家算法:在程序运行时避免发生死锁。
(4)检测死锁并恢复:死锁发生时对其进行检测,一旦发生死锁后,采取行动解决问题。
(5)通过仔细分配资源来避免死锁。
(6)通过破坏死锁产生的四个条件之一来避免死锁。
11.使用多线程时需要注意什么?
(1)线程安全:多线程环境下,多个线程同时访问共享资源可能会引发竞态条件(Race Condition),导致数据不一致或其他异常情况。确保共享资源的访问是线程安全的,可以通过使用互斥锁(Mutex)、条件变量(Condition Variable)等同步机制来保护共享资源的访问。
(2)死锁:死锁是指两个或多个线程在互相等待对方释放资源而无法继续执行的状态。避免死锁的方法之一是按照固定的顺序获取锁,避免循环依赖。另外,可以使用资源分配图等方法进行死锁检测和预防。
(3)错误处理和异常处理:在多线程环境下,错误和异常的处理需要更加谨慎。及时捕获和处理线程中的异常,确保程序的稳定性和可靠性。
12. 进程和线程的区别?
(1)线程是程序调度的基本单位,进程是资源分配的基本单位。
(2)一个进程崩溃不会导致其他进程崩溃,一个线程崩溃可能会导致整个程序崩溃。
(3)进程创建和销毁进程所消耗的资源比较多,线程创建和销毁线程所消耗的资源比较少。
(3)每个进程都有独立的内存空间和系统资源,线程是在进程内部创建的,共享相同的内存空间和系统资源。
13.使用fork函数和vfork函数创建进程的区别
(1)使用fork创建的子进程和父进程有独立的地址空间。
(2)vfork 创建的子进程与父进程共享地址空间,包括代码段、数据段和堆栈等。
(3)使用fork时父子进程的执行顺序是不确定的,取决于系统调度。
(4)vfork会暂停父进程的执行,直到子进程调用exec或_exit,在此期间,父进程一直等待子进程的结束。