@@ -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