欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > I/O进程(全)

I/O进程(全)

2025/4/17 7:28:20 来源:https://blog.csdn.net/JCBP_/article/details/147230125  浏览:    关键词:I/O进程(全)
  • I/O
    • 一、标准IO
      • 1.概念
        • 在C库中定义的一组用于输入输出的函数
      • 2.特点
        • (1).通过缓冲机制减少系统调用,提高效率
        • (2.)围绕流进行操作,流用FILE *来描述
        • (3).标准IO默认打开了三个流,stdin(标准输入)、stdout(标准输出)、stderr(标准错误)
        • (4).一般操作普通文件
      • 3.缓冲区
        • (1).全缓冲:与文件相关
        • 缓冲区刷新条件:
        • 程序正常退出:return(main) exit
        •  缓冲区溢出
        • 强制刷新fflush
        • fclose关闭对应的流
        • (2).行缓冲:与终端相关
        • 缓冲区刷新条件:
        • \n
        • 程序正常退出:return(main) exit
        • 缓冲区溢出
        • 强制刷新fflush
        • fclose关闭对应的流
        • (3).不缓冲:没有缓冲区,标准错误
        • 计算缓冲区大小(1kb)
      • 4.函数接口
        • 4.1打开文件fopen
          • FILE *fopen(const char *path, const char *mode)
          • 功能:打开文件
          • 参数:path:打开的文件     mode:打开的方式         r:只读,当文件不存在时报错,文件流定位到文件开头         r+:可读可写,当文件不存在时报错,文件流定位到文件开头         w:只写,文件不存在创建,存在清空         w+:可读可写,文件不存在创建,存在清空        a:追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾       a+:读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾         
          • 注:当a的方式打开文件时,写只能在末尾进行追加,定位操作是无法改变写的位置,但是可以改变读的位置
          • 返回值:成功:文件流;失败:NULL,并且会设置错误码。
        • 4.2关闭文件 fclose
          • int fclose(FILE* stream);
          • 功能:关闭文件
          • 参数:stream:文件流
        • 4.3读写操作fread fwrite fgets fputs
          • (1).按照字符串读写
            • char * fgets(char *s, int size, FILE * stream); 功能:从文件中每次读取一行字符串 参数:s:存放字符串的地址          size:一次读取的字符个数          stream:文件流 返回值:成功:s的地址;失败或读到文件末尾:NULL特性:每次实际读取的字符个数为size-1个,会在末尾自动添加\0           每次读一行,遇到\n后不再继续,读下一行。int fputs(const char *s, FILE * stream); 功能:向文件中写字符串 参数:s:要写的内容         stream:文件流 返回值:成功:非负整数;失败:EOF
            • char * fgets(char *s, int size, FILE * stream);
            • 功能:从文件中每次读取一行字符串
            • 参数:s:存放字符串的地址          size:一次读取的字符个数          stream:文件流
            • 返回值:成功:s的地址;失败或读到文件末尾:NULL
            • 特性:每次实际读取的字符个数为size-1个,会在末尾自动添加\0           每次读一行,遇到\n后不再继续,读下一行。
            • int fputs(const char *s, FILE * stream);
            • 功能:向文件中写字符串
            • 参数:s:要写的内容         stream:文件流
            • 返回值:成功:非负整数;失败:EOF
            • 练习:通过fgets实现"wc -l 文件名"命令功能(计算文件行数)
          • (2).按照二进制的方式进行读写:一般操作二进制文件
            • size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
            • 功能:从文件流读取多个元素
            • 参数:ptr :用来存放读取元素           size :元素大小 sizeof(数据类型)           nmemb :读取元素的个数           stream :要读取的文件
            • 返回值:成功:读取的元素的个数; 读到文件尾: 0        失败: -1
            • size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 功能:按对象写
            • 参数:同上
            • 返回值:成功:写的元素个数;失败 :-1
        • 4.4文件定位操作
          • void rewind(FILE *stream);
          • 功能:将文件位置指针定位到起始位置 int fseek(FILE *stream, long offset, int whence);
          • 功能:文件的定位操作
          • 参数:stream:文件流           offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移           whence:相对位置:            SEEK_SET:相对于文件开头            SEEK_CUR:相对于文件当前位置            SEEK_END:相对于文件末尾
          • 返回值:成功:0;失败:-1
          • 注:当打开文件的方式为a或a+时,fseek不起作用                                         
          •  long ftell(FILE *stream);
          • 功能:获取当前的文件位置
          • 参数:要检测的文件流
          • 返回值:成功:当前的文件位置,出错:-1
    • 二、文件IO
      • 1.概念
        • 在系统中(posix)定义的一组用于输入输出的接口
        • posix:可移植操作系统的接口
      • 2.特点
        • (1).没有缓冲机制,每次调用都会引起系统调用
        • (2).围绕文件描述符进行操作,文件描述符都是非负整数(>=0),依次分配
        • (3).文件IO默认打开了三个文件描述符,分别是0(标准输入),1(标准输出),2(标准错误)
        • (4).可以操作任意类型的文件,目录文件除外。
      • 3.函数接口
        • 3.1打开文件 open
          • int open(const char *pathname, int flags);
          • 功能:打开文件
          • 参数:
          • pathname:文件路径名          
          • flags:打开文件的方式            
          • O_RDONLY:只读            
          • O_WRONLY:只写            
          • O_RDWR:可读可写            
          • O_CREAT:创建            
          • O_TRUNC:清空            
          • O_APPEND:追加            
          • O_EXCL:判错
          • 返回值:成功:文件描述符;失败:-1
          • 当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限。
          • int open(const char *pathname, int flags, mode_t mode);
          • 创建出来的文件权限为指定权限值&(~umask) //umask为文件权限掩码
        • 3.2关闭文件 close
          • int close(int fd);
          • 功能:关闭文件
          • 参数:fd:文件描述符
        • 3.3读写操作 read write
          • ssize_t read(int fd, void *buf, size_t count);
          • 功能:从一个已打开的可读文件中读取数据
          • 参数:
          • fd 文件描述符          
          • buf 存放位置          
          • count 期望的个数
          • 返回值:
          • 成功:实际读到的个数              
          • 返回-1:表示出错,并设置errno号              
          • 返回0:表示读到文件结尾
          • ssize_t write(int fd, const void *buf, size_t count);
          • 功能:向指定文件描述符中,写入 count个字节的数据。
          • 参数:
          • fd 文件描述符          
          • buf 要写的内容          
          • count 期望值
          • 返回值:成功:实际写入数据的个数。失败 : -1 
        • 3.4定位操作 lseek
          • off_t lseek(int fd, off_t offset, int whence);
          • 功能:设定文件的偏移位置
          • 参数:
          • fd:文件描述符  
          • offset偏移量   正数:向文件结尾位置移动   负数:向文件开始位置  
          • whence 相对位置   SEEK_SET 开始位置   SEEK_CUR 当前位置   SEEK_END 结尾位置
          • 返回值:成功:文件的当前位置 。失败:-1
    • 三、文件IO与标准IO对比
    • 四、库
      • 1.库的定义
        • 通俗讲将用户写好的程序打包形成一个整体;当其他用户或其他模块使用时,只要有这个库文件就可以,不需要源代码。也可以理解为一组预先编译好的方法集合。 本质上来说库就是一种可执行代码的二进制形式。
        • 当要使用别人的函数的时候除了包含头文件还要有库 。
        • linux系统存储库的位置一般在/lib或/usr/lib
      • 2.库的分类
        • 静态库和动态库,本质区别是代码被载入时刻不同。
        • (1).静态库在程序编译时会被连接到目标代码中。
        • 优点:程序运行时将不再需要该静态库;运行时无需加载库,运行速度更快
        • 缺点:静态库中的代码复制到了程序中,因此体积较大; 静态库升级后,程序需要重新编译链接
        • (2).动态库是在程序运行时才被载入代码中。
        • 优点:程序在执行时加载动态库,代码体积小; 程序升级更简单; 不同应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
        • 缺点:运行时还需要动态库的存在,移植性较差
      • 3.静态库的制作
        • (1).将源文件生成目标文件(.o) gcc -c xxx.c -o xxx.o
        • (2).创建静态库文件,用ar指令,它会将许多.o文件转换为.a文件  ar crs libxxx.a xxx.o  静态库文件名命名规范:lib为前缀,紧跟的是静态库名 ,.a为扩展名
        • (3).测试静态库使用 gcc xxx.c -L指定库的路径 -l指定库名
      • 4.动态库制作
        • (1).用gcc创建共享库/动态库 gcc -fPIC -c xxx.c -o xxx.o -fPIC:创建与地址无关的编译程序 gcc -shared -o libxxx.so xxx.o
        • (2).测试动态库的使用 gcc xxx.c -L指定库的路径 -l指定库名
          • 解决方法
            • (1).将库拷贝到系统库路径下(/lib /usr/lib)(编译时不需要指定库的路径)
            • (2).更改环境变量,将库所在的路径添加到环境变量内 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. (只更改当前终端的环境变量,切换终端环境变量就不存在了)
            • (3).添加/etc/ld.so.conf.d/*.conf文件。把库所在的路径加到文件末尾,并执行ldconfig刷新。   
            •  sudo vi /etc/ld.so.conf.d/*.conf 添加动态库存在的路径
            • 刷新:sudo ldconfig
    • 五、进程
      • 1.进程、程序
        • 程序:编译好的可执行文件 存放在磁盘上的指令和数据的有序集合 程序是静态的,没有任何执行的概念                             
        • 进程:一次程序的运行过程,一个独立的可调度的任务 执行一个程序所分配资源的总称 进程是动态的,包括创建、调度、执行和消亡。
      • 2.进程的特点
        • (1).系统会为每个进程分配0-4G的虚拟空间,其中0-3G为每个进程所独有,3-4G为所有进程所共有。
        • (2).CPU调度进程时会为进程分配时间片(几毫秒-十几毫秒之间),当时间片用完之后,CPU会再进行其他进程的调度,实现进程的轮转,进而实现多任务操作。
      • 3.进程段
        • Linux中的进程包含五个段:
        • “BSS段”存放程序中未初始化的全局变量的一块内存区域
        • “数据段”已初始化的全局变量的一块内存区域。
        • “代码段”存放程序执行代码的一块内存区域
        • “堆段”存放进程运行中被动态分配的内存段
        • “栈段”又称为“堆栈段”存放的是函数的返回地址、函数的参数以及程序中的局部变量
      • 4.进程分类
        • 交互进程:该类进程是由shell控制和运行的。交互进程既可以在前台运行,也可以在后台运行。该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户的输入后,该类进程会立刻响应,典型的交互式进程有:shell命令进程、文本编辑器等。
        • 批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行。
        • 守护进程:该类进程在后台运行。它一般在Linux启动时开始执行,系统关闭时才结束。
      • 5.进程状态
        • (1).运行态(TASK_RUNNING):R 指正在被CPU运行或者就绪的状态。这样的进程被成为runnning进程。
        • (2).睡眠态(等待态): 可中断睡眠态(TASK_INTERRUPTIBLE)S:处于等待状态中的进程,一旦被该进程等待的资源被释放,那么该进程就会进入运行状态。 不可中断睡眠态(TASK_UNINTERRUPTIBLE)D:该状态的进程只能用wake_up()函数唤醒。
        • (3).暂停态(TASK_STOPPED):T 当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送SIGCONT信号让进程转换到可运行状态。
        • (4).死亡态:进程结束 X
        • (5).僵尸态(TASK_ZOMBIE):Z 当进程已经终止运行,但还占用系统资源,要避免僵尸态的产生
      • 6.进程状态切换
        • 进程创建后,进程会进入就绪态,当CPU调度到此进程进入执行态,当时间片用完,此进程会进入就绪态等待CPU的下一次调度;当此进程在执行过程中需要执行某些IO操作(阻塞操作)会进入阻塞态,当完成IO操作时又可进入就绪态,等待CPU调度,当进程运行结束进入结束态。
      • 7.函数接口
        • 7.1创建子进程
          • pid_t fork(void);
          • 功能:创建子进程
          • 返回值:     成功:在父进程中:返回子进程的进程号 >0          在子进程中:返回值为0     失败:-1并设置errno
          • 特点
            • 1.子进程几乎拷贝了父进程的所有内容,包括代码、数据,缓冲区,系统数据段中的值,栈中的数据,父进程打开的文件,但是PID,PPID不同
            • 2.fork之前的代码会被复制但是不会被重新执行,fork之后的代码会被复制,并且父子进程分别执行一遍。
            • 3.父子进程的空间相互独立,互不影响,当在相应的进程中改变全局变量,静态变量,都互不影响
            • 4.fork之前打开的文件,fork之后拿到的是同一个文件描述符,操作的是同一个文件指针                                                                                  
            • 5.若父进程先结束,子进程成为孤儿进程,被init进程所收养,会变成后台进程。
            • 6.若子进程先结束,父进程不结束,父进程没有及时回收,子进程就会变成僵尸进程(避免僵尸进程的产生)
      • 8.回收进程
        • pid_t wait(int *status);
        • 功能:回收子进程资源(阻塞)
        • 参数:status:子进程退出状态,不接受子进程状态设为NULL
        • 返回值:成功:回收的子进程的进程号       失败:-1                                                                          
        • pid_t waitpid(pid_t pid, int *status, int options);
        • 功能:回收子进程资源
        • 参数:
        • pid:>0 指定子进程进程号        =-1 任意子进程        =0 等待其组ID等于调用进程的组ID的任一子进程          <-1 等待其组ID等于pid的绝对值的任一子进程    
        • status:子进程退出状态    
        • options:0:阻塞          WNOHANG:非阻塞
        • 返回值:正常:结束的子进程的进程号      当使用选项WNOHANG且没有子进程结束时:0       出错:-1
      • 9.结束进程
        • void exit(int status);
        • 功能:结束进程,刷新缓存
        • 参数:退出的状态 不返回。
      • 10.获取进程号
        • pid_t getpid(void);
        • 功能:获取当前进程的进程号
        • pid_t getppid(void);
        • 功能:获取当前进程的父进程号
    • 六、进程间通信
      • 1.为什么要进行进程间通信
        • 数据传输、资源共享、事件通知、进程控制
      • 2.进程间通信方式(7)
        • (1).早期进程间通信 无名管道(pipe)、有名管道(fifo)、信号(sem)
        • (2).system V IPC通信 共享内存(share memory)、消息队列(message queue)、信号灯集(semaphore)
        • (3).BSD: 套接字(socket)
      • 3.无名管道
        • 3.1原理图
          • 通信原理:一个进程的输出可以当作另一进程的输入
        • 3.2特点
          • (1).只能用于具有亲缘关系的进程间通信
          • (2).半双工通信模式,具有固定的读端和写端 {单工:只能单方向通信, 广播 半双工:可以双向通信,但是同一时间不可以 对讲机 全双工:可以双向同时通信 打电话}
          • (3).无名管道可以被看做一个特殊的文件,对于他的读写可以使用文件IO函数 (注意:不是文件,他只是存在于内核空间的一部分,无实际文件)
          • (4).管道基于文件描述符进行通信。当一个管道建立的时候,它会自动创建两个文件描述符,一个用于读fd[0],一个用于写fd[1]。
        • 3.3函数
          • int pipe(int fd[2])
          • 功能:创建无名管道
          • 参数:文件描述符 fd[0]:读端 fd[1]:写端
          • 返回值:成功 0       失败 -1
      • 4.有名管道(FIFO)
        • 4.1特点
          • (1).可以使互不相干的两个进程通信
          • (2).有名管道可以通过路径名来指出,并在文件系统中可见,但是内容存储在内存中
          • (3).进程通过文件IO操作有名管道
          • (4).有名管道遵循先进先出的原则,不支持lseek()操作
          • (5).半双工通信
        • 4.2函数
          • int mkfifo(const char *filename,mode_t mode);
          • 功能:创建有名管道
          • 参数:filename:有名管道文件名        mode:权限
          • 返回值:成功:0        失败:-1,并设置errno号
    • 七、信号
      • 1.例子
        • kill -l:查看系统中信号
        • kill -num pid:给pid进程发送pid信号
      • 2.概念
        • (1).信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。
        • (2).信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。
        • (3).如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
      • 3.信号响应方式
        • (1.)忽略信号:对信号不做任何处理,但是有两个信号不能做忽略处理:SIGKILL和SIGSTOP
        • (2).捕捉信号:定义信号处理函数,当信号发生的时候,执行相应的处理函数,但是有两个信号不能做捕捉处理:SIGKILL和SIGSTOP
        • (3).执行缺省操作:linux对每种信号都规定了默认信号。
      • 4.信号的种类
        • 2)SIGINT:结束进程,对应快捷方式ctrl+c
        • 3)SIGQUIT:退出信号,对应快捷方式ctrl+\
        • 9)SIGKILL:结束进程,不能被忽略不能被捕捉
        • 14)SIGALRM:闹钟信号,alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程。
        • 15)SIGTERM:结束终端进程,kill 使用时不加数字默认是此信号
        • 17)SIGCHLD:子进程状态改变时给父进程发的信号
        • 19)SIGSTOP:暂停进程,不能被忽略不能被捕捉
        • 20)SIGTSTP:暂停信号,对应快捷方式ctrl+z
        • 信号的种类
        • 在Linux中,信号被分为不可靠信号和可靠信号,一共64种,可以通过kill -l命令来查看
        • ●不可靠信号:也称为非实时信号,不支持排队,信号可能会丢失,比如发送多次相同的信号,进程只能收到一次,信号值取值区间为1~31
        • ●可靠信号:也称为实时信号,支持排队,信号不会丢失,发多少次,就可以收到多少次,信号值取值区间为32~64 信号产生的方式有如下几种:
        • ● 对于前台进程,用户可以输入特殊终端字符来发送,比如输入Ctrl+C
        • ● 系统异常,比如浮点异常和非法内存段访问
        • ● 系统状态变化,比如alarm定时器到期时将引起SIGALRM信号
        • ● 在终端运行kill命令或在程序中调用kill函数
      • 5.函数接口
        • 5.1发送信号
          • int kill(pid_t pid, int sig);
          • 功能:信号发送
          • 参数:pid:指定进程           sig:要发送的信号
          • 返回值:成功 0              失败 -1     
          •                                              
          •  int raise(int sig);
          • 功能:进程向自己发送信号
          • 参数:sig:信号
          • 返回值:成功 0              
          • 失败 -1                                                 
          •   int pause(void);
          • 功能:用于将调用进程挂起,直到收到信号为止。
        • 5.2定时器
          • unsigned int alarm(unsigned int seconds)
          • 功能:在进程中设置一个定时器
          • 参数:seconds:定时时间,单位为秒
          • 返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则 返回上一个闹钟时间的剩余时间,否则返回0。
          • 注意:一个进程只能有一个闹钟时间。如果在调用alarm时 已设置过闹钟时间,则之前的闹钟时间被新值所代替
        • 5.3信号处理
          • #include <signal.h>
          • typedef void (*sighandler_t)(int);
          • sighandler_t signal(int signum, sighandler_t handler);
          • 功能:信号处理函数
          • 参数:
          • signum:要处理的信号          
          • handler:信号处理方式              
          • SIG_IGN:忽略信号              
          • SIG_DFL:执行默认操作              
          • handler:捕捉信号 void handler(int sig){} //函数名可以自定义
          • 返回值:成功:设置之前的信号处理方式              失败:-1
    • 八、共享内存
      • 1.特点
        • (1).共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,不需要进行任何数据的拷贝
        • (2).为了在多个进程间进行数据的交互,内核专门留了一块内存区,可以由需要访问的进程将其映射到自己的地址空间
        • (3).由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
      • 2.编程步骤
        • (1).创建或者打开共享内存shmget
        • (2).映射共享内存到自己的用户空间shmat
        • (3).使用共享内存
        • (4).撤销映射
        • (5).删除共享内存
      • 3.函数接口
        • int shmget(key_t key, size_t size, int shmflg);
        • 功能:创建或打开共享内存
        • 参数:     key 键值     size 共享内存的大小     shmflg IPC_CREAT|IPC_EXCL(判错)|0666
        • 返回值:成功 shmid              出错 -1
        • void *shmat(int shmid,const void *shmaddr,int shmflg);
        • 功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
        • 参数:    
        • shmid 共享内存的id号    
        • shmaddr 一般为NULL,表示由系统自动完成映射,如果不为NULL,那么由用户指定    
        • shmflg:SHM_RDONLY就是对该共享内存只进行读操作  ,0 可读可写
        • 返回值:成功:完成映射后的地址,       失败:-1的地址
        • 用法:if((p = (char *)shmat(shmid,NULL,0)) == (char *)-1)                 
        •  int shmdt(const void *shmaddr);
        • 功能:取消映射
        • 参数:要取消的地址
        • 返回值:成功0       失败的-1                                                                                             
        • int shmctl(int shmid,int cmd,struct shmid_ds *buf);
        • 功能:(删除共享内存),对共享内存进行各种操作
        • 参数:    
        • shmid 共享内存的id号    
        • cmd IPC_STAT 获得shmid属性信息,存放在第三参数            
        • IPC_SET 设置shmid属性信息,要设置的属性放在第三参数            
        • IPC_RMID:删除共享内存,此时第三个参数为NULL即可
        • 返回:成功0      失败-1
        • 用法:shmctl(shmid,IPC_RMID,NULL);
      • 4.命令
        • ipcs -m:查看系统中共享内存
        • ipcrm -m shmid :删除共享内存
    • 九、信号灯集
      • 1.概念
        • 信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制;System V的信号灯是一个或者多个信号灯的一个集合。其中的每一个都是单独的计数信号灯。 通过信号灯集实现共享内存的同步操作。
      • 2.编程
        • (1).创建key值
        • (2).创建或打开信号灯集:semget(semaphore)
        • (3).初始化信号灯:semctl
        • (4).PV操作:semop
        • (5).删除信号灯集:semctl
      • 3.函数接口
        • int semget(key_t key, int nsems, int semflg);
        • 功能:创建/打开信号灯
        • 参数:
        • key:ftok产生的key值    
        • nsems:信号灯集中包含的信号灯数目    
        • semflg:信号灯集的访问权限,通常为IPC_CREAT |IPC_EXCL |0666
        • 返回值:成功:信号灯集ID        失败:-1                                                                                         
        •  int semctl ( int semid, int semnum, int cmd…/*union semun arg*/);
        • 功能:信号灯集合的控制(初始化/删除)
        • 参数:
        • semid:信号灯集ID    
        • semnum: 要操作的集合中的信号灯编号    
        •  cmd:        
        • GETVAL:获取信号灯的值,返回值是获得值        
        • SETVAL:设置信号灯的值,需要用到第四个参数:共用体        
        • IPC_RMID:从系统中删除信号灯集合
        • 返回值:成功 0       失败 -1
        • 用法:初始化: union semun{     int val; }mysemun; mysemun.val = 10;                                                                               
        •  semctl(semid, 0, SETVAL, mysemun);
        • 获取信号灯值:函数semctl(semid, 0, GETVAL)的返回值
        • 删除信号灯集:semctl(semid, 0, IPC_RMID);                                         
        •  int semop ( int semid, struct sembuf *opsptr, size_t nops);
        • 功能:对信号灯集合中的信号量进行PV操作
        • 参数:
        • semid:信号灯集ID      
        • opsptr:操作方式      
        • nops: 要操作的信号灯的个数 1个
        • 返回值:成功 :0       失败:-1
        • struct sembuf {    short sem_num; // 要操作的信号灯的编号    short sem_op; // 0 : 等待,直到信号灯的值变成0           // 1 : 释放资源,V操作                    // -1 : 申请资源,P操作     short sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO };
        • 用法:
        • 申请资源 P操作:    
        • mysembuf.sem_num = 0;    
        • mysembuf.sem_op = -1;    
        • mysembuf.sem_flg = 0;    
        • semop(semid, &mysembuf, 1);
        • 释放资源 V操作:    
        • mysembuf.sem_num = 0;    
        • mysembuf.sem_op = 1;    
        • mysembuf.sem_flg = 0;    
        • semop(semid, &mysembuf, 1);
    • 十、消息队列
      • 1.特点
        • (1).消息队列是一种IPC对象,由消息队列ID来唯一标识
        • (2).消息队列就是一个消息的列表,用户可以在消息队列中添加消息,读取消息
        • (3).消息队列可以按照类型来进行消息的添加与读取
        • (4).消息队列是存在linux内核中,以链表的形式进行存放
      • 2.编程步骤
        • (1).创建key值
        • (2).创建或打开消息队列
        • (3).使用:添加消息:按照类型将消息添加到已打开的消息队列末尾 读取消息:按照类型从消息队列中读取消息
        • (4).删除消息队列
      • 3.函数接口
        • int msgget(key_t key, int flag);
        • 功能:创建或打开一个消息队列
        • 参数: key值        flag:创建消息队列的权限IPC_CREAT|IPC_EXCL|0666
        • 返回值:成功:msgid        失败:-1                                                                                               
        • int msgsnd(int msqid, const void *msgp, size_t size, int flag);
        • 功能:添加消息
        • 参数:
        • msqid:消息队列的ID      
        • msgp:指向消息的指针。常用消息结构msgbuf如下:           struct msgbuf{             long mtype; //消息类型             char mtext[N]}; //消息正文    
        • size:发送的消息正文的字节数    
        • flag:IPC_NOWAIT消息没有发送完成函数也会立即返回          0:直到发送完成函数才返回
        • 返回值:成功:0       失败:-1
        • 使用:msgsnd(msgid, &msg,sizeof(msg)-sizeof(long), 0)
        • 注意:消息结构除了第一个成员必须为long类型外,其他成员可以根据应用的需求自行定义。                                                                                            
        • int msgrcv(int msgid, void* msgp, size_t size, long msgtype, int flag);
        • 功能:读取消息
        • 参数:
        • msgid:消息队列的ID      
        • msgp:存放读取消息的空间    
        • size:接受的消息正文的字节数    
        • msgtype:0:接收消息队列中第一个消息。             大于0:接收消息队列中第一个类型为msgtyp的消息.             小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。    
        •  flag:0:若无消息函数会一直阻塞        
        • IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG
        • 返回值:成功:接收到的消息的长度       失败:-1                                                                                                
        • int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
        • 功能:对消息队列的操作,删除消息队列
        • 参数:
        • msqid:消息队列的队列ID      
        • cmd:        
        • IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。        
        • IPC_SET:设置消息队列的属性。这个值取自buf参数。        
        • IPC_RMID:从系统中删除消息队列。      
        • buf:消息队列缓冲区
        • 返回值:成功:0       失败:-1
        • 用法:msgctl(msgid, IPC_RMID, NULL)
      • 4.命令
        • ipcs -q:查看系统中消息队列
        • ipcrm -q msgid:删除系统中的消息队列
    • 十一、线程:实现多任务编程
    • 1.概念
      • 线程是一个轻量级的进程,为了提高系统性能引入的线程 Linux里同样用task_struct来描述一个线程。 线程和进程都参与统一的调度。
    • 2.进程与线程的区别
      • 共性:都为操作系统提供了并发执行能力。
      • 不同点: 调度和资源:线程是系统调度的最小单位,进程是资源分配的最小单位。地址空间方面:同一个进程创建的多个线程共享进程的资源;进程的地址空间相互独立。通信方面:线程通信相对简单,只需要通过全局变量可以实现,但是需要考虑临界资源访问的问题;进程通信比较复杂,需要借助进程间的通信机制(借助3g-4g内核空间)。安全性方面:线程安全性差一些,当进程结束时会导致所有线程退出;进程相对安全。
    • 3.线程资源
      • 共享的资源:可执行的指令、静态数据、进程中打开的文件描述符、信号处理函数、当前工作目录、用户ID、用户组ID
      • 私有的资源:线程ID (TID)、PC(程序计数器)和相关寄存器、堆栈、错误号 (errno)、信号掩码和优先级、执行状态和属性
    • 4.函数接口
      • 4.1创建线程
        • int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
        • 功能:创建线程
        • 参数:
        • thread:线程标识            
        • attr:线程属性,NULL:代表设置默认属性            
        • start_routine:函数名:代表线程函数            
        • arg:用来给前面函数传参
        • 返回值:成功:0               失败:错误码
      • 4.2退出线程
        • int pthread_exit(void *value_ptr)
        • 功能:用于退出线程的执行
        • 参数:value_ptr:线程退出时返回的值(任意类型)
        • 返回值:成功 : 0         失败:errno
      • 4.3线程回收
        • int pthread_join(pthread_t thread, void **value_ptr)
        • 功能:用于等待一个指定的线程结束,阻塞函数
        • 参数:
        • thread:创建的线程对象        
        • value_ptr:指针*value_ptr指向线程返回的参数
        • 返回值:成功 : 0        失败:errno                                                                  
        • int pthread_detach(pthread_t thread); 
        • 功能:让线程结束时自动回收线程资源,让线程和主线程分离
        • 参数:thread:线程ID
      • 4.4获取线程号
        • pthread_t pthread_self(void);
        • 功能:     获取线程号
        • 返回值:     成功:调用此函数线程的ID
      • 4.5练习: 通过线程实现数据的交互,主线程循环从终端输入,线程函数将数据循环输出,当输入quit结束程序。(输入一次,打印一次) 标志位:输入是否完成/输出是否完成
    • 5.线程同步:信号量
      • 5.1linux信号量的分类
        • (1). 内核信号量 由内核控制路径使用,类似于自旋锁
        • (2). Posix信号量   a. 无名信号量:数据存储在内存中,通常在线程间使用或父子进程间 函数接口:sem_init\sem_wait\sem_post   b. 有名信号量:数据存储在文件中,在进程间线程间都可以使用 函数接口:sem_open\sem_wait\sem_post\sem_close
        • (3). System V信号量 是信号量的集合,叫信号灯集,属于IPC对象 函数接口:semget\semctl\semop
      • 5.2无名信号量基础
        • 通过信号量实现线程间同步。
        • 信号量:通过信号量实现同步操作;由信号量来决定线程是继续运行还是阻塞等待。信号量代表某一类资源,其值表示系统中该资源的数量,信号量值>0,表示有资源可以用,可以申请到资源,继续执行程序,信号量值<=0,表示没有资源可以用,无法申请到资源,阻塞。
        • 信号量是一个受保护的变量,只能通过三种操作来访问:初始化sem_init、P操作(申请资源)sem_wait、V操作(释放资源)sem_post。信号量的值为非负整数。
      • 5.3函数接口
        • int sem_init(sem_t *sem, int pshared, unsigned int value)
        • 功能:初始化信号量
        • 参数:
        • sem:初始化的信号量对象    
        • pshared:信号量共享的范围(0: 线程间使用 非0:1进程间使用)    
        • value:信号量初值 返回值:成功 0       失败 -1                                                                                                           
        •  int sem_wait(sem_t *sem)
        • 功能:申请资源 P操作
        • 参数:sem:信号量对象
        • 返回值:成功 0       失败 -1
        • 注:此函数执行过程,当信号量的值大于0时,表示有资源可以用,则继续执行,同时对信号量减1;当信号量的值等于0时,表示没有资源可以使用,函数阻塞。               
        • int sem_post(sem_t *sem)
        • 功能:释放资源 V操作
        • 参数:sem:信号量对象
        • 返回值:成功 0       失败 -1 注:释放一次信号量的值加1,函数不阻塞
    •  十二、线程同步
      • 1.互斥
        • 1.1概念
          • 临界资源:一次仅允许一个进程所使用的资源
          • 临界区:指的是一个访问共享资源的程序片段
          • 互斥:多个线程在访问临界资源时,同一时间只能一个线程访问
          • 互斥锁:通过互斥锁可以实现互斥机制,主要用来保护临界资源,每个临界资源都由一个互斥锁来保护,线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止。
        • 1.2函数
          • int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
          • 功能:初始化互斥锁
          • 参数:
          • mutex:互斥锁    
          • attr: 互斥锁属性 // NULL表示缺省属性
          • 返回值:成功 0       失败 -1                                                                                                            
          • int pthread_mutex_lock(pthread_mutex_t *mutex)
          • 功能:申请互斥锁
          • 参数:mutex:互斥锁
          • 返回值:成功 0       失败 -1
          • 注:和pthread_mutex_trylock区别:pthread_mutex_lock是阻塞的;pthread_mutex_trylock不阻塞,如果申请不到锁会立刻返回                                    
          • int pthread_mutex_unlock(pthread_mutex_t *mutex)
          • 功能:释放互斥锁
          • 参数:mutex:互斥锁
          • 返回值:成功 0       失败 -1                                                                                                            
          • int pthread_mutex_destroy(pthread_mutex_t *mutex)
          • 功能:销毁互斥锁
          • 参数:mutex:互斥锁
        • 1.3练习
          • int a[10]={0,1,2,3,4,5,6,7,8,9}; 两个线程:一个线程打印,一个线程倒置
        • 1.4死锁
          • 1.4.1概念
            • 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
          • 1.4.2死锁产生的四个必要条件
            • (1)、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
            • (2)、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
            • (3)、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
            • (4)、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。 注意:当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
      • 2.条件变量
        • 2.1步骤
          • pthread_cond_init:初始化
          • pthread_cond_wait:阻塞等待条件产生,没有条件产生时阻塞,同时解锁,当条件产生时结束阻塞,再次上锁。 
          • pthread_mutex_lock(); //上锁  
          • pthread_cond_wait(cond, lock); //如果没有条件产生时,解锁,当等待到条件产生时,上锁。
          • pthread_cond_signal:产生条件,不阻塞
          • pthread_cond_wait先执行,pthread_cond_signal再产生条件
        • 2.2函数接口
          • int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
          • 功能:初始化条件变量
          • 参数:
          • cond:是一个指向结构pthread_cond_t的指针    
          • restrict attr:是一个指向结构pthread_condattr_t的指针,一般设为NULL
          • 返回值:成功:0 失败:非0                                                                                                                               
          • int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
          • 功能:等待信号的产生
          • 参数:
          • restrict cond:要等待的条件      
          • restrict mutex:对应的锁
          • 返回值:成功:0,失败:不为0 注:当没有条件产生时函数会阻塞,同时会将锁解开;如果等待到条件产生,函数会结束阻塞同时进行上锁。
          •          
          • int pthread_cond_signal(pthread_cond_t *cond);
          • 功能:给条件变量发送信号
          • 参数:cond:条件变量值
          • 返回值:成功:0,失败:非0
          • 注:必须等待pthread_cond_wait函数先执行,再产生条件才可以                                                                        
          • int pthread_cond_destroy(pthread_cond_t *cond);
          • 功能:将条件变量销毁
          • 参数:cond:条件变量值
          • 返回值:成功:0, 失败:非0
    • 十三、linux IO模型
      • 1.阻塞式IO:是常见,效率低,不浪费CPU
      • 2.非阻塞式IO:轮询、耗费CPU、可以处理多路IO
        • 2.1通过函数自带参数进行设置
        • 2.2.通过设置文件描述符属性设置非阻塞
          • int fcntl(int fd, int cmd, ... /* arg */ );
          • 功能:设置文件描述符属性
          • 参数:    
          • fd:文件描述符    
          • cmd:设置方式 - 功能选择        
          • F_GETFL 获取文件描述符的状态信息 第三个参数化忽略        
          • F_SETFL 设置文件描述符的状态信息 通过第三个参数设置        
          • O_NONBLOCK 非阻塞        
          • O_ASYNC 异步        
          • O_SYNC 同步  
          • arg:设置的值 in
          • 返回值:      
          • 特殊选择返回特殊值 - F_GETFL 返回的状态值(int)        
          • 其他:成功0 失败-1,更新errno
          • 使用:
          • 0为例  
          • 0-原本:阻塞、读权限 修改或添加非阻塞  
          • int flags=fcntl(0,F_GETFL);//1.获取文件描述符原有的属性信息  
          • flags = flags | O_NONBLOCK;//2.修改添加权限  
          • fcntl(0,F_SETFL,flags); //3.将修改好的权限设置回去
      • 3.信号驱动IO:异步通知方式,底层驱动的支持
        • 异步通知:异步通知是一种非阻塞的通知机制,发送方发送通知后不需要等待接收方的响应或确认。通知发送后,发送方可以继续执行其他操作,而无需等待接收方处理通知。
          • (1). 通过信号方式,当内核检测到设备数据后,会主动给应用发送信号SIGIO。
          • (2). 应用程序收到信号后做异步处理即可。
          • (3).应用程序需要把自己的进程号告诉内核,并打开异步通知机制。
            • //1.设置将文件描述符和进程号提交给内核驱动 //一旦fd有事件响应, 则内核驱动会给进程号发送一个SIGIO的信号    fcntl(fd,F_SETOWN,getpid());
            • //2.设置异步通知     int flags;     flags = fcntl(fd, F_GETFL); //获取原属性     flags |= O_ASYNC; //给flags设置异步 O_ASUNC 通知     fcntl(fd, F_SETFL, flags); //修改的属性设置进去,此时fd属于异步
            • //3.signal捕捉SIGIO信号 --- SIGIO:内核通知会进程有新的IO信号可用 //一旦内核给进程发送sigio信号,则执行handler     signal(SIGIO,handler);
      • 4.IO多路复用:select poll epoll
        • 4.1
          • ●应用程序中同时处理多路输入输出流,若采用阻塞模式,得不到预期的目的;
          • ● 若采用非阻塞模式,对多个输入进行轮询,但又太浪费CPU时间;
          • ● 若设置多个进程/线程,分别处理一条数据通路,将新产生进程/线程间的同步与通信问题,使程序变得更加复杂;
          • ● 比较好的方法是使用I/O多路复用技术。其基本思想是: ○ 先构造一张有关描述符的表(最大1024),然后调用一个函数。 ○ 当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。 ○ 函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。
        • 4.2select
          • 4.2.1特点
            • (1).一个进程最多只能监听1024个文件描述符
            • (2).select被唤醒之后要重新轮询,效率相对低
            • (3).select每次都会清空未发生响应的文件描述符,每次拷贝都需要从用户空间到内核空间,效率低,开销大
          • 4.2.2编程步骤
            • (1).先构造一张关于文件描述符的表
            • (2).清空表 FD_ZERO
            • (3).将关心的文件描述符添加到表中 FD_SET
            • (4).调用select函数
            • (5).判断式哪一个或者式哪些文件描述符产生了事件 FD_ISSET
            • (6).做对应的逻辑处理
          • 4.2.3函数接口
            • int select(int nfds, fd_set *readfds, fd_set *writefds,            fd_set *exceptfds, struct timeval *timeout);
            • 功能:  实现IO的多路复用
            • 参数:  nfds:关注的最大的文件描述符+1     readfds:关注的读表  writefds:关注的写表  exceptfds:关注的异常表  timeout:超时的设置   NULL:一直阻塞,直到有文件描述符就绪或出错   时间值为0:仅仅检测文件描述符集的状态,然后立即返回   时间值不为0:在指定时间内,如果没有事件发生,则超时返回0,并清空设置的时间值 struct timeval {     long tv_sec; /* 秒 */     long tv_usec; /* 微秒 = 10^-6秒 */ };
            • 返回值:  准备好的文件描述符的个数  -1 :失败:  0:超时检测时间到并且没有文件描述符准备好
            • 注意:  select返回后,关注列表中只存在准备好的文件描述符
            • 操作表: void FD_CLR(int fd, fd_set *set); //清除集合中的fd位 void FD_SET(int fd, fd_set *set);//将fd放入关注列表中 int FD_ISSET(int fd, fd_set *set);//判断fd是否在集合中 是--》1 不是---》0 void FD_ZERO(fd_set *set);//清空关注列表
          • 4.2.4练习
            • 输入鼠标的时候,响应鼠标事件,输入键盘的时候,响应键盘事件 (两路IO)
          • 4.2.5超时检测
            • (1).概念
              • 什么是网络超时检测呢,比如某些设备的规定,发送请求数据后,如果多长时间后没有收到来自设备的回复,那么需要做出一些特殊的处理  
              • 比如: 链接wifi的时候,等了好长时间也没有连接上,此时系统会发送一个消息: 网络连接失败
            • (2).必要性
              • (1). 避免进程在没有数据时无限制的阻塞;
              • (2).规定时间未完成语句应有的功能,则会执行相关功能
        • 4.3poll
          • 特点
            • (1).优化文件描述符的限制,文件描述符的限制取决于系统
            • (2).poll被唤醒之后要重新轮询一遍,效率相对低
            • (3).poll不需要重新构造表,采用结构体数组,每次都需要从用户空间拷贝到内核空间
        • 4.4epoll:百万级
          • 特点
            • (1).监听的最大的文件描述符没有个数限制
            • (2).异步IO,epoll当有事件产生被唤醒之后,文件描述符主动调用callback函数(回调函数)直接拿到唤醒的文件描述符,不需要轮询,效率高
            • (3).epoll不需要重新构造文件描述符表,只需要从用户空间拷贝到内核空间一次。
        • 4.5总结

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词