Skip to content

Commit 3516787

Browse files
committed
update OS part
1 parent a0d8bbb commit 3516787

1 file changed

Lines changed: 36 additions & 16 deletions

File tree

OperatingSystem/2.进程与线程.md

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,9 @@ A给B发送信号,B收到信号之前执行自己的代码,收到信号后
323323
324324
信号的特质:由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对于用户来说,这个延迟时间非常短,不易察觉。
325325
每个进程收到的所有信号,都是由内核负责发送的,内核处理。
326-
327-
326+
```c
327+
int kill(pid_t pid, int sig);
328+
```
328329

329330
两个或多个进程可以通过简单的信号进行合作,可以强迫一个进程在某个位置停止,直到它接收到一个特定的信号。信号量是一个特殊的变量。用于进程间传递信息的一个整数值。
330331

@@ -337,16 +338,37 @@ A给B发送信号,B收到信号之前执行自己的代码,收到信号后
337338
操作系统会中断目标程序的进程来向其发送信号、在任何非原子指令中,执行都可以中断,如果进程已经注册了新号处理程序,那么就执行进程,如果没有注册,将采用默认处理的方式。
338339

339340
信号效率非常高,但是可携带的数据有限,只能写到一个标志位,无法携带其他数据。
341+
342+
不建议使用信号完成进程间通信,因为信号的优先级太高,产生信号之后会打断程序的执行。
343+
344+
340345
- 信号量
341346

342347
信号量(semaphore)是由荷兰人E.W.Dijkstra在20世纪60年代所构思出的一种程序设计构造。其原型来源于铁路的运行:在一条单轨铁路上,任何时候只能有一列列车行驶在上面。而管理这条铁路的系统就是信号量。任何一列火车必须等到表明铁路可以行驶的信号后才能进入轨道。当一列列车进入单轨运行后,需要将信号改为禁止进入,从而防止别的火车同时进入轨道。而当列车驶出单轨后,则需要将信号变回允许进入状态。这很像以前的旗语,在计算机里,信号量实际上就是一个简单整数。一个进程在信号变为0或者1的情况下推进,并且将信号变为1或0来防止别的进程推进。当进程完成任务后,则将信号再改变为0或1,从而允许其他进程执行。需要注意的是,信号量不只是一种通信机制,更是一种同步机制。
343348

344349
- Shared Memory共享内存
345350

346-
管道、套接字、信号、信号量,虽然满足了多种通信需要,但还是有一种需要未能满足。这就是两个进程需要共享大量数据。这就像两个人,他们互相喜欢,并想要一起生活时(共享大量数据量),打电话、握手、对白等就显得不够了,这个时候需要的是拥抱,只有将其紧紧拥抱于怀,感觉才最到位,也才能尽可能地共享。进程的拥抱就是共享内存。共享内存就是两个进程共同拥有同一片内存。对于这片内存中的任何内容,二者均可以访问。要使用共享内存进行通信,一个进程首先需要创建一片内存空间专门作为通信用,而其他进程则将该片内存映射到自己的(虚拟)地址空间。这样,读写自己地址空间中对应共享内存的区域时,就是在和其他进程进行通信。乍一看,共享内存有点像管道,有些管道不也是一片共享内存吗?这是形似而神不似。首先,使用共享内存机制通信的两个进程必须在同一台物理机器上;其次,共享内存的访问方式是随机的,而不是只能从一端写,另一端读,因此其灵活性比管道和套接字大很多,能够传递的信息也复杂得多。共享内存的缺点是管理复杂,且两个进程必须在同一台物理机器上才能使用这种通信方式。共享内存的另外一个缺点是安全性脆弱。因为两个进程存在一片共享的内存,如果一个进程染有病毒,很容易就会传给另外一个进程。就像两个紧密接触的人,一个人的病毒是很容易传染另外一个人的。这里需要注意的是,使用全局变量在同一个进程的进程间实现通信不称为共享内存。
351+
管道、套接字、信号、信号量,虽然满足了多种通信需要,但还是有一种需要未能满足。这就是两个进程需要共享大量数据。这就像两个人,他们互相喜欢,并想要一起生活时(共享大量数据量),打电话、握手、对白等就显得不够了,这个时候需要的是拥抱,只有将其紧紧拥抱于怀,感觉才最到位,也才能尽可能地共享。进程的拥抱就是共享内存。
352+
共享内存就是两个进程共同拥有同一片内存。对于这片内存中的任何内容,二者均可以访问。要使用共享内存进行通信,一个进程首先需要在内核中创建一片内存空间专门作为通信用,然后该进程与这片内存空间进行关联,而其他进程则将该片内存映射到自己的(虚拟)地址空间。
353+
这样,读写自己地址空间中对应共享内存的区域时,就是在和其他进程进行通信。乍一看,共享内存有点像管道,有些管道不也是一片共享内存吗?这是形似而神不似:- 首先,使用共享内存机制通信的两个进程必须在同一台物理机器上;
354+
- 其次,共享内存的访问方式是随机的,而不是只能从一端写,另一端读,因此其灵活性比管道和套接字大很多,能够传递的信息也复杂得多。
355+
356+
共享内存的缺点是管理复杂,且两个进程必须在同一台物理机器上才能使用这种通信方式。共享内存的另外一个缺点是安全性脆弱。因为两个进程存在一片共享的内存,如果一个进程染有病毒,很容易就会传给另外一个进程。就像两个紧密接触的人,一个人的病毒是很容易传染另外一个人的。这里需要注意的是,使用全局变量在同一个进程的进程间实现通信不称为共享内存。
347357

