unix-2

Posted 悠悠我心。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unix-2相关的知识,希望对你有一定的参考价值。

1、fopen和open区别

 1 前者属于低级IO,后者是高级IO。
 2 前者返回一个文件描述符,后者返回一个文件指针。
 3 前者无缓冲,后者有缓冲。
 4 前者与 read, write 等配合使用, 后者与 fread, fwrite等配合使用。
 5 后者是在前者的基础上扩充而来的,在大多数情况下,用后者。 
 6 
 7 1.open 是系统调用 返回的是文件句柄,文件的句柄是文件在文件描述副表里的索引,fopen是C的库函数,返回的是一个指向文件结构的指针。
 8 2.fopen是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api 
 9 3.fopen是标准c函数。返回文件流而不是linux下文件句柄。设备文件不可以当成流式文件来用,只能用open
10 4.fopen是用来操纵正规文件的,并且设有缓冲的,跟open还是有一些区别
11 5.一般用fopen打开普通文件,用open打开设备文件
12 6.fopen是标准c里的,而open是linux的系统调用.
13 7.他们的层次不同.
14 8.fopen可移植,open不能
15 9.我认为fopen和open最主要的区别是fopen在用户态下就有了缓存,在进行read和write的时候减少了用户态和内核态的切换,而open则每次都需要进行内核态和用户态的切换;表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列快;如果随机访问文件open要比fopen快。
View Code

 2、进程(程序 数据  进程控制块pcb)

pcb:

 1 进程控制块包含三类信息 
 2 
 3 1.标识信息。用于唯一地标识一个进程,常常分由用户使用的外部标识符和被系统使用的内部标识号。几乎所有操作系统中进程都被赋予一个唯一的、内部使用的数值型的进程号,操作系统的其他控制表可以通过进程号来交叉引用进程控制表。常用的标识信息包括进程标识符、父进程的标识符、用户进程名、用户组名等。 
 4 
 5 2.现场信息。用于保留一个进程在运行时存放在处理器现场中的各种信息,任何一个进程在让出处理器时必须把此时的处理器现场信息保存到进程控制块中,而当该进程重新恢复运行时也应恢复处理器现场。常用的现场信息包括通用寄存器的内容、控制寄存器(如PSW寄存器)的内容、用户堆战指针、系统堆饺指针等。 
 6 
 7 3.控制信息。用于管理和调度一个进程。常用的控制信息包括:l)进程的调度相关信息,如进程状态、等待事件和等待原因、进程优先级、队列指引元等2)进程组成信息,如正文段指针、数据段指针:引进程间通信相关信息,如消息队列指针、信号量等互斥和同步机制4)进程在辅存储器内的地址5)CPU资源的占用和使用信息,如时间片余量、进程己占用CPU的时间、进程己执行的时间总和,记账信息6)进程特权信息,如在内存访问和处理器状态方面的特权7)资源清单,包括进程所需全部资源、已经分得的资源,如主存资源、I/0设备、打开文件表等。 
 8 
 9 ●队列:把处于同一状态(例如就绪态)的所有进程控制块链接在一起,这样的数据结构称为进程队列(Process Queues)。 
10 
11 ●进程的创建来源于以下四个事件: 
12 
13 1.提交一个批处理作业。 
14 
15 2.在终端上交互式的登录。 
16 
17 3.操作系统创建一个服务进程。 
18 
19 4.存在的进程孵化(spawn)新的进程。 
20 
21 ●进程的创建过程如下描述: 
22 
23 1.在主进程表中增加一项,并从PCB池中取一个空白PCB。 
24 
25 2.为新进程的进程映像中的所有成分分配地址空间。对于进程孵化操作还需要传递环境变量,构造共享地址空间。 
26 
27 3.为新进程分配资源,除内存空间外,还有其它各种资源。 
28 
29 4.查找辅助存储器,找到进程正文段并装入到正文区。 
30 
31 5.初始化进程控制块,为新进程分配一个唯一的进程标识符,初始化PSW。 
32 
33 6.把进程加入某一就绪进程队列,或直接将进程投入运行。 
34 
35 7.通知操作系统的某些模块,如记账程序、性能监控程序。 
36 
37 ●进程切换的步骤 
38 
39 1.保存被中断进程的处理器现场信息。 
40 
41 2.修改被中断进程的进程控制块的有关信息,如进程状态等。 
42 
43 3.把被中断进程的进程控制块加入有关队列。 
44 
45 4.选择下一个占有处理器运行的进程。 
46 
47 5.修改被选中进程的进程控制块的有关信息。 
48 
49 6.根据被选中进程设置操作系统用到的地址转换和存储保护信息。 
50 
51 7.根据被选中进程的信息来恢复处理器现场
View Code

  进程间通信方式:

