进程、线程、和程序的概念和他们之间的区别:
进程的产生方式,fork()、system()、exec()函数等。
linux进程间的通讯和同步方式,包括管道pipe、命名管道fifo、信号量sem、共享缓冲区shm、消息队列msg,以及信号signal。
1.进程:从用户的角度来看是应用程序的一个执行过程。
从操作系统核心角度来看,进程代表的是操作系统分配内存,CPU时间片等资源的基本单位,是为正在运行的程序提供的运行环境。
2.进程和应用程序的区别
区别在于应用程序作为一个静态文件存储在计算机系统的硬件扥存储空间中,而进程则是出于动态条件下由操作系统维护的系统资源管理实体。
3.进程概念和程序概念最大的不同之处在于:
a)进程是动态的,而程序是静态的
b)进程有一定的生命期,而程序是指令的集合,本身无运动的意义
c)一个进程只能对应一个程序,一个程序可以对应多个进程
4.进程产生的过程
a)首先赋值父进程的环境配置
b)在内核中建立进程结构
c)将结构插入到进程列表,便于维护
d)分配资源给此进程
e)复制父进程的内存映射信息
f)管理文件描述符和连接点
g)通知父进程
5.进程的终止方式
a)从main返回
b)调用exit
c)调用_exit
d)调用abort
e)由一个信号终止
6.进程之间的通信
a)管道:是unix族中进程通讯的最古老的方式,它利用内核在两个进程之间建立通道,它的特点是与文件的操作类似,仅仅在管道的一端只读,另一端只写。利用读写的方式在进程之间传递数据。
b)共享内存:将内存中的一段地址,在多个进程之间共享,多个进程利用获得的共享内存的地址来直接对内存进行操作。
c)消息队列:在内核中建立一个链表,发送方按照一定的表示将数据发送到内核中,内核将其放入量表后,等待接收方的请求。接收方发送请求后,内核按照消息的标识,从内核中将消息从链表中摘下,传递给接收方。消息队列是一种完全的异步操作。
7.进程之间的同步
多个进程之间需要协作完成任务时,经常发生任务之间的依赖现象,从而出现了进程的同步问题。linux下进程的同步方式主要有消息队列、信号量等。
信号量是一个共享的标识数量的值,用于多个进程之间操作或者共享资源的保护,他是进程之间同步的最主要方式。
8.进程和线程
a)进程是操作系统进行资源分配的基本单位,进程拥有完整的虚拟空间。进行系统资源分配的时候,除了cpu资源之外,不会给线程分配独立的资源,线程所需要的资源需要共享。
b)线程是进程的一部分,如果没有进行显式的线程分配,可以认为进程是单线程的;如果进程中建立了线程,则可以认为系统是多线程的。
c)多线程和多进程是两种不同的概念,虽然二者都是并行完成功能。但是,多个线程之间像内存、变量等资源可以通过简单的办法共享,多进程则不同,进程间的共享方式有限。
d)进程有进程控制表,系统通过PCB对进程进行调度;线程有线程控制表TCB。但是,TCP所表示的状态比PCB要少得多。
9.进程产生的方式
进程是计算机运行的基本单位,要产生一个进程,有多种产生方式,例如使用fork()、system()、exec()函数等。这些函数的不通过在于其运行环境的构造之间存在差别。但是本质都是对程序运行的各种条件进行配置,在系统之间建立一个可以运行的程序。
a)进程号
每个进程在初始化的时候,系统分配一个id号,用于标识此进程。在linux中进程号是唯一的,描述进程的id号也叫做pid,即进程id(process id).pid的变量类型为pid_t。
getpid():返回当前进程的id号。
getppid():返回当前进程的父进程的id号。
b)进程复制fork()
fork()函数以父进程为蓝本复制一个进程,其id号和父进程id号不同。
fork()的特点是是执行一次,返回两次。在父进程和子进程中返回的是不同的值,父进程返回的是子进程的id号,而子进程则返回0.
c)system()
system()调用“/bin/sh-c command"执行特定的命令,阻塞打过去进程直到connand命令执行完毕.
执行system()函数时,会调用fork()、execve()、waitpid()等函数,其中任意一个调用失败,将导致system()函数调用失败。
d)进程执行exec()函数系列
在使用fork()函数和system()函数的时候,系统会建立一个新的进程,执行调用者的操作,而原来的进程还会存在,直到用户显式的退出。而exec()族的函数会用新进程替代原进程,系统会从新的进程运行,新进程的pid和原进程的pid相同.
exec()函数执行成功后不会返回,因为新执行的程序已经占用旧的程序了。
使用exec()函数比较普遍的一只方法是先使用fork()函数分叉进程,然后在新的进程中调用exec()函数,这样exec()函数会占用原来一样的系统资源来运行。
10.所有用户态进程的产生进程init
a)linux操作系统下的每一个进程都有一个父进程或者兄弟进程,并且有自己的子进程。
b)init进程是所有进程的祖先,其他的进程都是由init进程直接或者间接fork()出来的。
11.进程间通信和同步
在linux下有多种进程间通信的方法:半双工管道、fifo(命名管道)、消息队列、信号量、共享内存等。使用这些机制可以为linux下的网络服务器开发提供灵活而又坚固的框架。
a)半双工管道
是一种把两个进程之间的标准输入和标准输出链接起来的机制。在shell中管道用"|"表示。
pipe()函数(int pipe(int filedes[2])):filedes一个元素是为了读操作而创建和打开的,第二个元素(下标为1)是为了写操作而打开的。
管道阻塞和管道操作的原子性
b)命名管道(int fifo(const char *pathname, mode_t mode))
在文件系统中命名管道是以设备特殊文件的形式存在的
不同的进程可以通过命名管道共享数据
对于命名管道fifo来说,io操作与普通的管道io操作基本上是一样的,二者之间存在着一个主要区别。在fifo中,必须使用一个open()函数来显式的建立连接到管道的通道。
一般来说fifo总是处于阻塞状态。
c)消息队列
ftok()函数(key_t ftok(const char *pathname, int proj_id)):这个函数将路径名和项目的表示符转变为一个系统的ipc值.
msgget()函数(int msgget(key_t key, int msgflg):创建一个新的消息队列,或者访问一个现有的队列。
msgsnd()函数(int msgsnd(int msqid, const void *msgq, size_t msgsz, int msgflg)):向队列传递信息
msgrcv()函数(ssize_t msgrcv(int msqid,void *msgp, size_t msgsz, long msgtyp, int msgflg)):消息队列接收操作
msgctl()函数(int msgctl(int msqid, int cmd, struct msqid_ds *buf)):向内核发送一个cmd命令,内核根据此来判断进行何种操作。
d)信号量
信号量是一种计数器,用来控制对多个进程共享的资源所进行访问,他们常被用作一个锁机制,在某个进程正在对特定资源进行操作时,信号量可以防止另一个进程去访问它。
CreateSem():安装用户的键值生成一个信号量。
semget():用于创建一个新的信号量集合,或者访问现有的集合。
semop():信号量的p、v操作时通过向已经建立好的信号量(使用semget()函数),发送命令来完成的。向信号量发送命令的函数时semop()。
semctl():信号量的执行控制操作。
e)共享内存
最快捷的方式,因为共享内存方式的通讯没有中间过程,而管道、消息队列等方式则是需要将数据通过中间机制进行转换,与此相反,共享内存方式直接将某段内存段进行映射,多个进程共享内存是同一块物理空间。
shmget():创建共享内存,或者访问一个现有的共享内存段。
shmat():用来获取共享内存的地址,获取共享内存成功后,可以像使用通用内存一样对其进行操作。
shmdt():用于删除一段共享内存。
shmctl():共享内存控制函数。
f)信号(signal)
最古老的进程之间的通信机制,它用于在一个或多个进程之间传递异步信号。
signal():用于截取系统信号,对此信号挂接用户自己的处理函数。
kill()和raise():向进程发送信号函数。
12.linux下的线程
a)int pthread_create(pthread_t *thread,pthread_attr_t *attr, void*(*start_routine)(void*),void *arg)
创建线程,成功返回0,失败返回非0
b)线程结束函数
extern int pthread_join _p(pthread_t _th, void **_thread_return)
extern void pthread_exit _p(void * retval, attributr _noreturn)
c)线程的属性
属性结构:pthread_attr_t
线程的优先级
pthread_attr_getschedparam()->获得线程的优先级设置
pthread_attr_setschedparam()->设置线程的优先级
线程的绑定状态
pthread_attr_setscope()->设置线程绑定状态
线程的分离状态
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
d)线程互斥
互斥初始化函数:pthread_mutex_init()
互斥锁定函数:pthread_mutex_lock()
互斥预锁定函数:pthread_mutex_trylock()
互斥解锁函数:pthread_mutex_nulock()
互斥销毁函数:pthread_mutex_destroy()
e)线程中使用信号量
信号量实际上是一个非负的整数计数器,用来实现对公共资源的控制。
初始化:sem_init()
销毁:sem_destroy()
增加:sem_post()
减少:sem_wait()
资源是否可用判断:sem_trywait()
linux用户层网络编程
1.TCP/IP协议族简介
ISO/OSI的网络模型架构
TCP/IP网络模型
Internet协议,即IP协议
TCP/IP模型中的TCP和UDP协议及ICMP协议
地址解析协议ARP
IP地址的组成,掩码及建网时如何进行子网划分及端口的含义
主机字节序和网络字节序
a)OSI分层结构:
物理层:规定物理线路和设备的触发、维护、关闭物理设备的机械特性、电气特性、功能特性和过程。
数据链路层:提供可靠的数据传输,利用通信信道实现无差错传输,提供物理地址寻址、数据层帧、数据的检测重发、流量控制和链路控制等功能。
网络层:负责将各个子网之间的数据进行路由选择,将数据从一个主机传送到另一个主机,其功能包括网际互联、流量控制和拥塞控制等。
传输层:将上层的数据处理为分段的数据,提供可靠或不可靠的传输。
会话层:管理主机之间的会话过程,包括会话的建立、终止和会话过程管理。
表示层:对网络数据进行变换,使得多个主机之间传送的信息能够相互理解,包括数据的压缩、加密、格式的转换等。
应用层:为应用程序提供访问网络接口,为用户提供常用的应用。
b)TCP/IP协议栈参考模型
网络接口层
网际层:IP协议
传输层:TCP/UDP
应用层:
C)主机字节序和网络字节序
小端字节序:将数据的最低字节放在内存的低位置,如:X86架构的intel产品。
大端字节序:将数据的高字节放在内存的低位置
网络字节序转换:
网络字节序标准规定为大端字节序。
htons():表示对于short类型的变量,从主机字节序转换为网络字节序
ntohs():表示对于long类型的变量,从网络字节序转换为主机字节序
htonl():表示对于long类型的变量,从主机字节序转换为网络字节序
ntohl():表示对于long类型额变量,从网络字节序转换为主机字节序
应用层网络服务简介
1.http协议和服务
TCP网络基础编程
套接字地址结构、内核和应用层之间的内存数据传递方式
TCP网络编程函数:socket(),bind(),listen(),accept(),connect(),close()和利用read()和write()函数进行数据接受和发送。
1.套接字编程基础知识
a)套接字的地址结构
通用:
struct sockaddr
{
sa_family_t sa_family; //协议族 unsigned short
char sa_data[14]; //协议族数据
}
常用:
struct scokaddr_in
{
u8 sin_len; //结构体长度
u8 sin_family; //协议族
u16 sin)_port; //端口
struct inaddr sinaddr; //ip地址
char sin_zero[8]; //未用
}
IP地址结构:
struct in_addr
{
u32 s_addr; //32位ip地址,网络字节序
}
b)TCP网络编程流程
TCP编程主要为C/S模式,c-客户端、s-服务器。
服务器端程序设计模式:
socket()套接字初始化:成功返回套接字描述符,失败返回-1
bind()套接字与端口绑定:返回0表示成功,-1返回失败
listen()设置服务器的侦听连接:初始化服务器可连接队列,成功返回0,是否返回-1。
accept()接受客户端连接:成功返回一个新的套接字文件描述符来表示客户端的连接,客户端连接的信息可以通过这个新描述符来获得的。
read()和write()接收和发送数据
close()。套接字的关闭
客户端程序设计模式
socket()套接字初始化
connect()连接服务器
read()、write()读写网络数据
close()套接字关闭
2.服务器和客户端信息的获取
网络字节序和主机字节序的概念
字符串ip和二进制ip地址之间的转换函数,例如inet_aton()、inet_ntia()、inet_addr()等。并介绍协议无关的转换函数,例如:inet_pton(),inet_ntop()。
如何使用gethostbyname()和gethostbyaddr()函数获得目标主机的信息。
协议处理函数,例如:getprotobyname()、getprotobyaddr()函数等。
a)字节序:
b)字符串ip地址和二进制ip地址的转换
int inet_aton(const char *cp,struct in_addr *inp); //将点分四段式的ip地址转换为地址结构in_addr值
in_addr_t inet_addr(const char *cp); //将字符串转换为in_addr值
in_addr_t inet_network(const char *cp); //字符串地址的网络部分转为in_addr类型
char* inet_ntoa(struct in_addr in); //将in_addr结构地址转为字符串
struct in_addr inet_makeaddr(int net,int host); //将网络地址和主机地址合成为ip地址
in_addr_t inet_lnaof(struct in_addr in) //获得地址的主机部分
in_addr_t inet_netof(struct in_addr in) //获得地址的网络部分
int inet_pton(int af, const char *src, void *dst); //将字符串类型的ip地址转换为二进制类型。第一个参数af表示网络类型的协议族,在ipv4下的值AF_INET;第2个参数src表示需要转换的字符串;第三个参数dst指向转换后额结果,在IPV4下,dst指向结构 struct in_addr的指针。
const char* inet_ntop(int af, const void *src, char *dst, socklen_t cnt); //将二进制的网络ip地址转换为字符串。第一个参数表示网络类型的协议族,在IPV4下的值为AF_INET;第二个参数src为需要转换的二进制ip地址,在IPV4下,src指向一个struct in_addr结构类型的指针;第三个参数dst指向保存结果缓冲区的指针;第四个参数cnt的值时dst缓冲区的大小。
3.数据的io和复用
a)IO函数
ssize_t recv(int s, void* buf, size_t len, int flags); //从内核的接收缓冲区中赋值数据到用户指定的缓冲区
ssize_t send(int s,const void* buf, size_t len, int flags); //
ssize_t readv(int s,const struct iovec* vector,int count); //接收多个缓冲区数据
ssize_t writev(int fd, const struct iovec* vector, int count); //向多个缓冲区同时写入数据
ssize_t recvmsg(int s, struct msghdr *msg, int flags); //从套接字s中接收数据放到缓冲区
ssize_t sendmsg(int s, count struct msghdr *msg, int flags); //向多个缓冲区发送数据
b)IO函数的比较
read()/write()和readv()/writev()可以对所有文件描述符使用
recv()/send()、recvfrom()/writeto()和recvmsg()/sendmsg()只能操作套接字描述符
readv()、writev()和recvmsg()/sendmsg()可以操作多个缓冲区
read()/write()、recv()/send()、recvfrom()、sendto()只能操作单个缓冲区
recv()/send()、recvfrom()、sendto()、recvmsg/sendmsg()具有可选标志
recvfrom()、sendto()、recvmsg/sendmsg()可以选择对方ip地址
recvmsg()/sendmsg()有可选择的控制信息,能进行高级操作。
4.IO模型
a)阻塞io:这种模型进行数据接收的时候,在数据没有到之前程序会一只等待。
b)非阻塞io:对每次请求,内核不会阻塞,会立即返回,当没有数据的时候,会返回一个错误。
c)io复用:等待的时候加入超时的时间,当超时时间没有到达的时候与阻塞的情况一致,而当超时时间到达任然没有数据接受到,系统会返回,不再等待。
d)信号驱动io模型:在进程开始的时候注册一个信号处理的回调函数,进程继续执行,当信号发生时,即有了io的时间,这里就有数据到来,利用注册的回调函数将到来的数据用recvfrom接受到。
e)异步io模型
5.select()函数和pselect()函数