1.标准IO和文件IO的区别
标准IO:调用封装好的相关库函数,来实现数据的输入输出
文件IO:调用系统(内核)提供的相关函数,来实现数据的输入输出
1、标准IO属于库函数,文件IO属于系统调用
2、标准IO操作的是文件指针,文件IO操作的是文件描述符
3、标准IO有缓冲区,文件IO没有缓冲区
2.静态库和动态库的区别
静态库在编译阶段会被直接复制到最终的可执行文件中。这意味着,一旦程序被编译,它就包含了所有需要的库函数代码,不再依赖于外部库文件。
动态库在程序运行时才被加载。多个程序可以共享同一份库文件,减少了磁盘空间的占用。
程序运行时需要访问系统中已安装的动态库。如果缺少必要的动态库,程序将无法正常运行。
动态库的更新更为方便,只需替换库文件即可。
3.怎么创建进程
pid_t pid = -1; //定义变量存储创建的进程的id号
pid = fork(); //创建一个子进程
4.什么是守护进程
守护进程:脱离了终端而存在的进程,随着系统的启动而开始,随着系统的结束而终止。
守护进程相当于一个服务,不依赖于任何终端而存在,随着系统的启动而启动,终止而结束
守护进程制作流程:
1、创建子进程,让其成为孤儿进程:fork exit
2、将自己设置成会话组组长,不依赖终端:setsid
5.什么是僵尸进程?什么是孤儿进程?
孤儿进程:当前进程的父进程死亡后,但是当前进程还没有结束,那么当前进程称为孤儿进程,孤儿进程会由1号进程收养
僵尸进程:当前进程已经死亡,但是其父进程没有为其收尸,那么该进程为僵尸进程
6.时间片了解么?
时间片(Time Slice)是操作系统调度机制中的一个基本概念,主要用于多任务处理环境。它指的是分配给每个进程或线程在中央处理器(CPU)上执行的时间长度。通过这种方式操作系统能够实现多个进程或线程之间的快速切换,从而让用户感觉像是多个程序同时在运行,即使实际上只有一个CPU核心在工作。
7.进程和线程区别?
- 进程是一个程序在一个给定数据集上的一次执行活动,它是操作系统进行资源分配和调度的基本单位。
- 线程是进程内的一个执行单元,是CPU调度和分配的基本单位。一个进程中可以有多个线程,这些线程共享进程的资源。
- 每个进程都有自己独立的地址空间、内存、数据栈以及其他操作系统资源
- 同一进程内的所有线程共享进程的地址空间、文件描述符、堆栈等资源。每个线程有自己的程序计数器、寄存器集合和栈。
8.线程的同步怎么实现
可以使用互斥锁以及信号量集进行进程的同步,还可以使用条件变量控制线程的运行
9.线程的互斥怎么实现
使用互斥锁确保同一个时间只有一个线程持有锁,就可以实现线程的互斥
10.进程间通信方式有哪些?哪种效率最高
通信方式有管道文件,消息队列,共享内存,信号量集,套接字等。共享内存的效率最高。
11.通信方式的优缺点对比
管道的优点是实现简单,使用方便,效率高。缺点是数据流是单向的。
消息队列是进程将数据以消息的形式发送到一个队列中,其他进程可以从队列中读取消息。消息队列由操作系统内核管理。优点是通信灵活,支持多对多通信。缺点是需要管理生命周期。
共享内存是多个进程可以映射同一块物理内存区域,从而实现高效的数据共享。优点是效率高,缺点是需要同步机制来避免数据竞争。
套接字的优点是适用于不同主机之间的通信。缺点是效率低,需要协议。
12.有名管道和无名管道的区别?
有名管道可以在文件系统中找到, 可以被任何进程访问。无名管道只能用于父子进程间通信,不能找到。有名管道不会在程序结束时删除,除非被显示的删除。无名管道两端关闭时就会删除。
13.共享内存的实现方式
发送端
1·首先int shmget(key_t key, size_t size, int shmflg);创建一个共享内存并返回id
2·然后将共享内存地址映射到用户空间 void *shmat(int shmid, const void *shmaddr, int shmflg);返回地址
3·然后使用int shmctl(int shmid, int cmd, struct shmid_ds *buf); 控制共享内存对象
4·取消共享内存的映射 int shmdt(const void *shmaddr);
接收端
1·创建key值用于创建共享内存段 key_t key = ftok("/", 't');
2·创建一个共享内存的对象 int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
3·将共享内存段映射到程序中来 char *addr = (char *)shmat(shmid, NULL, 0);
4·读出共享内存中的数据 printf("消息为:%s", addr);
5·删除共享内存 if(shmctl(shmid, IPC_RMID, NULL) == -1)......
14.消息队列的实现方式
1·创建用于生成消息队列的钥匙 key_t ftok(const char *pathname, int proj_id);
2·通过钥匙创建出一个消息队列对象 int msgget(key_t key, int msgflg);
3·向消息队列中存放消息 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
4.从消息队列中取消息 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
5·控制消息队列 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
15.fork和vfork区别
fork()
创建一个新进程,这个新进程几乎是父进程的一个完全复制。子进程中所有变量的值都是从父进程中复制过来的,包括内存空间、文件描述符等。
vfork()
也用来创建一个新的进程,但是与 fork()
不同的是,vfork()
不复制父进程的地址空间给子进程。子进程在没有自己的地址空间的情况下运行,它实际上是在父进程的地址空间上运行。
16.线程的死锁,怎么避免?
两个及以上线程互相互相等待对方持有的资源而无法继续执行时就会发生死锁,避免方法有
-
避免持有和等待:尽可能让线程在开始执行前一次性获取所有必需的资源。
-
资源排序:规定一个全局顺序来获取资源,并且强制所有线程按这个顺序获取资源。
-
使用超时:在尝试获取资源时使用超时机制,这样线程在等待过长时间后可以放弃,回退,并重新尝试。
-
检测死锁并恢复:运行时检测死锁的存在,一旦检测到死锁,采取措施(如终止线程或回滚操作)来解决。