1 # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2 # 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
3 # 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
4 # 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5 # 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
6 # 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
7 # 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
View Code

  进程线程的关系

1 进程和线程的关系:
21)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
32)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
43)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
54)处理机分给线程,即真正在处理机上运行的是线程。
65)线程是指进程内的一个执行单元,也是进程内的可调度实体。
View Code
1 线程与进程的区别:
21)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
32)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
43)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
54)系统开销:在创建或撤销进程的时候,由于系统都要为之分配和回收资源,导致系统的明显大于创建或撤销线程时的开销。但进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响,而线程只是一个进程中的不同的执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但是在进程切换时,耗费的资源较大,效率要差些。
View Code

一个线程挂了  整个进程就挂了

3、死锁  银行家算法

4、socket模型

select 模型

  1 socket编程的select模型
  2 
  3       在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的监听客户端的请求,有新的请求到达时,开辟一个新的线程去和该客户端进行后续处理,但是这样针对每一个客户端都需要去开辟一个新的线程,效率必定底下。
  4 
  5      其实,socket编程提供了很多的模型来处理这种情形,我们只要按照模型去实现我们的代码就可以解决这个问题。主要有select模型和重叠I/o模型,以及完成端口模型。这次,我们主要介绍下select模型,该模型又分为普通select模型,wsaasyncselect模型,wsaeventselect模型。我们将通过样例代码的方式逐一介绍。
  6 
  7 一、select模型
  8 
  9 使用该模型时,在服务端我们可以开辟两个线程,一个线程用来监听客户端的连接
 10 
 11 请求,另一个用来处理客户端的请求。主要用到的函数为select函数。如:
 12 
 13 全局变量:
 14 
 15 fd_set  g_fdClientSock;
 16 线程1处理函数:
 17 
 18 复制代码
 19     SOCKET listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
 20 
 21     sockaddr_in sin;
 22     sin.sin_family = AF_INET;
 23     sin.sin_port = htons(7788);
 24     sin.sin_addr.S_un.S_addr = INADDR_ANY;
 25 
 26     int nRet = bind( listenSock, (sockaddr*)&sin, (int)(sizeof(sin)));
 27     if ( nRet == SOCKET_ERROR )
 28     {
 29         DWORD errCode = GetLastError();
 30         return;
 31     }
 32 
 33     listen( listenSock, 5);
 34 
 35     int clientNum = 0;
 36 
 37     sockaddr_in clientAddr;
 38 
 39     int nameLen = sizeof( clientAddr );
 40 
 41     while( clientNum < FD_SETSIZE )
 42     {
 43         SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen );
 44         FD_SET( clientSock, &g_fdClientSock);
 45         clientNum++;
 46      }
 47 复制代码
 48 线程2处理函数:
 49 
 50 复制代码
 51     fd_set fdRead;
 52     FD_ZERO( &fdRead );
 53     int nRet = 0;
 54     char* recvBuffer =(char*)malloc( sizeof(char) * 1024 );
 55 
 56     if ( recvBuffer == NULL )
 57     {
 58         return;
 59     }
 60 
 61     memset( recvBuffer, 0, sizeof(char) * 1024 );
 62     while ( true )
 63     {
 64         fdRead = g_fdClientSock;
 65         nRet = select( 0, &fdRead, NULL, NULL, NULL );
 66         if ( nRet != SOCKET_ERROR )
 67         {
 68             for ( int i = 0; i < g_fdClientSock.fd_count; i++ )
 69             {
 70                 if ( FD_ISSET(g_fdClientSock.fd_array[i],&fdRead)  )
 71                 {
 72                     memset( recvBuffer, 0, sizeof(char) * 1024 );
 73                     nRet = recv( g_fdClientSock.fd_array[i], recvBuffer, 1024, 0);
 74                     if ( nRet == SOCKET_ERROR )
 75                     {
 76                         closesocket( g_fdClientSock.fd_array[i] );
 77                             FD_CLR( g_fdClientSock.fd_array[i], &g_fdClientSock );
 78                     }
 79                     else
 80                     {
 81                         //todo:后续处理
 82                        }
 83                 }
 84             }
 85         }
 86     }
 87 
 88     if ( recvBuffer != NULL )
 89     {
 90         free( recvBuffer );
 91     }
 92 复制代码
 93 该模型有个最大的缺点就是,它需要一个死循环不停的去遍历所有的客户端套接字集合,询问是否有数据到来,这样,如果连接的客户端很多,势必会影响处理客户端请求的效率,但它的优点就是解决了每一个客户端都去开辟新的线程与其通信的问题。如果有一个模型,可以不用去轮询客户端套接字集合,而是等待系统通知,当有客户端数据到来时,系统自动的通知我们的程序,这就解决了select模型带来的问题了。
 94 
 95 二、WsaAsyncSelect模型
 96 
 97 WsaAsyncSelect模型就是这样一个解决了普通select模型问题的socket编程模型。它是在有客户端数据到来时,系统发送消息给我们的程序,我们的程序只要定义好消息的处理方法就可以了,用到的函数只要是WSAAsyncSelect,如:
 98 
 99 首先,我们定义一个Windows消息,告诉系统,当有客户端数据到来时,发送该消息给我们。