348-
共享内存是进程通信中最快的一种方法,因为数据不需要在进程间复制,而可以直接映射到各个进程的地址空间中。在共享内存中要注意的一个问题是:当多个进程对共享内存区域进行访问时,要注意这些进程之间的同步问题。例如,如果server正在向共享内存区域中写数据,那么client进程就不能访问这些数据,直到server全部写完之后,client才可以访问。为了实现进程间的同步问题,通常使用前面介绍过的信号量来实现这一目标。一个共享内存段可以由一个进程创建,然后由任意共享这一内存段的进程对它进行读写。当进程间需要通信时,一个进程可以创建一个共享内存段,然后需要通信的各个进程就可以在信号量的控制下保持同步,在这里交换数据,完成通信。
358+
共享内存是进程通信中最快的一种方法,因为数据不需要在进程间复制,而可以直接映射到各个进程的地址空间中。在共享内存中要注意的一个问题是:当多个进程对共享内存区域进行访问时,要注意这些进程之间的同步问题。
359+
例如,如果server正在向共享内存区域中写数据,那么client进程就不能访问这些数据,直到server全部写完之后,client才可以访问。
360+
为了实现进程间的同步问题,通常使用前面介绍过的信号量来实现这一目标。一个共享内存段可以由一个进程创建,然后由任意共享这一内存段的进程对它进行读写。
361+
当进程间需要通信时,一个进程可以创建一个共享内存段,然后需要通信的各个进程就可以在信号量的控制下保持同步,在这里交换数据,完成通信。
362+
创建共享内存:
363+
```c
364+
#include <sys/ipc.h>
365+
#include <sys/shm.h>
366+
// 创建或者获得共享内存ID
367+
int shmget(key_t key, size_t size, int shmflg);
368+
// 连接共享内存
369+
void *shmat(int shmid, const void *shmaddr, int shmflg);
349370

371+
```
350372
351373
- FIFO
352374
@@ -360,16 +382,16 @@ FIFO常被称为命名管道,以区分管道(pipe)。管道(pipe)只能用于
360382
361383
362384
- 存储映射
363-
       存储映射I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用read和write函数的情况下,使用地址(指针)完成I/O操作。
364-
       使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap函数来实现。
365-
总结:使用mmap时务必注意以下事项:
366-
1.     创建映射区的过程中,隐含着一次对映射文件的读操作。
367-
2.     当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护)。而MAP_PRIVATE则无所谓,因为mmap中的权限是对内存的限制。
368-
3.     映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭。
369-
4.     特别注意,当映射文件大小为0时,不能创建映射区。所以:用于映射的文件必须要有实际大小!!   mmap使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
370-
5.     munmap传入的地址一定是mmap的返回地址。坚决杜绝指针++操作。
371-
6.     如果文件偏移量必须为4K的整数倍
372-
7.     mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。
385+
   存储映射I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用read和write函数的情况下,使用地址(指针)完成I/O操作。
386+
   使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap函数来实现。
387+
总结:使用mmap时务必注意以下事项:
388+
1. 创建映射区的过程中,隐含着一次对映射文件的读操作。
389+
2. 当MAP_SHARED时,要求:映射区的权限应 <=文件打开的权限(出于对映射区的保护)。而MAP_PRIVATE则无所谓,因为mmap中的权限是对内存的限制。
390+
3. 映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭。
391+
4. 特别注意,当映射文件大小为0时,不能创建映射区。所以:用于映射的文件必须要有实际大小!!   mmap使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
392+
5. munmap传入的地址一定是mmap的返回地址。坚决杜绝指针++操作。
393+
6. 如果文件偏移量必须为4K的整数倍
394+
7. mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。
373395
374396
- 匿名映射
375397
@@ -388,8 +410,6 @@ int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
388410
进程与其它的进程进行通信而不必借助共享数据,通过互相发送和接收消息,建立一条通信链路。
389411

390412

391-
392-
393413
## 进程调度
394414

395415
当一个计算机是多道程序设计系统时,会频繁的有很多进程或者线程来同时竞争 CPU 时间片。当两个或两个以上的进程/线程处于就绪状态时,就会发生这种情况。如果只有一个 CPU 可用,那么必须选择接下来哪个进程/线程可以运行。操作系统中有一个叫做调度程序(scheduler) 的角色存在,它就是做这件事儿的,该程序使用的算法叫做调度算法(scheduling algorithm) 。进程调度就是处理器调度(上下文切换)。

0 commit comments

Comments
 (0)