⑴ 如何去理解linux中进程,线程等概念
对于linux来说,则没有很明确的进程、线程概念。首先linux只有进程而没有线程,然而它的进程又可以表现得像windows下的线程。linux利用fork()和exec函数族来操作多线程。fork()函数可以在进程执行的任何阶段被调用,一旦调用,当前进程就被分叉成两个进程——父进程和子进程,两者拥有相同的代码段和暂时相同的数据段(虽然暂时相同,但从分叉开的时刻就是逻辑上的两个数据段了,之所以说是逻辑上的,是因为这里是“写时复制”机制,也就是,除非万不得已有一个进程对数据段进行了写操作,否则系统不去复制数据段,这样达到了负担最小),两者的区别在于fork()函数返回值,对于子进程来说返回为0,对于父进程来说返回的是子进程id,因此可以通过if(fork()==0)…else…来让父子进程执行不同的代码段,从而实现“分叉”。
exec函数族的函数的作用则是启动另一个程序的新进程,然后完全用那个进程来代替自己(代码段被替换,数据段和堆栈被废弃,只保留原有进程id)。这样,如果在fork()之后,在子进程代码段里用exec启动另一个进程,就相当于windows下的CreateThread()的用处了,所以说linux下的进程可以表现得像windows下的线程。
然而linux下的进程不能像windows下线程那样方便地通信,因为他们没有共享数据段、地址空间等。它们之间的通信是通过所谓IPC(InterProcess Communication)来进行的。具体有管道(无名管道用于父子进程间通信,命名管道可以用于任意两个进程间的通信)、共享内存(一个进程向系统申请一块可以被共享的内存,其它进程通过标识符取得这块内存,并将其连接到自己的地址空间中,效果上类似于windows下的多线程间的共享数据段),信号量,套接字。
标签: 进程, 线程
⑵ 计算机网络知识点总结(六)Linux C++ Socket实现并发TCP服务器(fork)
本文阐述了使用 Linux 下的 fork 函数实现并发 TCP 服务器的过程。在编写并发 TCP 客户/服务器程序时,需要处理多客户端连接,实现并发处理,以提高服务器的响应效率。具体步骤如下:
1. 首先,客户机从标准输入读入一行文本,并将其发送给服务器。
2. 服务器则从网络输入读取这行文本,然后将文本回传给客户机。
3. 客户机再从网络输入读取这行回传的文本,并将其显示在标准输出上。
并发服务器与迭代服务器区别在于,迭代服务器每次只处理一个客户端连接,而并发服务器则允许同时处理多个客户端请求,使得服务效率提升。
并发服务器实现方式主要有三种:进程、线程和 IO 复用。本文通过 fork 函数实现并发处理,当一个客户端连接被处理后,服务器会继续处理下一个连接。
当客户机调用 socket 和 connect 函数时,TCP 的三路握手完成,连接建立。随后,客户机调用 send 函数发送文本,服务器则调用 accept 函数接受连接,并通过 fork 函数创建子进程。子进程调用 write 函数将输入信息回传给客户机,同时,服务器父进程继续调用 accept 函数等待下一个客户连接。
因此,系统中存在多个睡眠中的进程,包括客户进程、服务器父进程和服务器子进程。当客户机输入文本后,文本会通过网络回传至客户机的输出。
POSIX 信号处理机制允许进程在接收到特定事件时执行预定义的处理函数。信号可以由进程间传递,也可由内核触发。本文重点介绍了如何使用 signal 函数和 sigaction 函数来处理信号,包括捕获、忽略或使用默认处理方式。
以 SIGCHLD 信号为例,当子进程终止或停止时,父进程会接收到此信号。默认情况下,信号会被忽略。为了防止僵尸进程的产生,需要通过信号处理函数调用 wait 函数,以等待子进程结束。本文展示了如何定义和使用 sig__chld 函数来捕获并处理 SIGCHLD 信号,确保子进程能够被父进程正确等待。
最后,文章提及了 wait 和 waitpid 函数的原理和用法。其中,wait 函数阻塞当前进程直到等待到一个子进程终止,而 waitpid 函数则提供了更多控制,如指定等待的子进程 ID 和是否阻塞。
本文详细介绍了使用 fork 函数实现并发 TCP 服务器的过程,包括客户端与服务器的交互、并发处理方式、信号处理机制以及进程等待的实现。通过这些技术,可以构建高效、响应迅速的服务器系统。
⑶ Linux中fork,vfork和clone详解(区别与联系)
在Linux系统中,进程复制的实现不仅仅是通过fork系统调用,而是包含了fork、vfork和clone三个功能各异的实现方式。下面将分别解析这三个系统调用及其特点、区别与联系。
fork:这是Unix标准的进程复制机制,其本质是创建一个与当前进程完全相同的新进程。在fork后,父子进程的PID不同,且堆栈和数据资源完全复制。子进程对共享变量(如count)的修改不会影响到父进程的同名变量。
在复制过程中,Linux采用了写时复制策略。子进程复制了父进程的task_struct、系统堆栈空间和页面表,使得子进程与父进程在复制前共享相同的内存地址。然而,当子进程试图对共享资源进行写操作时,系统会通过_on_write机制为所涉及的页面建立新的副本,从而防止直接修改共享资源。
第一代Unix系统采用了一种“傻瓜式”的进程创建方法,即fork时复制父进程的整个地址空间。这种方式耗时且不高效。现代Linux内核采用写时复制(Copy On Write,COW),通过共享页帧而不是复制页帧来提高效率。当需要写入共享页帧时,系统会复制一份副本给进程。
vfork:vfork是fork的一个变种,它更偏向于创建轻量级的线程。vfork创建的子进程会立即执行exec或_exit函数,因此它在创建新进程后不会立即与父进程分离。这意味着子进程共享了父进程的虚拟地址空间。子进程对父进程的共享变量进行修改时,修改会直接影响到父进程的同名变量,因为它们共享同一块内存。然而,如果在vfork后使用return退出子进程,会导致父进程再次调用vfork,进入循环,这是需要避免的情况。解决方法是使用exit或_exit代替return。
clone:clone函数是功能最强大的,提供了高度的灵活性,允许用户选择性地继承父进程的资源。clone可以创建线程,共享父进程的虚存空间,或者创建独立的进程。通过设置不同的标志参数(flags),用户可以控制子进程是否与父进程共享资源、栈空间以及是否继承某些进程特性。
尽管fork、vfork和clone都能实现进程复制,但它们的使用场景和行为有所差异。fork创建完全独立的进程,vfork创建共享资源的轻量级线程,而clone提供了高度定制化的控制,允许更精细地管理子进程与父进程之间的资源共享。clone函数的参数丰富,灵活性高,可以创建线程或独立进程,继承或不继承父进程的某些特性。
这三者之间的区别主要体现在资源复制、共享方式以及创建过程的效率上。在具体的应用场景中,选择合适的系统调用可以优化程序的性能和资源管理。同时,理解它们之间的联系有助于在不同需求下灵活运用进程复制机制。
总之,fork、vfork和clone分别适用于不同场景下的进程复制需求,理解它们的原理和用法对于提高程序的效率和实现更精细的资源管理具有重要意义。