Linux应用编程&网络编程

  1. 常用Linux API和c标准库函数
    常用【shell】
    stat :查看文件属性信息
    chmod :权限修改(root用户可用)
    chown :属主修改
    umask :设定我们系统中新创建的文件的默认权限的。
    > :重定位命令 ,相当于API的dup
    file : 查看文件信息
    export :命令查看环境变量
    ps (-a/aux/ajx):查看进程相关
    kill :-信号编号 进程ID,向一个进程发送一个信号

常用【LinuxAPI】&【c标准库】::
文件相关:
open、close、write、read、lseek(移动文件描述符)
dup2和dup:复制文件描述符
fcntl:多功能文件管理
ctime :从time_t出发想得到字符串格式的时间
localtime :把time得到的秒数变成一个struct tm结构体表示的国际时间
gmtime :把time得到的秒数变成一个struct tm结构体表示的计算机设置的所在时区本地时间
mktime :用来完成相反方向的转换(struct tm到time_t)
asctime :从struct tm出发想得到字符串格式
strftime :从struct tm出发想得到字符串格式
rand :函数可以返回一个伪随机数序列(使用前需要设置种子,种子默认是1)
srand :函数用来设置rand获取的伪随机序列的种子
atexit:注册进程终止处理函数,注册的多个进程终止处理函数,先注册的后执行
文件属性
stat、fstat、lstat(可查符号链接本身) :查看文件属性信息
access :测试得到当前执行程序的那个用户在当前那个环境下对目标文件是否具有某种操作权限。
chmod/fchmod :权限修改
chown/fchown/lchown :属主修改
umask :新创建的文件的默认权限
目录文件
opendir 打开一个目录后得到一个DIR类型的指针给readdir使用
readdir 函数调用一次就会返回一个struct (包含了目录信息)
时间相关:
time :从jjffies得到当前时间,返回从1970-01-01 00:00:00 +0000(UTC)过去的秒数
gettimeofday :返回的时间是由struct timeval和struct timezone这两个结构体来共同表示。timeval表示时间,而timezone表示时区
settimeofday :用来设置当前的时间和时区的
正常终止:
return、exit、_exit
环境变量:
getenv:获取指定环境变量
进程相关:
getpid :获取当前进程
getppid :获取父进程
getuid :当前用户ID
geteuid :当前组id
getgid :有效用户id
getegid :有效组id
setsid :将当前进程设置为一个新的会话期session
chdir() :设置当前进程工作目录
多进程相关:
fork :创建子进程,fork后就有了一个子进程了,返回值为0的那份就是子进程,父进程的fork会返回子进程的进程ID
wait :用来回收子进程(阻塞)
waitpid :可以回收指定PID的子进程,可以阻塞式或非阻塞式
exec族函数:用来运行一个可执行程序
execl和execv :最基本的exec,都可以用来执行一个程序,区别是传参的格式不同。必须指定可执行程序的全路径
execlp和execvp :会先去环境变量PATH所指定的目录下去找,然后去去路径下找,执行程序。
execle和execvpe :执行可执行程序时会多传一个环境变量的字符串数组给待执行的程序。
system :函数 = fork+exec。原子操作。整个操作一旦开始就会不被打断的执行完,不会引起竞争态。
main函数的原型也可以是int main(int argc, char **argv, char **env),第三个参数为当前环境变量的拷贝。
进程间通讯:
pipe、write、read、close:管道通信的函数;只能在父子进程间通信
mkfifo、open、write、read、close:有名管道,任意2个进程都可
信号相关:
signal:指定信号编号处理信号的函数
sigaction:更具有可移植性,参数不是函数指针而是sigaction结构体指针
alarm函数:闹钟API,指定时间到后会有SIGALARM信号
pause函数:让当前进程暂停运行,交出CPU给其他进程去执行。(阻塞需要被信号唤醒。)
并发式IO:
select 和poll函数实现:外部阻塞式,内部非阻塞式自动轮询多路阻塞式IO
线程:
pthread_create :主线程 创建线程
pthread_join :主线程 阻塞等待回收子线程
pthread_detach :主线程分离子线程,主线程不再回收,子线程自己回收资源
pthread_cancel 一般都是主线程调用该函数去取消子线程
pthread_setcancelstate 子线程设置自己是否允许被取消
pthread_setcanceltype 子线程设置自己是被取消的模式(立即或其他)
pthread_exit与return退出 正常线程退出一般调用pthread_exit
pthread_cleanup_push 在线程中保存锁、等资源时,需要压进相应的解锁或其他函数,在线程被取消或是终止时,压进的内容会被执行
pthread_cleanup_pop 参数可以让压进的资源弹出执行或是不执行
pthread_self 获取线程id
线程同步:
信号量
sem_init :初始化信号量
sem_destroy :销毁一个信号量
sem_post :激活信号量
sem_wait :子线程判断信号量是否被激活
互斥锁
pthread_mutex_init 初始化一个互斥锁
pthread_mutex_destroy 销毁一个互斥锁
pthread_mutex_lock 上锁
pthread_mutex_unlock 解锁
条件变量
pthread_cond_init 初始化一个条件变量
pthread_cond_destroy 销毁一个条件变量
pthread_cond_wait 等待一个条件变量成立,否则阻塞
pthread_cond_signal 发条件变量,可激活一个线程
pthread_cond_broadcast 发条件变量,可激活多个线程
网络编程
建立连接
socket :类似于open,用来打开一个网络连接
bind :服务器绑定一个socket到ip地址
listen :服务器进行监听设置
accept :阻塞等待客户端接入,通过这个和客户端进行读写操作
connect :客户端连接到服务器的ip
发送和接收
send和write :发送
recv和read :接收
守护进程(任何一个进程都可以将自己实现成守护进程)
create_daemon函数要素:子进程等待父进程退出;子进程使用setsid创建新的会话期,脱离控制台;调用chdir将当前工作目录设置为/;umask设置为0以取消任何文件权限屏蔽;关闭所有文件描述符;将0、1、2定位到/dev/null

