Char* fgets(char*s,int size,FILE* stream);
Int printf(constchar* format,...);
Int fprintf(FILE*stream, const char* format,...);
Size_t fread(void*ptr,size_t size,size_t nmemb,FILE* stream );
Size_tfwrite(const void* ptr,size_t size,size_t nmemb,FILE* stream );
Typedeg structiobuf
{
char* ptr; //下一字符的位置
. . .
}
全缓存:
要求填满整个缓冲区才进行I/O调用。一般用于磁盘文件
Fwrite(“asdf”,1,4,pfile);
行缓存 printf();
涉及到一个终端时(例如标准输入输出)。
碰到换行符自动输出
行缓存满自动输出
无缓存:
标准错误流stderr不带缓存区,是错误尽快现实出来
STDIN_FILENO(0) STDOUT_FILENO(1) STDERR_FILENO(2)
文件描述符数量:0---OPRN_MAX
FILE* dopen(intfd,const char* mode); 文件描述符à 文件指针
Int fileno(FILE*stream); 文件指针->文件描述符
Open函数 打开或创建文件 失败返回-1,成功>0
#include<sysy/type.h> //类型
#include<sysy/stath> //状态
#include<fcntl.h> //控制
Int open(constchar* pathname,int flags);
Int open(constchar* pathname,int flags,mode_t mode); //兼容creat函数
Flag: O_RDONLY(只读)O_WRONLY(只写) 0_RDWR(读写)O_APPEND(追加模式,每次写在文件的尾端)
O_CREAT (如果文件不存在,按照mode指定的权限创建)
O_EXCL 测试文件是否存在
O_DIRECTORY 测试pathname是不是目录
O_TRUNC 如果文件存在,以只读或只写方式打开,并清零
O_NONBLOCK 设置本次I/O操作非阻塞
Mode:新建文件的访问权限,对于open函数而言,仅当创建文件时使用第三个参数
#include<sysy/type.h> //类型
#include<sysy/stath> //状态
#include<fcntl.h> //控制
Int creat(constchar* pathname,mode_t mode);//以只写创建文件 等价于open(pathname,O_WRONLY|O_CREAT|O_TRUNC,mode)
#include<unistd.h>
Int close(int fd);//关闭一个文件
当进程结束时,内核关闭所有文件
#include<unistd.h>
Size_t read(intfd,void* buff,size_t count);
一下情况返回值小于count
读普通文件,剩下的字节数不够
从终端设备读取数据时,一次最多读取一行
当从网络读取时,网络的缓冲机构可能会产生这种情况
面向记录的设备,一次最多返回一个记录,如磁带
进程间信号中断
#include<unistd.h>
Size_t write(intfd,const void* buff,size_t count);
返回值小于count的情况
磁盘一些满或者超过文件最大长度限制
Perror(“”);
Gcc –o obj/io.o –csrc/io.c –Include –Wall
#include<sysy/type.h> //类型
#include<unistd.h>
Off_t lseek(intfd,off_t offset,int whence)
Whence:定位位置
SEEK_SET:文件头 SEEK_CUR:当前位置 SEEK_END:文件尾
Lseek也可用来检测文件是否可以设置偏移量。如果文件描述符引用的是管道或匿名管道,lseek返回-1,并将error设置为EPIPE
Int ilen =Lsseek(fd,0,SEEK_END); 返回文件的长度
Int dup(ingoldfd);
Int dup2(ingoldfd,int newfd); newfd中原来的文件描述符被覆盖,如果newfd已经打开,就先自动关闭,再复制。
两个函数的功能:复制文件描述符,返回新的文件描述符
作用:进程间通信时改变进程中标准输入和标准输出设备STDIN_FILENO STDOUT_FILENO
cat cat 文件名 cat> 文件名
mycp
本课总结:
标准c的IO缓存类型
文件描述符
文件操作方式
文件IO系统调用 open close creat read write iseek
文件操作的内核数据结构
Dup和dup2函数
fcntl函数
Intfcntl(int fd,int cmd)
fflush
intflag = fcntl(STDIN_FILEIN,F_GETFL);
flag|=O_NONBLOCK;
fcntl(STDIN_FILENO,F_SETFL,flag);
文件和目录:
文件属性:stat结构体定义于:/usr/include/sys/stat.h
操作函数:
intstat(const char* path, struct stat *buf);
intfstat(int fd, struct stat *buf);
intlstat(const char* path, struct stat *buf);
1和2一样
1和3的却别:path是获取链接时,2获取链接指向的文件的属相,3获取链接本身的属性 ln –s(创建链接)
例子:
structstat buf={0};
stat(path,&buf); 或 l stat(path,&buf);
if(S_ISREG(buf.st_mode))
{ 普通文件}
if(S_ISDIR(buf.st_mode))
{ 目录文件}
if(S_ISLNK(buf.st_mode))
{ 链接文件}
2.文件权限
判断文件属性:
access(path,R_OK)
R_OK 可读
W_OK 可写
F_OK存在
X_OK
屏蔽文件权限
#include<sys/type.h>
#include<sys/stat.h>
Mode_tumask(mode_t mode)
功能:为进程设置文件方式创建屏蔽字,并返回以前的值
Man2 creat 查看
//创建文件 Int creat(constchar* path,mode_t mode); mode是权限
然后屏蔽权限
例子:
Umask(0);//都不屏蔽
Creat(argv[1],0777);//创建的文件有所有权限
Umask(0777);//屏蔽所有权限
Creat(argv[2],0777);//创建没有任何权限的文件
修改文件权限
Chmod(argv[i],权限)//修改权限
缩短文件
Inttruncate(const char* path,off_t length);
Intf truncate(int fd,off_t length);
//文件尾端截取一些数据,以缩短文件
给文件创建链接
IntLink(const char* exitfile,const char* newfile)
删除链接
Intunlink(const char* path)
解除链接
remove 对于目录 和rmdir一样 对于文件 和unlink一样
更换文件名
rename
symlink创建软连接
readlink//得到链接的指向的原文件
文件时间
Structutimbuf //时间的结构体
Utime(constchar* path,const struct utimbuf *buf) 更改文件的时间
命令 : Touch 文件名//恢复文件的时间
操作目录:
mkdir(constchar* path,mode_t mode);//创建目录
Rmdir(constchar* path);//删除目录(里面不能有文件且无其他进程打开此目录)
Opendir打开目录
readdir读取目录
rewinddir重新定位目录
intchdir(const char* path);指定新的当前目录
intfchdir(int fd); 指定新的当前目录
成功返回0,失败返回1
Char*getcwd(char* buf,size_t size);获取当前工作目录的绝对路径 成功返回buf,失败返回NULL 命令pwd
closedir关闭目录
第三章 进程
命令 ps 查看进程ID 号
Ps–u 查看进程状态
Ps–x
正常终止
1. 从main返回
2. 调用exit(标准c函数)
3. 调用_exit或_Exit(系统调用)
4. Int atexit(fun);2和3调用4实现,可以登记进程终止时收尾工作的函数,可以多次登记
5. 最后一个线程调用pthread_exit
异常终止
1. 调用abort
2. 接收信号终止
3. 最后一个线程对取消请求做处理响应
进程返回
1. 通常程序运行成功返回0,失败返回非0
2. 在shell中可以查看进程返回值(echo $?)
环境变量操作函数
char*getenv(const char* name);获取环境变量值
intputenv(cahr* str);形式为name=value的字符串,将其放到环境表中,如果name已经存在,则先删除其原来的定义
intsetenv(name,value,rewrite);略
intunsetenv(name);删除name的定义
setjmp 功能:设置非局部跳转的跳转点
longjmp功能:进行非局部跳转
C程序缺乏异常处理的语法,可以用非局部跳转处理C的程序异常
一.I/O处理的五种模型:
1.阻塞
非阻塞
I/O多路转换
信号驱动
异步
进程
命令:
Ps –aux 查看所有进程
USER 进程属主
PID进程ID
PPID 父进程
%CPU 进程占的CPU百分比
%MEM 占用内存百分比
NI 进程的NICE值,数值大表示较少占用CPU时间
VSZ 进程虚拟大小
RSS 驻留中页的数量
TTY 终端ID
WCHAN 正在等待的进程资源
START 启动进程的时间
TIME 进程小号的CPU时间
COMMAND 命令的名称和参数
进程常见状态:
运行状态 stat = R
等待状态 stat = S
停止状态 stat = T
僵尸状态 Z
Ctrl+z 暂停进程 fg 恢复暂停的进程
Pid_tgetpid(void) 获得当前进程ID
Pid_tgetuid(void) 获得当前进程的实际用户ID
uid_tgeteuid(void) 获得当前进程的有效用户ID
Pid_tgetgid(void) 获得当前进程的用户组ID
Pid_tgetppid(void) 获得当前进程的父进程ID
Pid_tgetpid(void) 获得当前进程ID
Pid_tgetpgrp(void) 获得当前进程所在进程组ID
Pid_tgetpgid(void) 获得进程ID为pid的进程所在的进程组ID
Pstree命令
Pid fork(void) 创建进程
Pid vfork(coid) 同上
#define typedef_STD_TPYE
孤儿进程:父进程结束 子进程没结束,子进程由1号进程结束
僵尸进程:子进程结束但没完全释放内存(内存中的task_struct没有释放)。
僵尸进程的父进程结束后就会被Init进程领养,最终被回收
避免僵尸进程:wait(int*status);
Waitpid(pid_t pid,int*status,int opration);
Exec族函数:通过exec函数执行其他的程序,exec之后exec打开的进程覆盖当前程中进程
Int execl(constchar* path,const char* arg0,....);
Int execv(constchar* path,const char* argv[ ],....);
Int execle(constchar* path,const char* arg0,....);
Int execve(constchar* path,const char* argv[],const char* envp[]);
Int execl(constchar* path,const char* arg0,....);
Int execvp(constchar* path,const char* argv[],....);
Int main(void)
{
Pid_t pid=0;
Pid=fork();
If(pid>0)
{
Exitlp(“\bin\ls”,”mingling”,NULL); //如果有这句 以后的都不会执行
Wait(NULL);
Printf(“创建子进程成功”);
Printf(“子进程ID=%d,她的父进程ID=%d\n”, getpid(),getppid());
}
Else if(pid == 0)
{
Printf(“这里是父进程\n”);
}
Else
{
Printf(“创建子进程失败”);
}
}
System(const char*comment);创建一个子进程并在子进程中执行exec
等同于/bin/bash/ -c “cmd”或exec(“bash”,”-c”,”cmd”);
Cat命令
信号
1.基本概念;进程间通信机制,解决进程在正常运行过程中被中断的问题,导致进程的处理流程发生变化
Kill –l 查看linux系统的信号,共64个。1---31最重要 unix的信号 没有优先级, 非实时 不支持排队
34----64 linux自加的信号 实时的信号 支持排队
Cd /user/include
Find –name signum.h
Cd ./i386-linux-gnu/bits/signum.h
Vi signum.h 通过这些命令可以找到前31个信号
Kill -9 信号编码 关掉一个信号
.信号软件中断
.信号异步事件
不可预见
信号有自己的名称和编码
.信号发送的来源
硬件来源:键盘或其他硬件,信号是由硬件驱动产生的
软件来源:函数kill()相当于命令kill -9, raise()自己发给自己, alarm(), setitimer()定时器
2.信号处理
A.忽略信号:
SIGKILL和SIGSTOP永远不能忽略。
忽略硬件异常
进程启动时SIGUSR1和SIGUER2两个信号被忽略
B.执行默认操作
每个信号都有默认操作,大部分信号默认动作是终止进程。SIGUSR1和SIGUER2没有默认处理
C.捕获信号
告诉内核出现信号时调用自己的处理函数
SIGKILL和SIGSTOP不能被捕获
3.Signal函数
Void( *signal( int signo, void(*func)(int) )) (int);
参数: signo 要登记的信号值
Func 信号处理函数指针
SIG_IG 忽略信号
SIG_DFL 采用系统默认的方式处理信号,执行默认操作
例子:
Void sig_handler(inxint signo)
{
Printf(“%d,%doccuren\n”,getpid(),signo);
}
Int main(int argc,char* argv[])
{
//捕获ctrl+c
If( SIG_ERR == signal(SIGINT,sig_ handler))//向内核登记信号处理函数以及信号值
{ perror(“登记失败\n”);}
If( SIG_ERR == signal(SIGKILL,sig_ handler)) //捕获KILL -9信号
{ perror(“登记失败\n”);}
If( SIG_ERR == signal(SIGSTOP,sig_ handler)) //捕获STOP信号
{ perror(“登记失败\n”);}
If( SIG_ERR == signal(SIGUSR1,sig_ handler)) //捕获用户注册信号
{ perror(“登记失败\n”);}
While(1);
}
4. SIGCHLD信号:子进程状态发生变化(子进程结束),父进程需要调用wait来等待子进程结束并回收它,避免僵尸进程
Void sig_handler(int signo) //当父进程捕获到signo信号后要调用
{
Printf(“%d,%doccuren\n”,getpid(),signo);
Wait(null);// wait回收子进程,否则子进程会成为僵尸进程
}
Void out(int n)
{
Int i=0;
For(;i<n;i++)
{
printf(“%d,OUT %d\n”,getpid(),i); //获得当前进程ID
sleep(2);
}
}
Int main(int argc,char* argv[])
{
If( SIG_ERR == signal(SIGCHLD,sig_ handler) )//向内核登记信号处理函数以及信号值SIGHLID(子进程结束信号)
{ perror(“登记失败\n”);}
Pid_t pid = fiork();
If(pid<0)
{
perror(“ fork error”);
exit(1);
}
Else if(pid>0) //父进程
{ Out(100); }
Else //子进程
{ out(10); }
Return 0;
}
5. 信号发送 kill raise
除了内核和超级用户,并不是每个进程都可以向其他的进程发送信号
一般的进程只能向具有相同uid和gid的进程发信号,或者向相同进程组中的其他进程发信号
常用的发信号函数有kill(), raise(), setitmer(), abort()
Int kill(pid_t pid, int signo); 0为空信号,用来检测特定进程是否存在
成功返回0,失败返回1. 向指定进程发出一个信号
Int raise(int signo)
成功返回0,失败返回1. 向进程本身发出一个信号,相当于kill(getpid(),sig);
Pid>0 将信号发给进程ID为pid的进程
Pid=0 将信号发给于发送进程统一进程组的所有进程
Pid<0 将信号发给进程组ID等于pid的绝对值
Pid==-1 将信号发给发送进程有权限向他们发送信号的所有进程
定时器
6.定时器 Unsignedint alarm(Unsigned int seconds);
设置定时器,当超时,产生SIGALRM信号
信号由内核产生,在指定的seconds秒后,给进程本身发一个SIGALM信号
参数为0,取消以前设置的定时器
Void sig_handler(int signo) //当父进程捕获到signo信号后要调用
{
If(signo == SIGALRM)
{
Printf(“CLOSCTIME OUT\n”);
Alarm(5);
}
}
Void Out_data()
{
Int i=1;
While(i<=20)
{ double d = drand48();
Printf(“%d-10d:%1f\n”,i++,d);
If(i==16)
{
Alarm(0); //取消之前定义的定时器
}
Sleep(1);
}
}
Int main(int argc,char* argv[])
{
If( SIG_ERR == signal(SIGALRM,sig_ handler) )//向内核登记信号处理函数以及信号值SIGALRM (超时信号)
{ perror(“登记失败\n”);}
Alarm(5);//设置定时器
Printf(“开始运行main函数”);
Out_data();
Printf(“停止运行main”);
Return 0;
}
函数的可重入性
Int a;
Void handle(int signo) //a的值受外界影响,不定,就是不可重入函数
{ printf(“a=%d\n”,a);}
Int main()
{
Signal(SIGINT,handle);
While(1)
{
a=3;
a=4;
a=5;
a=6;
}
}
信号集函数:信号集是一个或多个信号的几何,信号集函数用来屏蔽信号
Int sigemptyset(sigset_t *set) 将信号集清空,将所有信号屏蔽字置0
Int sigfillset(sigset_t *set) 将所有信号加入到信号集中,所有信号屏蔽字置1
Int sigaddset(sigset_t *set) 将某个信号加入到信号集中,所有信号屏蔽字置1
Int sigdelset(sigset_t *set) 将某个信号从信号集中中删除,对应信号屏蔽字置0
以上函数成功返回0,出错返回-1
Int sigismember(const siget_t *set,intsigno);测试信号集中是否包含某个信号,判断信号屏蔽字某位是否置1
真返回1,假返回0,出错返回-1
信号未决:信号从产生道递达之间的状态称为信号未决(pending)执行信号的处理动作称为信号递达。
进程可以选择阻塞(block)某个信号,被阻塞的信号产生时就会保持在未决状态,直到解除阻塞才能执行递达
|
SIGHUP 0 |
SIGINT 1 |
SIGQUIT 1 |
|
|
|
|
1 |
0 |
|
|
|
|
Task_struct block peding handler
产生 阻塞 未决 递达
Viod handle(int signo)
{
Switch(signo)
{
Case SIGINT:
Printf(“a=%d\n”,a); break;
Case SIGQUIT:
Sigset_t bset;
Sigemptyset(&bset); // 清空信号集
Sigaddset(&bset,SIGINT);
Sigprocmask(SIG_UNBLOCK,&bset,NULL);
Break;
Defult:
Break;
}
}
Void printfsigset(sigset_t* pset)
{
Int i=0;
For(;i<NSIG;i++)
{
If( sigismember(pset,i) == 1 )
Putchar(“1”);
Else if(sigismember(pset,i) == 0 )
Putchar(“0”);
Else
Perror(“”);
}
Printf(“\n”);
}
Int main()
{
Signal(SIGINT,handl);
Signal(SIGQUIT,handl);
Sigset_t bset; //用来设置信号集
Sigset_t bset2;//存放信号未决的信号集
Sigemptyset(&bset);// 清空信号集
Sigaddset(&bset,SIGINT);把SIGINT信号添加到信号集中
//SIG_BLOCK SIG_UNBLOCK SIG_SETMASK
Sigprocmask(SIG_BLOCK,&bset,NULL);//第三个参数是就得block集
While(1)
{
Sigpending(&bset2);//查看
printfsigset(&bset2);
sleep(1);
}
Return 0;
}
作业 完成mshell项目
进程是资源管理的做小单位,线程是程序执行的最小单位
每个进程有自己的数据段,代码段,堆栈段,线程包含独立的栈和cpu寄存状态。
创建线程:
Int pthread_creat(pthread_t *restricttidp,const pthread_attr_t *restrictattr, void*(start_rtn)(void*), void*restrictarg );
Void* pthread_mian(void* arg)
{
Struct teacher *tea = arg;
Whiel(1)
{
Printf(“age=%d,sex=%c\n”, tea->age,tea->sex);
Sleep(2);
}
Pthread_exit((void*)100);
Return (void*)100;
}
Struct teacher
{
Int age;
Char sex;
}
Int main()
{
Struct teacher tea;
T ea.age=40;
Tea.sex=’M’;
Pthread_attr_t attr;//线程属性
Pthread_attr_int(&attr); //初始化结构体
pthread_attr_setdetachstat(&attr,PTHREAD_CREATE_DETACHED);
Pthread_t pthreadid=0;
pthread_creat(&pthreadid, &attr, pthread_mian,&tea);
//while(1)
//{sleep(2);}
For(int i=0;i<10;i++)
{ sleep(2); }
Printf(“主线程ID=%d,子线程ID=%d”,pthread_self(),pthreadid);
Pthread_cancel( pthreadid );
Printf(“主线程ID=%d,子线程ID=%d”,pthread_self(),pthreadid);
Sleep(5);// Pthread_jion会阻塞等待子线程退出,不需要sleep
Int res=0;
//Pthread_jion( pthreadid,(void**)&res);//取退出的线程的返回值,并清理子线程的资源 上面设置里分离属性
Printf(“%d\n”,res);
Pthread_attr_destroy(&attr);//线程属性注销
return 0;
}
编译:gcc .... –lpthread
Gcc ......–std=c99(int定义到for循环里)
线程退出
Intpthread_exit(void* retval); 自己关闭自己
Intphread_cancel(pthread_t id); 被统一进程中的其他线程取消
Int pthread_jion(pthread_th th, void**thread_return); 取退出的线程的返回值,并清理子线程的资源
设置和获取分离属性
结合适创建的线程,线程结束时需要用pthread_jion来回收子线程的资源
如果是分离式创建线程,不需要调用pthread_jion来回收子线程的资源,子线程会自己去回收资源,但是无法获得线程结束的返回值
Intpthread_attr_getdetachstat(const pthread_attr_t* restrict attr, int*detachstate);
Intpthread_attr_setdetachstat(const pthread_attr_t* attr, int detachstate);
线程属性初始化和注销
Intpthread_attr_init(pthread_attr_t* attr);
Intpthread_attr_destory(pthread_addr_t* attr);
成功返回0,否则返回错误编码
阶段回顾:
线程的概念
Linux中线程的实现
线程创建和终止
线程清理和控制函数
线程的状态
线程属性初始化
分离属性
管道:
内容概述:
进程间通信概述
管道通信
消息队列
共享内存
信号量
进程间通信概述:
数据传输、
共享数据、
通知事件、
资源共享(锁和同步机制)、
进程控制
进程间通信方式:
1.管道(pipe)和命名管道(FIFO)
2.信号(signal)
3.消息队列
4.共享内存
5.信号量
6.套接字(socket)
1.管道
1.1匿名管道:
在关系进程中才能用
由pipe系统调用,由父进程创建
位于内核空间,其实是一块缓存
1.2命名管道(FIFO)
两个没有任何关系的进程间通信
本质是内核中的一块缓存,在文件系统中以一个特殊的设备文件(管道文件)存在
通过系统调用mkfifo创建
管道创建: int pipe(int fd[2]); fd[0]读 fd[1]写
例1 Int main()
{
Int fd[2];
If( pipi(fd)<0 )
{
Perror(“ creat error\n”); return-1;
}
Else
{
Printf(“succes\n”);
}
Close(fd[0]);
Clsoe(fd[1]);
Return 0;
}
例2 父进程通过管道传输2个数据给子进程,子进程从管道中读取数据
首先创建一个管道,再通过fork创建一个子进程
Int main(int argc,char* argv[] )
{
Int fd[2]; //0读1写
If( pipi(fd)<0 ) //成功返回, 失败返回-1
{
Perror(“ creat error\n”); return-1;
}
Else
{
Pid_tpid;
If( (Pid = fork()) <0 )
{ perror(“创建进程失败”); exit(1); }
Elseif(pid>0) //父进程
{
Close( fd[0] );//关闭读
Int start = 1;
Int end = 100;
If( write(fd[1],&start, sizeof(int)) != sizeof(int) )
{ perror(“写入失败\n”); exit(1); }
If( write(fd[1], &stend, sizeof(int)) !=sizeof(int) )
{ perror(“写入失败\n”); exit(1); }
Close(fd[1]);
Wait(0); //回收子进程,防止产生孤儿进程
}
Else if(pid == 0)//子进程
{
Close( fd[1] );//关闭写
Int start = 0;
Int end = 0;
If( read(fd[0], &start, sizeof(int)) <0 ) //阻塞
{ perror(“读取失败\n”); exit(1); }
If( read(fd[0], &stend,sizeof(int)) <0 ) //阻塞
{ perror(“读取失败\n”); exit(1); }
Close(fd[0]);
Printf(“子进程读的start=%d, end=%d”, start,end);
}
}
Close(fd[0]);
Clsoe(fd[1]);
Return 0;
}
通过打开两个管道来创建一个双向管道
管道默认是阻塞的,当读取时,若没有数据,进程会阻塞
读端不读,写端一直写,管道放满数据就会报错
不完整管道:当读一个写端已关闭的管道时,在所有数据被读完后,read返回0,表示到达文件尾部。 当写一个读端已被关闭的管道,则产生信号SIGPIPE,如果忽略该信号,或者捕捉该信号并从处理程序返回,则write返回-1,同时error设置为EPIPE.
//不完整管道: 读取一个写端已被关闭的管道
Int main(int argc,char* argv[])
{
Int fd[2];
If( pipe(fd) <0 )
{ perror(“创建管道失败\n”); exit(1);}
Pie_t pid;
If( (pid = fork()) < 0 )
{ perror(“创建子线程失败\n”); exit(1);}
else if(pid>0 )//父
{
Sleep(5);
Close(fd[1]);
While(1)
{
Charc;
If(read(fd[0],&c,1) == 0)
{ printf(“写端已经关闭\n”) break;}
Else
{ printf(“%c\n”,c); }
}
Close(fd[0]);
Wait(0);
}
Else //子进程
{
Clsose(fd[0]);
Char* s=”1234”;
Write(fd[1],s,strlen(s));
Close(fd[1]);
}
Return 0;
}
//不完整管道: 写入一个读端已被关闭的管道
Void sig_handler(int signo)
{
If( signo == SIGPIPE)
{ printf(“SIGPIPE发生了 \N”); }
}
Int main(int argc,char* argv[])
{
Int fd[2];
If( pipe(fd) <0 )
{ perror(“创建管道失败\n”); exit(1);}
Pie_t pid;
If( (pid = fork()) < 0 )
{ perror(“创建子线程失败\n”); exit(1);}
else if(pid>0 )//父
{
Sleep(5);
Close(fd[0]);
If(signal(SIGPIPE, sig_handler)==SIG_ERR )
{ perror(“signal sigpipe error\n”) ; exit(1); }
Char*s=”1234”;
If(write(fd[1],s,strlen(s))!= strlen(s) )
{
Fprintf(stderr,“%s%s\n”, strerror(errno),(errno==EPIPE)?”EPIPE”:”UNKOWN”);
}
Close( fd[1] );
Wait(0);
Else
{ close(fd[0]); close(fd[1]); }
Return 0;
}
标准库中管道操作
FILE* popen(const char* cmdstring, const char*type);
成功返回文件指针,出错返回NULL.
Int pclose(FILE*fp);返回值cmdstring的终止状态,出错返回-1
Int main(int argc,char* agrv[])
{
FILE* fp;
Fp = popen(“cat /etc/passwd”,”r”);
Char buff[512];
Memset(buff,0,sizeof(buff));
While(fgets(buff,sizeof(buff),fp) != NULL )
{
Printf(“%s\n”,buff);
}
Pclose(fp);
Printf(“------------------\n”);
Fp = fopen(“wc -l”,”w”);
Fprintf(fp,”1\n2\n3\n”);
Pclose(fp);
Return 0;
}
命名管道(FIFO)
只要对FIFO有适当访问权限,FIFO可用在两个没有关系的进程间通信
本质是内核中的一块缓存,在文件系统中以一个特殊的设备文件(管道文件)存在
在文件系统中只有一个索引快存放文件爱你的路径,没有数据块,所有的数据存放在内核中
命名管道必须读和写同时打开,否则会阻塞
命令mkfifo创建命名管道(命令内部调用mkfifo函数)
对FIFO的操作与操作普通函数一样
用mkfifo创建一个FIFO后,一般的文件I/O函数都可以用于FIFO. Open close read write unlink.....
FIFO出错的信息
EACCES(无存取权限)
.....
........
.....
有名管道的操作
读取
Int main(int argc,char* agrv[])
{
If(argc<2)
{ perror(“参数错误\n”); exit(1);}
Printf(“openfifo for read\n”);
Int fd = open( argv[1],O_RDONLY); //阻塞
或 intfd = open(argv[1],O_RDONLY | O_NONBLOCK);//不阻塞
If(fd<0)
{ perror(“打开错误\n”); exit(1); }
Else
{ perror(“打开成功:%d\n”,fd ); }
Char buff[512];
Memset(buff,0,sizeof(buff));
While(read(fd,buff,sizeof(buff)) < 0 )
{
perror(“打读取失败\n”);
}
Printf(“%s\n”,buff);
close(fd);
return 0;
}
写入
Int main(int argc,char* agrv[])
{
If(argc<2)
{ perror(“参数错误\n”); exit(1);}
Printf(“openfifo for write\n”);
Int fd = open( argv[1],O_WRONLY);
If(fd<0)
{ perror(“打开错误\n”); exit(1); }
Else
{ perror(“打开成功:%d\n”,fd ); }
Char* s = “1234567890”;
Int isize = strlen(s);
If(write(fd,s,isize)!=isize )
{ perror(“write error\n”);}
close(fd);
return 0;
}
匿名管道和命名管道读写相同点
默认都是阻塞
都是用于socket通信
阻塞不完整管道(有一端关闭):当读一个写端已关闭的管道时,在所有数据被读完后,read返回0,表示到达文件尾部。 当写一个读端已被关闭的管道,则产生信号SIGPIPE,如果忽略该信号,或者捕捉该信号并从处理程序返回,则write返回-1,同时error设置为EPIPE.
阻塞完整通道(两端口开启)
单纯读时,要么阻塞,要么读到数据
单纯写时,写到管道满报错
非阻塞不完整管道(有一端关闭):
单纯读时直接报错
单纯写时,产生SIGPIPE,如果忽略该信号或捕捉该信号并从处理程序返回,则write返回-1,同时errno设置为EPIPE
非阻塞完整通道(两端口开启)
单纯读时直接报错
单纯写时,管道满是出错
打开方式不一致
Pipe通过fcntl系统调用来设置O_NONBLOCK来设置非阻塞读写
FIFO通过fcntl系统调用或者open函数来设置非阻塞读写
阶段回顾
进程间通信概述 :管道信号system
管道通信
管道分类:匿名 有名
匿名(pipe)管道特点,创建于读写
管道读写特性
标准库中的管道操作
命名管道FIFO的特点和创建
匿名管道和命名管道的读写区别