# | 数据流 |
0 | 键盘 |
1 | 屏幕 |
2 | 屏幕 |
描述符表的第一列是文件描述符号,另一列是它们对应的数据流,描述符表前三项恒古不变,0号标准输入,1号标准输出,2号标准错误输出,其他项要么为空,要么为链接进程打开的数据流。
怎样返回描述符
FILE * my_file = fopen("xxxx.txt", "r"); int p = fileno(my_file);
复制数据流
dup2(4,3);
将4号描述符的数据流连接到3号描述符
waitpid()函数在sys/wait.h头文件中,作用是等子进程结束后在继续运行父进程
waitpid(进程的pid, 这个变量用来保存进程信息(int指针变量),选项(一般设置为0,设置为0表示等待进程结束))
在第二个参数中会保存一个值,它会记录进程完成情况,可以用WEXITSTATUS()来查看。
二.c语言中的错误处理代码
每次系统调用时都需要写错误处理代码,即出现错误停止程序,并报错。
exit()函数在stdlib.h头文件中,它可以让进程退出并设置退出状态值
错误处理函数的例子:
void error (char * msg) { fprintf(stderr, "%s : %s\n", msg, strerror(errno)); exit(1); }
调用方法
pid_t pid = fork(); if(pid == -1) { error("无法克隆进程!"); }
二.在代码中创建管道
pipe()函数创建两条相连的数据流,与linux命令中的管道符号|效果相同
首先创建一个存储两个字符的数组,pipe()函数会创建一条管道并返回两个描述符,一个描述符读数据,一个描述符写数据
int fd[2]; pipe(fd);
子进程把管道写入端连到标准输出
close(fd[0]) close()关闭管道的数据流 dup2(fd[1], 1); fd[1]向管道写数据
父进程把读取端连到标准输入
dup2(fd[0], 0); close(fd[1]);
其中fd[0]从管道读数据
一条管道只能单向通信,可以创建两条管道进行双向通信
可以反向连接管道从父进程到子进程
三.操作系统可以向程序发送信号
1.信号表如下:
信号名 | 作用 |
SIGINT | 进程被中断 |
SIGQUIT | 有人要求停止进程,并且要求把存储器中的内容保存到核心转储文件 |
SIGFPE | 浮点错误 |
SIGTRAP | 调试人员询问进程执行到了哪里 |
SIGSEGV | 进程企图访问非法存储器地址 |
SIGWINCH | 终端窗口的大小发生改变 |
SIGTERM | 有人要求内核终止进程 |
SIGPIPE | 进程在向一个没有人读的管道写数据 |
2.在使用ctrl+c使用相当于操作系统向程序发送SIGINT信号,默认的信号处理器会调用exit()函数
3.sigaction是一个函数包装器,它有一个函数指针,sigaction告诉操作系统进程收到某个信号时应该调用哪个函数。
sigaction的创建方法
struct sigaction xxxx; xxxx.sa_handler = 函数名字; sigemptyset(&action.sa_mask); 用掩码过滤要处理的信号 xxxx.sa_flags = 0;一些附加标志位将它置0; return sigaction(信号编号, &xxxx, NULL);最后一个选项可以使用&变量名用来保存被替换的信号处理器
使用sigaction所包装的函数叫做信号处理器,信号是一个整型值,如果你创建一个自定义的处理器它的形参需要接收一个整型值。
例如:
void diediedie(int sig) { puts("Goodbye cruel world......\n"); exit(1); }
4.使用kill杀死进程
kill 进程id 向程序发送SIGTERN
kill -INT 进程id 向程序发送SIGINT信号
kill -SEGV 进程id 向程序发送SIGSEGV
kill -KILL 进程id向程序发送SIGKILL信号
5.可以使用raise(信号名)函数让程序向自己发送信号,可以在程序接收到低级别信号时引发更高级别的信号,这叫做信号升级。
6.使用定时器函数
alarm(时间数值以秒为单位)
一个进程最好只设置一个定时器
在n秒后发出SIGALRM信号
7.还原与忽略信号处理器
SIG_DFL以默认方式处理信号
SIG_IGN让进程忽略某个信号
使用方法:
调用前面的使用sigaction注册的处理器函数
函数名(信号,SIG_DEL)以默认方式处理信号 函数名(信号,SIG_IGN)忽略某个信号