文件类型:
  1. linux文件io
    1.1 基于linux去做应用编程
    通过调用linux的系统API来实现应用需要完成的任务
    1.2. 文件操作API
    (1)API是一些函数,这些函数是由linux系统提供支持的,由应用层程序来使用。
    (2)应用层程序通过调用API来调用操作系统中的各种功能。
    (3)学习一个操作系统,就是学习使用这个操作系统的API。
    linux常用文件IO接口:open、close、write、read、lseek
    文件操作:
    linux系统中要操作一个文件,先open打开一个文件(如果打开本身失败,后面就不用操作了),得到一个文件描述符,然后对文件进行读写操作(或其他操作),最后close关闭文件
    文件描述符:
    文件描述符其实实质是一个数字,在一个进程中表示一个特定的含义,当我们open打开一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,就是用来区分一个程序打开的多个文件的。文件描述符的作用域就是当前进程
    1.3. 简单文件读写
    【open】文件:linux中的文件描述符fd的合法范围是0或者一个正正数,不可能是一个负数。open返回的fd程序必须记好,这个文件所有操作都要fd去对应这个文件,关闭文件时也需要fd去指定关闭这个文件。
    【read】文件内容:ssize_t read(int fd, void buf, size_t count);
    fd表示要读取哪个文件,一般由前面的open返回得到,buf是应用程序自己提供的一段内存缓冲区,用来存储读出的内容,count是我们要读取的字节数,返回值ssize_t类型是linux内核用typedef重定义的一个类型(int),返回值表示成功读取的字节数。
    【write】文件内容:用write系统调用,write的原型和理解方法和read相似
    【close】文件:参数为fd
    ----open函数的flag:
    读写权限:【O_RDONLY】表示只读打开,【O_WRONLY】表示只写打开,【O_RDWR】表示可读可写打开
    【O_APPEND】、【O_TRUNC】:O_TRUNC属性去打开文件时,如果这个文件中本来是有内容的,则原来的内容会被丢弃。O_APPEND属性去打开文件时,如果这个文件中本来是有内容的,则新写入的内容会接续到原来内容的后面。若同时出现O_TRUNC起作用。
    exit、_exit、_Exit退出进程,正式终止进程(程序)应该使用exit或者_exit或者_Exit之一。
    打开不存在的文件时:【O_CREAT、O_EXCL】
    open中加入O_CREAT后,不管原来这个文件存在与否都能打开成功,如果原来这个文件不存在则创建一个空的新文件,如果原来这个文件存在则会重新创建这个文件,原来的内容会被消除掉
    O_EXCL标志和O_CREAT标志来结合使用,没有文件时创建文件,有这个文件时会报错提醒我们。可用open第三个参数[mode]来指定要创建的文件的权限。mode使用4个数字来指定权限的如0777
    1.4 阻塞与非阻塞(只用在设备文件,而不是普通文件)
    如果一个函数是阻塞式的,则调用这个函数时当前进程有可能被卡住,函数被阻塞住了就不能立刻返回;如果一个函数是非阻塞式的那么我们调用这个函数后一定会立即返回,但是函数有没有完成任务不一定。
    阻塞式的结果有保障但是时间没保障;非阻塞式的时间有保障但是结果没保障。
    操作系统提供的API和由API封装而成的库函数,有很多本身就是被设计为阻塞式或者非阻塞式的
    打开文件默认是阻塞式的,要以非阻塞的方式打开文件,则open中要加【O_NONBLOCK】标志
    open的【O_SYNC】标志:无它时,write只是将内容写入底层缓冲区而不是硬件,有这个标志会阻塞等待底层写入硬件才返回
    1.5 文件读写细节
    【errno】和【perror】:
    linux系统中对各种常见错误做了个编号,当函数执行错误时,函数会返回一个特定的errno编号来告诉我们这个函数到底哪里错了。
    errno是由OS来维护的一个全局变量(编号),任何OS内部函数都可以通过设置errno
    linux系统提供了一个函数perror,函数内部会读取errno并将这个编号对应的错误信息字符串输出打印
    read和write的count
    count参数表示我们想要写或者读的字节数,返回值表示实际完成的要写或者读的字节数。实现的有可能等于想要读写的,也有可能小于
    要读取或者写入的是一个很庞大的文件,不可能把count设置为2
    1024*1024,而应把count设置为一个合适的数字(譬如2048、4096),通过多次读取来实现全部读完
    文件IO效率和标准IO
    文件IO就指的是API函数构成的一套用来读写文件的体系
    应用层C语言库函数提供了一些用来做文件读写的函数列表,叫标准IO,标准IO函数其实是由文件IO封装而来的。标准IO加了封装之后主要是为了在应用层添加一个缓冲机制,标准IO库自己根据操作系统单次write的最佳count来选择好的时机来完成write到内核中的buf
    1.6 linux如何管理文件
    硬盘中的静态文件和inode(i节点)
    文件平时都在存放在硬盘中的,硬盘中存储的文件以一种固定的形式存放的,叫静态文件。
    硬盘中可以分为两大区域:
    硬盘内容管理表项:以文件为单位记录了各个文件的各种信息,每一个文件有一个信息列表(我们叫inode,i节点,其实质是一个结构体,这个结构体有很多元素,每个元素记录了这个文件的一些信息,其中就包括文件名、对应的扇区号、块号·····)
    内容存储的区域
    硬盘管理的时候是以文件为单位的,每个文件一个inode,每个inode有一个数字编号,对应一个结构体,结构体中记录了各种信息。操作系统最初拿到的信息是文件名,最终得到的是文件内容。
    内存中被打开的文件和vnode(v节点)
    一个程序的运行就是一个进程,每个进程都有一个数据结构用来记录这个进程的所有信息【进程信息表】,表中有一个指针会指向一个文件管理表,文件管理表中记录了当前进程打开的所有文件及其相关信息。
    【文件管理表】中用来索引各个打开的文件的index就是文件描述符fd,我们最终找到的就是一个已经被打开的文件的管理结构体vnode
    我们只要知道这个文件的fd,就可以很容易的找到这个文件的vnode进而对这个文件进行各种操作。
    文件与流:
    【(IO)流】(stream):文件被读出/写入时都只能一个字符一个字符的进行,一个文件中N多的个字符被挨个一次读出/写入时,这些字符就构成了一个字符流。
    一般都是IO相关的,常叫IO流。文件操作时就构成了一个IO流。
    1.7 lseek详解
    打开读写一个文件时,操作的是动态文件,在内存中以文件流形式存在
    动态文件中,我们会通过文件指针来表征这个正在操作的位置。
    【文件指针】是我们文件管理表这个结构体里面的一个指针。是vnode中的一个元素。这个指针表示当前我们正在操作文件流的哪个位置。这个指针【不能被直接访问】
    linux系统用【lseek】函数来访问这个文件指针。
    write和read函数本身自带移动文件指针的功能。read和write函数都是从当前文件指针处开始操作的
    linux中并没有一个函数可以直接返回一个文件的长度,利用lseek来写一个函数得到文件长度即可。
    lseek构建空洞文件:
    空洞文件就是这个文件中有一段是空的,普通文件中间是不能有空的,用lseek往后跳过一段,再write写入一段,就会构成一个空洞文件。
    空洞文件方法对多线程共同操作文件是及其有用的。有时候我们创建一个很大的文件,如果从头开始依次构建时间很长。有一种思路就是将文件分为多段,然后多线程来操作每个线程负责其中一段的写入。(如迅雷下载)
    1.8 多次打开同一文件,O_APPEND
    重复打开同一文件读写
    一个进程中两次打开同一个文件,然后分别读取。一种是fd1和fd2分别读写,第二种是接续读。实际是分别读取fd1和fd2所对应的文件指针是不同的2个独立的指针。
    有时候我们希望接续写而不是分别写,在open时加O_APPEND标志即可
    加了O_APPEND,fd1和fd2还是各自拥有一个独立的文件指针,但是这两个文件指针关联起来了
    O_APPEND对文件指针的影响,对文件的读写是原子的。
    原子操作的含义是:整个操作一旦开始是不会被打断的,必须直到操作结束其他代码才能得以调度运行。每种操作系统中都有一些机制来实现原子操作
    1.9 文件共享实现方式
    文件共享就是同一个文件(同一个文件指的是同一个inode,同一个pathname)被多个独立的读写体,同时(一个打开尚未关闭的同时另一个去操作)操作。
    可以通过文件共享来实现多线程同时操作同一个大文件,以减少文件读写时间,提升效率。
    文件共享的3种实现方式
    第一种是同一个进程中多次使用open打开同一个文件
    第二种是在不同进程中去分别使用open打开同一个文件(这时候因为两个fd在不同的进程中,所以两个fd的数字可以相同也可以不同)
    第三种情况是linux系统提供了dup和dup2两个API来让进程复制文件描述符。
    文件共享时的核心关注点在于:分别写/读还是接续写/读.根据需要选择
    1.10. 文件描述符
    文件描述符的本质是一个数字,这个数字本质上是进程表中文件描述符表的一个表项,进程通过文件描述符作为index去索引查表得到文件表指针,再间接访问得到这个文件对应的文件表。
    文件描述符这个数字是open系统调用内部由操作系统自动分配的
    操作系统规定,fd从0开始依次增加。fd也是有最大限制的,文件描述符表其实就是一个数组,fd是index,文件表指针是value
    open时,内核会从文件描述符表中挑选一个最小的未被使用的数字给我们返回。
    fd中0、1、2已经默认被系统占用了,这三个文件分别叫stdin、stdout、stderr。
    printf函数其实就是默认输出到标准输出stdout上了。stdio中还有一个函数叫fpirntf,这个函数就可以指定输出到哪个文件描述符中。
    dup和dup2函数介绍:
    dup系统调用对fd进行复制,会返回一个新的文件描述符
    dup系统调用不能指定复制后得到的fd的数字是多少,dup2系统调用修复了这个缺陷
    dup2和dup的作用是一样的,都是复制一个新的文件描述符。但是dup2允许用户指定新的文件描述符的数字。
    命令行中重定位命令 【>】
    linux中的shell命令执行后,打印结果都是默认进入stdout的。定位的符号>把ls、pwd等命令的输出给重定位到一个文件中
    实现原理,其实就是利用open+close+dup,open
    1.11fcntl函数
    fcntl函数是一个多功能文件管理的工具箱,接收2个参数+1个变参。对文件的一些操作
    第一个参数是fd表示要操作哪个文件,第二个参数是cmd表示要进行哪个命令操作。变参是用来传递参数的,要配合cmd来使用。
    1.12标准IO库
    【标准IO】是【C库函数】,而【文件IO】是linux系统的【API】
    C库函数具有可移植性而API不具有可移植性。C语言库函数是由API封装而来的。
    性能上和易用性上看,C库函数一般要好一些。譬如IO,文件IO是不带缓存的,而标准IO是带缓存的,因此标准IO比文件IO性能要更高

  2. 文件属性
    2.1 linux文件类型(一切皆文件)
    普通文件【-】:文本文件&二进制文件
    文本文件:字符集合的文件。本质内容是数字。如.h .c .txt等文件
    二进制文件:存储的也是数字。常见的是可执行程序
    区分:linux操作系统不区分,只能本身知道文件类型,使用该文件的用法去用。有时候用后缀名人为标记
    目录文件【d】:linux下目录是一种特殊文件
    内容包括 文件路径 文件列表等等
    linux有API来操作目录文件
    设备文件:字符设备文件【c】&块设备文件【b】
    设备文件对应硬件设备,并不是真正在硬盘上的文件,文件系统虚拟制造。
    设备文件需要使用API产生或使用。
    管道文件【p】:通讯手段
    套接字文件【s】:网络编程使用
    符号链接文件【l】:软硬链接
    2.2 文件属性
    linux shell命令【stat】:可得到文件属性
    API函数:stat、fstat、lstat 文件属性获取
    stat这个API的作用就是让内核将我们要查找属性的文件的属性信息结构体的值放入我们传递给stat函数的buf中,当stat这个API调用从内核返回的时候buf中就被填充了文件的正确的属性信息
    stat是从文件名出发得到文件属性信息结构体,而fstat是从一个已经打开的文件fd出发得到一个文件的属性信息。
    对于符号链接文件,stat和fstat查阅的是符号链接文件指向的文件的属性,而lstat查阅的是符号链接文件本身的属性。
    struct stat结构体:在中声明
    这个结构体中的所有元素加起来就是我们的文件属性信息。
    文件权限:属主(owner、user)、组(group)、其他用户(others)
    access API函数:测试得到当前执行程序的那个用户在当前那个环境下对目标文件是否具有某种操作权限。
    chmod shell:用来修改文件的各种权限属性。只有root用户才有权利去执行修改。
    chmod的API:修改文件的各种权限属性
    chown shell: 修改文件属主
    chown/fchown/lchown API:修改文件属主
    umask与文件权限掩码:用来设定我们系统中新创建的文件的默认权限的。
    umask命令就是用umask API实现的
    opendir与readdir API函数:读取目录文件
    readdir调用一次只能读出一个目录项,要想读出目录中所有的目录项必须多次调用readdir函数。readdir函数内部户记住哪个目录项已经被读过了哪个还没读,所以多次调用后不会重复返回已经返回过的目录项。当readdir函数返回NULL时就表示目录中所有的目录项已经读完了。
    可重入函数:有些函数是可重入的有些是不可重入的
    一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;
    不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。
    C库,提供了对应的可重复版本(一般是不可重入版本函数名_r)

  3. 获取系统信息
    时间:
    GMT时间:(以前使用)国际时间(格林尼治)。一般一个国家统一时间统一使用一个当地时间
    UTC时间:UTC时间是0时区时间,Date: Fri, 08 Nov 2002 09:42:22 +0800 【表示一个北京时间9:42】
    【UTC+时区差=本地时间】UTC +0800就表示背景时区时间(东八区)
    点时间和段时间:时间点 和 时间段 段时间=时间点-时间点
    定时器(timer)与实时时钟(RTC): 定时器定的是段时间,RTC是和点时间相关的
    linux系统中的时间
    jiffies:linux内核的全局变量,jiffies记录以(内核节拍)为单位长度的数值。
    内核配置了一个节拍时间,linux内核调度系统工作时,以节拍时间为时间片的。
    jiffies-jiffies开机的基准值=开机经过了多少节拍 *每个节拍的时间 =开机了多长时间
    内核开机时会读取RTC硬件获取一个时间作为初始基准时间,对应一个jiffies值。(RTC时间-1970-01-01 00:00:00(UTC),再换算为jiffies值作为基准值)。系统运行一个节拍,就会将jiffies+1
    当前时间=jiffies对应的时间点+1970-01-01 00:00:00(UTC)
    一个时钟节拍取决于系统配置,一般10ms或1ms,就是调度时间。内核用Hz表示
    time :从jjffies得到当前时间,返回从1970-01-01 00:00:00 +0000(UTC)过去的秒数
    ctime :从time_t出发想得到字符串格式的时间
    localtime :把time得到的秒数变成一个struct tm结构体表示的国际时间
    gmtime :把time得到的秒数变成一个struct tm结构体表示的计算机设置的所在时区本地时间
    mktime :用来完成相反方向的转换(struct tm到time_t)
    asctime :从struct tm出发想得到字符串格式
    strftime :从struct tm出发想得到字符串格式
    gettimeofday :返回的时间是由struct timeval和struct timezone这两个结构体来共同表示。timeval表示时间,而timezone表示时区
    settimeofday :用来设置当前的时间和时区的
    随机数:
    真正的完全随机的数列是不存在的,只是一种理想情况。我们平时要用到随机数时一般只能通过一些算法得到一个伪随机数序列。
    rand函数可以返回一个伪随机数序列(使用前需要设置种子,种子默认是1)
    srand函数用来设置rand获取的伪随机序列的种子
    操作系统级别的调试
    (1)简单程序单步调试
    (2)复杂程序printf打印信息调试
    (3)框架体系日志记录信息调试
    (4)内核调试的
    proc文件系统内核调试:
    在内核中构建一个虚拟文件系统/proc,内核运行时将内核中一些关键的数据结构以文件的方式呈现在/proc目录中的一些特定文件中,这样相当于将不可见的内核中的数据结构以可视化的方式呈现给内核的开发者。
    通过实时的观察/proc/xxx文件,来观看内核中特定数据结构的值。来调试内核操作系统
    proc目录下的文件大小都是0,因为这些文件本身并不存在于硬盘中,不是一个真实文件,只是一个接口,去读取这个文件时,内核并不是去硬盘上找这个文件,而是映射为内核内部一个数据结构被读取并且格式化成字符串返回给我们。
    常用proc中的文件介绍
    (1)/proc/cmdline
    (2)/proc/cpuinfo
    (3)/proc/devices
    (4)/proc/interrupts
    使用:cat以手工查看
    程序中可以文件IO访问
    shell程序中用cat命令结合正则表达式来获取并处理内核信息
    sys文件系统:
    本质上和proc文件系统是一样的,都是虚拟文件系统(一个是/proc目录,另一个是/sys目录)
    /proc中的文件只能读,但是/sys中的文件可以读写。
    /sys是/proc的升级版,/sys目录使用有一定规则

  4. 进程
    main函数
    编译链接时的引导代码。操作系统下的应用程序其实在main执行前也需要先执行一段引导代码才能去执行main,我们写应用程序时不用考虑引导代码的问题,链接时由链接器将编译器中事先准备好的引导代码给连接进去和我们的应用程序一起构成最终的可执行程序。
    加载器是操作系统中的程序,当我们去执行一个程序时加载器负责将这个程序加载到内存中去执行这个程序。
    程序如何结束
    正常终止:return、exit、_exit
    非正常终止:自己或他人发信号终止进程
    atexit注册进程终止处理函数
    atexit注册多个进程终止处理函数,先注册的后执行
    return和exit效果一样,都是会执行进程终止处理函数,但是用_exit终止进程时并不执行atexit注册的进程终止处理函数。
    环境变量
    export命令查看环境变量
    每一个进程中都有一份所有环境变量构成的一个表格,也就是说我们当前进程中可以直接使用这些环境变量。进程环境表其实是一个字符串数组,用environ变量指向它。程序中通过environ全局变量使用环境变量
    获取指定环境变量函数getenv
    进程运行的虚拟地址空间
    操作系统中每个进程在独立地址空间中运行,每个进程的逻辑地址空间均为4GB(32位系统),0-1G为OS,1-4G为应用,虚拟地址到物理地址空间的映射
    意义:进程隔离,提供多进程同时运行
    进程:(动态过程而不是静态实物)程序的一次运行过程
    进程控制块(process control block),内核中专门用来管理一个进程的数据结构。
    进程ID(PID):shell 【ps (-a/aux)】可查看
    getpid :获取当前进程
    getppid :获取父进程
    getuid :当前用户ID
    geteuid :当前组id
    getgid :有效用户id
    getegid :有效组id
    多进程调度
    操作系统同时运行多个进程,宏观上的并行和微观上的串行,现代操作系统最小的调度单元是线程而不是进程
    fork创建子进程
    如果操作系统需要一个新进程来运行一个程序,那么操作系统会用一个现有的进程来复制生成一个新进程。老进程叫父进程,复制生成的新进程叫子进程。
    fork函数调用一次会在父进程返回一次,子进程返回一次,使用fork后然后用if判断返回值,并且返回值大于0时就是父进程,等于0时就是子进程。fork的返回值在子进程中等于0,在父进程中等于本次fork创建的子进程的进程ID。
    子进程继承父进程中打开的文件。父子进程各自独立打开同一文件实现共享(O_APPEND)
    父进程在没有fork之前自己做的事情对子进程有很大影响,但是父进程fork之后在自己的if里做的事情就对子进程没有影响了。fork内部实际上已经复制父进程的PCB生成了一个新的子进程,并且fork返回时子进程已经完全和父进程脱离并且独立被OS调度执行。子进程最终目的是要独立去运行另外的程序
    进程的诞生和消亡
    进程0和进程1,fork(复制生成一个子进程),vfork
    正常终止和异常终止
    进程在运行时需要消耗系统资源(内存、IO),进程终止时理应完全释放这些资源
    linux系统设计时规定:每一个进程退出时,操作系统会自动回收这个进程涉及到的所有的资源。但是操作系统只是回收了这个进程工作时消耗的内存和IO,而并没有回收这个进程本身占用的内存(8KB,主要是task_struct和栈内存),每个进程都需要一个帮助它收尸的人,这个人就是这个进程的父进程。
    僵尸进程:子进程先于父进程结束。子进程结束后父进程此时并不一定立即就能帮子进程“收尸”,在这一段子进程就被成为僵尸进程。(子进程除task_struct和栈外其余内存空间皆已清理)父进程可以使用wait或waitpid以显式回收子进程的剩余待回收内存资源并且获取子进程退出状态。父进程也可以不使用wait或者waitpid回收子进程,此时父进程结束时一样会回收子进程的剩余待回收内存资源。
    孤儿进程:父进程先于子进程结束,子进程成为一个孤儿进程。
    linux系统规定:所有的孤儿进程都自动成为一个特殊进程(进程1,也就是init进程)的子进程。
    父进程wait回收子进程:
    子进程结束时,系统向其父进程发送SIGCHILD信号,父进程调用wait函数后阻塞,父进程被SIGCHILD信号唤醒然后去回收僵尸子进程.父子进程之间是异步的,SIGCHILD信号机制就是为了解决父子进程之间的异步通信问题.若父进程没有任何子进程则wait返回错误
    wait函数:用来回收子进程(阻塞)
    waitpid函数:可以回收指定PID的子进程,可以阻塞式或非阻塞式
    竟态:竞争状态,多进程环境下,多个进程同时抢占系统资源(内存、CPU、文件IO)
    竞争状态对OS来说是很危险的,此时OS如果没处理好就会造成结果不确定。
    写程序时要尽量消灭竞争状态。操作系统给我们提供了一系列的消灭竟态的机制,我们需要做的是在合适的地方使用合适的方法来消灭竟态。
    【exec族函数】
    fork子进程是为了执行新程序.可以直接在子进程的if中写入新程序的代码,也可以使用exec族运行新的可执行程序
    典型的父子进程程序是这样的:子进程需要运行的程序被单独编写、单独编译连接成一个可执行程序,主程序为父进程,fork创建了子进程后在子进程中exec来执行子程序,达到父子进程分别做不同程序同时(宏观上)运行的效果。
    execl和execv
    最基本的exec,都可以用来执行一个程序,区别是传参的格式不同。execl是把参数列表(本质上是多个字符串,必须以NULL结尾)依次排列而成,execv是把参数列表事先放入一个字符串数组中,再把这个字符串数组传给execv函数。实际上应当至少传一个参数,参数0可以写为文件名或路径
    execlp和execvp
    上面2个执行程序时必须指定可执行程序的全路径(如果exec没有找到path这个文件则直接报错),而加了p的传递的可以是file(也可以是path,只不过兼容了file。加了p的这两个函数会先去环境变量PATH所指定的目录下去找,然后去去路径下找,如果找到则执行如果没找到则报错)
    execle和execvpe
    函数的参数列表较上面中多了一个字符串数组envp形参,执行可执行程序时会多传一个环境变量的字符串数组给待执行的程序。
    main函数的原型也可以是int main(int argc, char **argv, char **env),第三个参数为当前环境变量的拷贝。
    用户在执行这个程序时没有传递第三个参数,则程序会自动从父进程继承一份环境变量(默认的,最早来源于OS中的环境变量);如果我们exec的时候使用execlp或者execvpe去给传一个envp数组,则程序中的实际环境变量是我们传递的这一份(取代了默认的从父进程继承来的那一份)
    进程状态
    (1)就绪态。这个进程当前所有运行条件就绪,得到了CPU时间就能直接运行。
    (2)运行态。就绪态时得到了CPU就进入运行态开始运行。
    (3)僵尸态。进程已经结束但是父进程还没来得及回收
    (4)等待态(浅度睡眠&深度睡眠),进程在等待某种条件,条件成熟后可进入就绪态。等待态下给他CPU调度进程也无法执行。浅度睡眠等待时进程可以被(信号)唤醒,而深度睡眠等待时不能被唤醒只能等待的条件到了才能结束睡眠状态。
    (5)暂停态。暂停并不是进程的终止,只是被被人(信号)暂停了,还可以恢复的。
    进程状态切换-》图
    system函数 = fork+exec
    原子操作。原子操作意思就是整个操作一旦开始就会不被打断的执行完。原子操作的好处就是不会被人打断(不会引来竞争状态),坏处是自己单独连续占用CPU时间太长影响系统整体实时性,因此应该尽量避免不必要的原子操作,尽量使原子操作的时间缩短。
    进程关系
    (1)无关系
    (2)父子进程关系
    (3)进程组(group)由若干进程构成一个进程组
    (4)会话(session)会话就是进程组的组
    守护进程
    进程查看命令ps -xxx
    ps -ajx 偏向显示各种有关的ID号
    ps -aux 偏向显示进程各种占用资源
    向进程发送信号指令kill
    kill -信号编号 进程ID,向一个进程发送一个信号
    如:kill -9 xxx,将向xxx这个进程发送9号信号,也就是要结束进程
    常见守护进程
    syslogd,系统日志守护进程,提供syslog功能。
    cron,用来实现操作系统的时间管理,linux中实现定时执行程序的功能就要用到
    简单守护进程(任何一个进程都可以将自己实现成守护进程)
    create_daemon函数要素
    (1)子进程等待父进程退出
    (2)子进程使用setsid创建新的会话期,脱离控制台
    (3)调用chdir将当前工作目录设置为/
    (4)umask设置为0以取消任何文件权限屏蔽
    (5)关闭所有文件描述符
    (6)将0、1、2定位到/dev/null
    void create_daemon(void)
    {
    pid_t pid = 0;
    pid = fork();
    if (pid < 0)
    {
    perror(“fork”);
    exit(-1);
    }
    if (pid > 0)
    {
    exit(0); // 父进程直接退出
    }
    // 执行到这里就是子进程
    // setsid将当前进程设置为一个新的会话期session,目的就是让当前进程 脱离控制台。
    pid = setsid();
    if (pid < 0)
    {
    perror(“setsid”);
    exit(-1);
    }
    // 将当前进程工作目录设置为根目录
    chdir("/");
    // umask设置为0确保将来进程有最大的文件操作权限
    umask(0);
    // 关闭所有文件描述符
    // 先要获取当前系统中所允许打开的最大文件描述符数目
    int cnt = sysconf(_SC_OPEN_MAX);
    int i = 0;
    for (i=0; i {
    close(i);
    }
    open("/dev/null", O_RDWR);
    open("/dev/null", O_RDWR);
    open("/dev/null", O_RDWR);
    }
    使用syslog来记录调试信息(openlog、syslog、closelog)
    一般log信息都在操作系统的/var/log/messages这个文件中存储着,但是ubuntu中是在/var/log/syslog文件中的。
    操作系统中有一个守护进程syslogd(开机运行,关机时才结束),这个守护进程syslogd负责进行日志文件的写入和维护。任何需要写日志的进程都可以通过openlog/syslog/closelog这三个函数来利用syslogd提供的日志服务。这就是操作系统的服务式的设计。
    单例运行程序
    守护进程一般都是服务器,服务器程序只要运行一个就够了,多次同时运行并没有意义甚至会带来错误。
    最常用的一种方法就是:用一个文件的存在与否来做标志。具体做法是程序在执行之初去判断一个特定的文件是否存在,若存在则标明进程已经在运行,若不存在则标明进程没有在运行。然后运行程序时去创建这个文件。当程序结束的时候去删除这个文件即可。这个特定文件要古怪一点,确保不会凑巧真的在电脑中存在的
    IPC(进程间通讯):指的是2个任意进程之间的通信。
    每个进程都在独立的虚拟地址中运行,故不同的进程间通讯困难
    99%的程序是不需要考虑进程间通信的。因为大部分程序都是单进程的。常见的如GUI、服务器、大型程序才需要。
    IPC机制
    管道(无名管道):只能父子进程,半双工,类似读写文件
    (1)管道通信的原理:内核维护的一块内存,有读端和写端(管道是单向通信的)
    (2)管道通信的方法:父进程创建管理后fork子进程,子进程继承父进程的管道fd
    (3)管道通信的限制:只能在父子进程间通信、半双工(实际使用单工)
    (4)管道通信的函数:pipe、write、read、close
    有名管道(fifo)只能本机内进程通讯,半双工,类似读写文件
    (1)有名管道的原理:实质也是内核维护的一块内存,表现形式为一个有名字的文件
    (2)有名管道的使用方法:固定一个文件名,2个进程分别使用mkfifo创建fifo文件,然后分别open打开获取到fd,然后一个读一个写
    (3)管道通信限制:半双工(注意不限父子进程,任意2个进程都可)
    (4)管道通信的函数:mkfifo、open、write、read、close
    SystemV IPC:系统通过一些专用API来提供SystemV IPC功能,实质也是内核提供的公共内存
    消息队列
    (1)本质上是一个队列,队列可以理解为(内核维护的一个)FIFO
    (2)工作时A和B2个进程进行通信,A向队列中放入消息,B从队列中读出消息。
    信号量
    (1)实质就是个计数器(相当一个可以共同用来计数的变量)
    (2)通过计数值来提供互斥和同步
    共享内存
    (1)大片内存直接映射
    (2)类似于LCD显示时的显存用法
    信号:
    域套接字socket:进程间通讯、网络通讯
    信号:信号是内容受限的异步通讯机制
    信号原理:
    目的是用来进程间通讯的,是异步的,能传输内容较少,本质是int型编号(事先定义好的)
    信号的发出:用户中断按键;硬件异常,内核发出信号;用户kill命令/程序kill函数发送;软件条件满足后发送信号
    信号的接收:
    忽略信号;捕获信号(信号绑定了一个函数);默认处理(忽略或终止程序);
    常见信号:
    SIGINT 2 Ctrl+C时OS送给前台进程组中每个进程
    SIGABRT 6 调用abort函数,进程异常终止
    SIGPOLL SIGIO 8 指示一个异步IO事件,在高级IO中提及
    SIGKILL 9 杀死进程的终极办法
    SIGSEGV 11 无效存储访问时OS发出该信号
    SIGPIPE 13 涉及管道和socket
    SIGALARM 14 涉及alarm函数的实现
    SIGTERM 15 kill命令发送的OS默认终止信号
    SIGCHLD 17 子进程终止或停止时OS向其父进程发此信号
    SIGUSR1 10 用户自定义信号,作用和意义由应用自己定义
    SIGUSR2 12
    进程信号处理:
    用signal函数处理SIGINT信号
    signal返回值:如果出错;之前绑定的函数指针
    参数:信号,要绑定处理的函数指针
    简单好用,捕获信号常用,无法简单直接得知之前设置的对信号的处理方法
    sigaction函数
    sigaction比signal更具有可移植性,参数不是函数指针而是sigaction结构体指针
    可以一次得到设置新捕获函数和获取旧的捕获函数(还可以单独设置新的捕获或者单独只获取旧的捕获函数),而signal函数不能单独获取旧的捕获函数而必须在设置新的捕获函数的同时才获取旧的捕获函数。
    alarm函数:闹钟API,指定时间到后会有SIGALARM信号
    pause函数:让当前进程暂停运行,交出CPU给其他进程去执行。当前进程会表现为“卡住、阻塞住”,要退出pause状态当前进程需要被信号唤醒。
    可以使用alarm和pause来模拟sleep

  5. 高级IO
    阻塞与非阻塞IO
    常见的阻塞:wait、pause、sleep等函数;read或write某些文件时
    实现非阻塞IO访问:O_NONBLOCK和fcntl函数操作
    并发式IO的解决方案
    非阻塞式IO
    IO多路复用(IO multiplexing):
    select和poll函数实现:外部阻塞式,内部非阻塞式自动轮询多路阻塞式IO
    异步通知(异步IO):几乎可以认为:异步IO就是操作系统用软件实现的一套中断响应系统
    工作方法是:当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。
    函数:fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN)
    signal或者sigaction(SIGIO)
    存储映射IO
    mmap函数
    LCD显示和IPC之共享内存
    存储映射IO的特点
    (1)共享而不是复制,减少内存操作
    (2)处理大文件时效率高,小文件不划算
    6.线程
    (pthread相关的man手册。安装方法:1、虚拟机上网;2、sudo apt-get install manpages-posix-dev)
    多进程可以用来实现并发:CPU分时复用,单核CPU可实现宏观并行。可以用来实现多任务。
    劣势:进程间切换开销大,进程间通讯麻烦且效率低
    线程技术:
    保留了进程所实现的多任务特性,像进程一样可被OS调度。
    改进了切换和通讯的效率,同一进程的多个线程通讯效率高。
    在多核CPU(对称多处理器架构SMP)效率最大化。
    特点:线程相当轻量级进程,是参与内核调度的最小单元,一个进程可以有多个线程
    线程常见函数:用到了pthread静态库,故gcc时 加-lpthread
    线程创建与回收
    pthread_create :主线程 创建线程
    pthread_join :主线程 阻塞等待回收子线程
    pthread_detach :主线程分离子线程,主线程不再回收,子线程自己回收资源
    线程创建后应当在两个回收资源方式中选择一个
    线程取消
    pthread_cancel 一般都是主线程调用该函数去取消子线程
    pthread_setcancelstate 子线程设置自己是否允许被取消
    pthread_setcanceltype 子线程设置自己是被取消的模式(立即或其他)
    线程函数退出相关
    pthread_exit与return退出 正常线程退出一般调用pthread_exit
    pthread_cleanup_push 在线程中保存锁、等资源时,需要压进相应的解锁或其他函数,在线程被取消或是终止时,压进的内容会被执行
    pthread_cleanup_pop 参数可以让压进的资源弹出执行或是不执行
    获取线程id
    pthread_self
    线程同步——
    信号量
    子线程被阻塞,主程序可以激活,这是线程同步问题。
    信号量:应当是全局变量
    sem_init :初始化信号量
    sem_destroy :销毁一个信号量
    sem_post :激活信号量
    sem_wait :子线程判断信号量是否被激活
    互斥锁又叫互斥量(mutex):可以认为互斥锁是一种特殊的信号量,主要用来实现关键段保护
    相关函数:pthread_mutex_init 初始化一个互斥锁
    pthread_mutex_destroy 销毁一个互斥锁
    pthread_mutex_lock 上锁
    pthread_mutex_unlock 解锁
    条件变量
    pthread_cond_init 初始化一个条件变量
    pthread_cond_destroy 销毁一个条件变量
    pthread_cond_wait 等待一个条件变量成立,否则阻塞
    pthread_cond_signal 发条件变量,可激活一个线程
    pthread_cond_broadcast 发条件变量,可激活多个线程

  6. 网络基础
    网络域套接字socket,网络通信其实就是位于网络中不同主机上面的2个进程之间的通信。
    网络通信的层次
    (1)硬件部分:网卡
    (2)操作系统底层:网卡驱动
    (3)操作系统API:socket接口
    (4)应用层:低级(直接基于socket接口编程)
    (5)应用层:高级(基于网络通信应用框架库)
    (6)应用层:更高级(http、网络控件等)
    网络通信的发展历程
    (1)单机阶段
    (2)局域网阶段
    (3)广域网internet阶段
    (4)移动互联网阶段
    (5)物联网阶段
    三大网络:电信网、电视网络、互联网
    网络通信的传输媒介
    无线传输:WIFI、蓝牙、zigbee、4G/5G/GPRS等
    有线通信:双绞线、同轴电缆、光纤等
    **OSI 7层网络模型:物理层,数据链路层,网络层,传输层,会话层,表示层和应用层。
    物理层(Physical Layer)
    实际上就是布线、光纤、网卡和其它用来把两台网络通信设备连接在一起的东西。
    数据链路层(Data Link Layer)
    运行以太网等协议。交换机可以看成网桥,网桥都在这层工作,仅关注以太网上的MAC地址。数据链路层把数据帧转换成二进制位供物理层处理
    网络层(Network Layer)
    网络层的任务就是选择合适的网间路由和交换结点, 确保数据及时传送。网络层将数据链路层提供的帧组成数据包,包中封装有网络层包头,其中含有逻辑地址信息- -源站点和目的站点地址的网络地址。IP是网络层问题的一部分,此外还有一些路由协议和地址解析协议(ARP)。有关路由的一切事情都在第3层处理。地址解析和路由是网络层的重要目的。
    处理信息的传输层(Transport Layer)
    负责获取全部信息,提供端对端的通信管理。
    数据单元也称作数据包(packets),TCP等具体的协议时又有特殊的叫法,TCP的数据单元称为段(segments)而UDP协议的数据单元称为“数据报(datagrams)
    会话层( Session Layer)
    会话层及以上的高层次中,数据传送的单位不再另外命名,统称为报文。会话层不参与具体的传输,它提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。如服务器验证用户登录便是由会话层完成的。
    表示层(Presentation Layer)
    解决用户信息的语法表示问题。将欲交换的数据从适合于某一用户的抽象语法,转换为适合于OSI系统内部使用的传送语法。即提供格式化的表示和转换数据服务。数据的压缩和解压缩, 加密和解密等工作都由表示层负责。
    “应用层”(Application Layer)
    确定进程之间通信的性质以满足用户需要以及提供网络与用户应用软件之间的接口服务。SMTP、DNS和FTP都是7层协议。
    网卡
    (1)计算机上网必备硬件设备,CPU靠网卡来连接外部网络
    (2)串转并设备
    (3)数据帧封包和拆包
    (4)网络数据缓存和速率适配
    集线器(HUB)
    (1)信号中继放大,相当于中继器
    (2)组成局域网络,用广播方式工作。
    (3)注意集线器是不能用来连接外网的
    交换机
    (1)包含集线器功能,但更高级
    (2)交换机中有地址表,数据包查表后直达目的通信口而不是广播
    (3)找不到目的口时广播并学习
    路由器
    路由器是局域网和外部网络通信的出入口,将整个internet划分成一个个的局域网,却又互相联通。
    路由器对内管理子网(局域网),可以在路由器中设置子网的网段,设置有线端口的IP地址,设置dhcp功能等,因此局域网的IP地址是路由器决定的。
    路由器对外实现联网,联网方式取决于外部网络(如ADSL拨号上网、宽带帐号、局域网等)。这时候路由器又相当于是更高层级网络的其中一个节点而已。
    路由器相当于有2个网卡,一个对内做网关、一个对外做节点。
    路由器的主要功能是为经过路由器的每个数据包寻找一条最佳路径(路由)并转发出去。其实就是局域网内电脑要发到外网的数据包,和外网回复给局域网内电脑的数据包。
    路由器技术是网络中最重要技术,决定了网络的稳定性和速度。
    DNS(Domain Name Service 域名服务)
    IP地址难记、不直观,域名用来代替IP地址
    DNS服务器就是专门提供域名和IP地址之间的转换的服务的
    访问一个网站的流程是:先使用IP地址访问DNS服务器IP地址,查询我们要访问的域名的IP地址,然后再使用该IP地址访问我们真正要访问的网站。这个过程被浏览器封装屏蔽,其中使用的就是DNS协议。
    DHCP(dynamic host configuration protocl,动态主机配置协议)
    动态分配是局域网内的DHCP服务器来协调的,很多设备都能提供DHCP功能,方便接入和断开、有限的IP地址得到充分利用
    NAT(network address translation,网络地址转换协议)
    IP地址分为公网IP(internet范围内唯一的IP地址)和私网IP(内网IP),局域网内使用的都是私网IP。
    网络通信的数据包中包含有目的地址的IP地址当局域网中的主机要发送数据包给外网时,路由器要负责将数据包头中的局域网主机的内网IP替换为当前局域网的对外外网IP。这个过程就叫NAT。缓解IPv4的IP地址不够用问题,IPv6解决了这个问题
    IPv4地址分类
    IP地址实际是一个32位二进制构成,在网络通信数据包中就是32位二进制,而在人机交互中使用点分十进制方式显示。IP地址 = 网络地址 + 主机地址
    IP地址中32位实际包含2部分,分别为:网络地址和主机地址。子网掩码,用来说明网络地址和主机地址各自占多少位。
    由网络地址和主机地址分别占多少位的不同,将IP地址分为若干类,常用:
    A类:由一字节的​网络地址和三字节的主机地址组成。网络地址的最高位​必须为0b,即0127。A类地址的第一个地址块(网络号为0)和最后一个地址块(网络号为127)保留使用。即全0表示本地网络,全1表示保留诊断作用,因此A类地址的有效网络范围为1126.
    B类:由二字节的网络地址和二字节的主机地址组成,网络地址最高位必须为10b,即128 ~191
    C类:由三字节的网络地址和一字节的主机地址组成,网络地址最高位必须为110b,即192~223
    本地回环地址指的是以127开头的地址(127.0.0.1 - 127.255.255.254),通常用127.0.0.1来表示。
    判断2个IP地址是否在同一子网内
    网络标识 = IP地址 & 子网掩码;
    2个IP地址的网络标识一样,那么就处于同一网络。
    源IP地址:发出数据包的网络的IP地址
    目标IP地址:要接收数据包的计算机的IP地址

  7. 网络编程
    CS架构(client server,客户端服务器架构)
    BS架构(broswer server,浏览器服务器架构)
    TCP/IP协议:用的最多的网络协议实现。分为4层,对应OSI的7层
    编程时最关注应用层,了解传输层,网际互联层和网络接入层不用管
    特点:
    TCP协议工作在传输层,对上服务socket接口(API),对下调用IP层(网络链路层)
    TCP协议面向连接,通信前必须先3次握手建立连接关系后才能开始通信。
    TCP协议提供可靠传输,不怕丢包、乱序等。
    TCP如何保证可靠传输
    (1)TCP在传输有效信息前要求通信双方必须先握手,建立连接才能通信
    (2)TCP的接收方收到数据包后会ack给发送方,若发送方未收到ack会丢包重传
    (3)TCP的有效数据内容会附带校验,以防止内容在传递过程中损坏
    (4)TCP会根据网络带宽来自动调节适配速率(滑动窗口技术)
    (5)发送方会给各分割报文编号,接收方会校验编号,一旦顺序错误即会重传。
    TCP的建立连接需要三次握手,建立连接条件:服务器listen时客户端主动发起connect
    TCP的关闭连接需要四次握手,服务器或者客户端都可以主动发起关闭
    这些握手协议已经封装在TCP协议内部,socket编程接口平时不用管
    基于TCP通信的服务模式
    (1)具有公网IP地址的服务器(或者使用动态IP地址映射技术)
    (2)服务器端socket、bind、listen后处于监听状态
    (3)客户端socket后,直接connect去发起连接。
    (4)服务器收到并同意客户端接入后会建立TCP连接,然后双方开始收发数据,收发时是双向的,而且双方均可发起
    (5)双方均可发起关闭连接
    常见的使用了TCP协议的网络应用:http、ftp、QQ服务器、mail服务器
    端口号,实质就是一个数字编号,用来在我们一台主机中(主机的操作系统中)唯一的标识一个能上网的进程。端口号和IP地址一起会被打包到当前进程发出或者接收到的每一个数据包中。每一个数据包将来在网络上传递的时候,内部都包含了发送方和接收方的信息(就是IP地址和端口号),所以IP地址和端口号这两个往往是打包在一起不分家的。
    socket编程接口:网络通讯统一使用网络字节序,即大端模式
    建立连接
    socket:类似于open,用来打开一个网络连接,如果成功则返回一个网络文件描述符(int类型),之后我们操作这个网络连接都通过这个网络文件描述符。客户端和服务器都会调用
    bind:服务器绑定一个socket到ip地址
    listen:服务器进行监听设置
    accept:阻塞等待客户端接入,返回值是一个fd,通过这个连接来和客户端进行读写操作
    connect:客户端连接到服务器的ip
    服务器socket返回的fd叫做监听fd,是用来监听客户端的,不能用来和任何客户端进行读写;accept返回的fd叫做连接fd,用来和连接那端的客户端程序进行读写。
    发送和接收
    send和write:发送
    recv和read:发送
    辅助性函数:IP地址格式转换函数,会自己考虑大小端模式
    (1)inet_aton、inet_addr、inet_ntoa
    (2)(兼容IPv6)inet_ntop(二进制IP转点分十进制字符串ip)、inet_pton(反过来)
    htonl ;htons; ntohl; ntohs;//h:host n:net l:long s:short
    表示IP地址相关数据结构:都定义在 /usr/include/netinet/in.h
    struct sockaddr,这个结构体是网络编程接口中用来表示一个IP地址的,兼容IPv4和IPv6的
    typedef uint32_t in_addr_t; 网络内部用来表示IP地址的类型
    struct in_addr //将上面这种类型封装成结构体
    {
    in_addr_t s_addr;
    };
    struct sockaddr 这个结构体是linux的网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都需要这个结构体,这个结构体是兼容IPV4和IPV6的。在实际编程中这个结构体会被一个struct sockaddr_in或者一个struct sockaddr_in6所填充。
    struct sockaddr_in //表示IPv4的标准结构体
    {
    _SOCKADDR_COMMON (sin);
    in_port_t sin_port; /* Port number. /
    struct in_addr sin_addr; /
    Internet address. */

    		/* Pad to size of `struct sockaddr'.  */
    		unsigned char sin_zero[sizeof (struct sockaddr) -
    							   __SOCKADDR_COMMON_SIZE -
    							   sizeof (in_port_t) -
    							   sizeof (struct in_addr)];
    	  };
    	
    client和server之间的通信是异步的,依靠应用层协议来解决通讯问题
    

    UDP:…

你可能感兴趣的:(linux,底层应用开发,经验分享,恰饭)