100 
101 #define  UM_SOCK_ASYNCRECVMSG  WM_USER + 1
102 在我们的处理函数中可以如下监听客户端的连接:
103 
104 复制代码
105     SOCKET listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
106     sockaddr_in sin;
107     sin.sin_family = AF_INET;
108     sin.sin_port = htons(7788);
109     sin.sin_addr.S_un.S_addr = INADDR_ANY;
110     int nRet = bind( listenSock, (sockaddr*)&sin, (int)(sizeof(sin)));
111     if ( nRet == SOCKET_ERROR )
112     {
113         DWORD errCode = GetLastError();
114         return;
115     }
116 
117     listen( listenSock, 5);
118 
119     int clientNum = 0;
120     sockaddr_in clientAddr;
121     int nameLen = sizeof( clientAddr );
122 
123     while( clientNum < FD_SETSIZE )
124     {
125         SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen );
126         //hWnd为接收系统发送的消息的窗口句柄
127          WSAAsyncSelect( clientSock, hWnd, UM_SOCK_ASYNCRECVMSG, FD_READ | FD_CLOSE );
128         clientNum++;
129     }
130 复制代码
131  
132 
133 接下来,我们需要在我们的窗口添加对UM_SOCK_ASYNCRECVMSG消息的处理函数,在该函数中真正接收客户端发送过来的数据,在这个消息处理函数中的wparam参数表示的是客户端套接字,lparam参数表示的是发生的网络事件如:
134 
135 复制代码
136    SOCKET clientSock = (SOCKET)wParam;
137    if ( WSAGETSELECTERROR( lParam ) )
138    {
139       closesocket( clientSock );
140       return;
141    }
142 
143    switch ( WSAGETSELECTEVENT( lParam ) )
144    {
145        case FD_READ:
146        {
147            char recvBuffer[1024] = {\'\\0\'};
148            int nRet = recv( clientSock, recvBuffer, 1024, 0 );
149            if ( nRet > 0 )
150            {
151                 szRecvMsg.AppendFormat(_T("Client %d Say:%s\\r\\n"), clientSock, recvBuffer );
152            }
153            else
154            {
155                 //client disconnect
156                 szRecvMsg.AppendFormat(_T("Client %d Disconnect!\\r\\n"), clientSock );
157            }
158         }                              
159 
160         break;
161 
162       case FD_CLOSE:
163       {
164            closesocket( clientSock );
165            szRecvMsg.AppendFormat(_T("Client %d Disconnect!\\r\\n"), clientSock );
166       }
167 
168       break;
169     }
170 复制代码
171     可以看到WsaAsyncSelect模型是非常简单的模型,它解决了普通select模型的问题,但是它最大的缺点就是它只能用在windows程序上,因为它需要一个接收系统消息的窗口句柄,那么有没有一个模型既可以解决select模型的问题,又不限定只能是windows程序才能用呢?下面我们来看看WsaEventSelect模型。
172 
173 三、WsaEventSelect模型
174 
175 WsaEventSelect模型是一个不用主动去轮询所有客户端套接字是否有数据到来的模型,它也是在客户端有数据到来时,系统发送通知给我们的程序,但是,它不是发送消息,而是通过事件的方式来通知我们的程序,这就解决了WsaAsyncSelect模型只能用在windows程序的问题。
176 
177 该模型的实现,我们也可以开辟两个线程来进行处理,一个用来接收客户端的连接请求,一个用来与客户端进行通信,用到的主要函数有WSAEventSelect,WSAWaitForMultipleEvents,WSAEnumNetworkEvents实现方式如下:
178 
179 首先定义三个全局数组
180 
181 SOCKET      g_SockArray[MAX_NUM_SOCKET];//存放客户端套接字
182 
183 WSAEVENT    g_EventArray[MAX_NUM_SOCKET];//存放该客户端有数据到来时,触发的事件
184 
185 UINT32      g_totalEvent = 0;//记录客户端的连接数
186 线程1处理函数如下:
187 
188 复制代码
189     SOCKET listenSock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
190     sockaddr_in sin;
191     sin.sin_family = AF_INET;
192     sin.sin_port = htons(7788);
193     sin.sin_addr.S_un.S_addr = INADDR_ANY;
194     int nRet = bind( listenSock, (sockaddr*)&sin, (int)(sizeof(sin)));
195     if ( nRet == SOCKET_ERROR )
196     {
197         DWORD errCode = GetLastError();
198         return199     }
200 
201     listen( listenSock, 5);
202 
203     sockaddr_in clientAddr;
204     int nameLen = sizeof( clientAddr );
205     while( g_totalEvent < MAX_NUM_SOCKET )
206     {
207         SOCKET clientSock = accept( listenSock, (sockaddr*)&clientAddr, &nameLen );
208         if ( clientSock == INVALID_SOCKET )
209         {
210             continue;
211         }
212         g_SockArray[g_totalEvent] = clientSock;
213 
214         if( (g_EventArray[g_totalEvent] = WSACreateEvent()) == WSA_INVALID_EVENT )
215         {
216             continue;
217         }
218 
219         WSAEventSelect( clientSock, g_EventArray[g_totalEvent],FD_READ | FD_CLOSE );
220         g_totalEvent++;
221     }
222 复制代码
223     线程2的处理函数如下:
224 
225 复制代码
226     int nIndex = 0;
227     char* recvBuffer =(char*)malloc( sizeof(char) * 1024 );
228 
229     if ( recvBuffer == NULL )
230     {
231     return;
232     }
233 
234     memset( recvBuffer, 0, sizeof(char) * 1024 );
235 
236     while( true )
237     {
238         nIndex = WSAWaitForMultipleEvents( g_totalEvent, g_EventArray, FALSE, WSA_INFINITE,FALSE );
239         if ( nIndex == WSA_WAIT_FAILED )
240         {
241             continue;
242         }
243         else
244         { 
245             WSAResetEvent( g_EventArray[ nIndex - WSA_WAIT_EVENT_0]);
246             SOCKET clientSock = g_SockArray[ nIndex - WSA_WAIT_EVENT_0 ];
247             WSANETWORKEVENTS wsaNetWorkEvent;
248 
249             int nRet = WSAEnumNetworkEvents( clientSock, g_EventArray[nIndex - WSA_WAIT_EVENT_0], &wsaNetWorkEvent );
250             if ( SOCKET_ERROR == nRet )
251             {
252                 continue;
253             }
254             else if ( wsaNetWorkEvent.lNetworkEvents & FD_READ )
255             {
256                 if ( wsaNetWorkEvent.iErrorCode[FD_READ_BIT] != 0 )
257                 {
258                     //occur error
259                     closesocket( clientSock );
260                 }
261                 else 
262                 {
263                     memset( recvBuffer, 0, sizeof(char) * 1024 );
264                     nRet = recv( clientSock, recvBuffer, 1024, 0);
265                     if ( nRet == SOCKET_ERROR )
266                     {
267                         closesocket( clientSock );
268                     }
269                     else
270                     {
271                         //todo:对接收到的客户端数据进行处理
272                         }
273                  }
274              }
275              else if( wsaNetWorkEvent.lNetworkEvents & FD_CLOSE )
276              {
277                 if ( wsaNetWorkEvent.iErrorCode[FD_CLOSE_BIT] != 0 )
278                 {
279                     //occur error
280                     closesocket( clientSock );
281                 }
282                 else
283                 {
284                     closesocket( clientSock );
285                 }  
286              }
287         }
288     }
289 
290     if ( recvBuffer != NULL )
291     {
292         free( recvBuffer );
293     }
294 复制代码
295  
296 
297      该模型通过一个死循环里面调用WSAWaitForMultipleEvents函数来等待客户端套接字对应的Event的到来,一旦事件通知到达,就通过该套接字去接收数据。虽然WsaEventSelect模型的实现较前两种方法复杂,但它在效率和兼容性方面是最好的。
298 
299     以上三种模型虽然在效率方面有了不少的提升,但它们都存在一个问题,就是都预设了只能接收64个客户端连接,虽然我们在实现时可以不受这个限制,但是那样,它们所带来的效率提升又将打折扣,那又有没有什么模型可以解决这个问题呢?我们的下一篇重叠I/0模型将解决这个问题
View Code

 5、为什么多线程能提高效率