unix下C标准文件操作及进程相关知识

2024-08-30 01:18

本文主要是介绍unix下C标准文件操作及进程相关知识,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

close函数可以关闭一个已打开的文件

#include<unistd.h>

int close(int fd);//返回值:若成功返回0;若出错,返回-1并设置errno

 

 

参数fd是要关闭的文件描述符,需要说明的是,当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close(fd)函数关闭,所以即便用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件,但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着文件打开的越来越多,会占用大量文件描述符和系统资源。

 

有open函数返回的文件描述符一定是该进程尚未使用的最小描述符,由于程序启动时自动打开文件描述符0.1.2,因此第一次调用open打开文件通常会返回描述符3,再调用open就会返回4,可以利用这一点在标准输入,标准输出,或标准错误输出上打开一个新文件,实现重定向的功能,例如,首先调用close关闭文件描述符1,然后调用open打开一个常规文件,则一定会返回文件描述符1,这时标准输出就不再是终端,而是一个常规文件了,再调用printf就不会打印到屏幕上,而是写到这个文件中了。

 

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限时间内返回,但是从终端设备或网络读则不一定,如果从终端输入的数据没有换行,调用read读终端设备时就会阻塞,如果网络上没有接收到数据包,调用read从网络上读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在哪里,写常规文件是不会阻塞的,而向终端或网络设备写则不一定,。

 

 

fcntl

先前我们以read终端设备为例介绍了非阻塞的I|o,为什么我们不直接对STDIN_FILENO做非阻塞read而是要重新open以便/dev/tty。因为STDIN_FILENO在程序启动时已经被自动打开了,而且我们需要在调用open时指定O_NONBLOCK标志,这里介绍另外一种办法,可以用fcntl函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File Status Flag),而不必重新open文件

函数原型为

#include<unistd.h>

#include<fcntl.h>

 

 

int fcntl<int fd,int cmd>;

int fcntl(int fd,int cmd,long arg);

int fcntl(int fd,int cmd,struct flock*lock);

这个函数和open一样,使用可变参数来实现的,可变参数的类型和个数取决于前面的cmd参数,下面的例子使用F_GETFL和F_SETFL这两种fcntl命令改变STDIN_FILENO的属性,。

 

ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但是这些数据是不能用read|write读写的,称为out-of-band数据,也就是说,read|write读写的数据是in-band数据,是I|O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据,例如,在串口线上收发数据通过read|write操作,而串口的波特率、校验位、停止位通过ioctl设置,A|D转换的结果是通过read读取,而A|D转换的精度和工作的频率通过ioctl设置。

其函数原型为:

#include<sys/ioctl.h>

int ioctl(int d,int request,.....);

d是某个设备的文件描述符,request是ioctl的命令,可变参数取决于request,通常是一个指向变量或结构体的指针,若出错则返回-1,若成功则返回其他值,返回值取决于request

 

我们知道,每个进程在内核内都有进程控制块(pcb)来维护进程相关的信息,linux内核的进程控制块是task_struct结构体,现在我们来了解其中有哪些信息。

可以形象地描述为

{

进程id,系统中每一个进程都有唯一的id。在C语言中用pid_t类型来表示,其实就是一个非负的整数。

进程的状态,有运行、挂起、停止、僵尸等。

进程切换时需要保存和恢复一些cpu寄存器。

描述虚拟地址空间的信息。

描述控制终端的信息。

当前工作目录

umask掩码

文件描述符表,包括很多指向file结构体的指针

和信号有关的信息

用户id和组id

控制终端、Session和进程组

进程可以使用的资源上限

}

 

环境变量

先前讲过,exec系统调用执行新程序时会把命令行参数和环境变量表传递给main函数,它们在整个进程地址空间中的位置如下图所示

 

和命令行参数argv类似,环境变量表也是一组字符串。libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明。

 

PATH可执行文件的搜索路径,ls也是一个程序,执行它时 不需要提供完整的路径名/bin/ls。然而通常我们执行当前目录下的程序a.out却需要提供完整的路径名./a.out,这是因为PATH环境变量的值里面包含有ls命令所在的目录/bin.却不包含a.out所在的目录,PATH环境变量的值可以包含多个目录,用:号隔开

 

父进程在创建子进程时会复制一份环境变量给子进程,但是此后两者之间的环境变量互不影响

 

 

 

fork函数

一个现有进程可以调用fork函数创建一个新进程

 

#include<unistd.h>

pid_t fork(void);

由fork创建的新进程被称为子进程,fork函数被调用一次,但返回两次,两次返回的唯一区别是子进程的返回值为0,而父进程的返回值为新子进程的进程ID

 

 

下面介绍一个例子

*********************************************************

char*message;

int n;

pid=fork();

if(pid<0)

{

perror("fork failed");

exit(1);

}

if(pid==0)

{

message="this is the child\n";

n=6;

}

else

{

message="this is the parent\n";

n=3;

}

for(;n>0;n--)

{

printf(message);

sleep(1);

}

**************************************************************

fork调用把父进程的数据复制一份给子进程,但此后;两者互不影响,在这个例子中,fork调用之后父进程和子进程的变量message和n被赋值不同的值,互不影响(子进程只是得到父进程的副本,并不共享存储空间。)

 

}

}

 

 

wait和waitpid函数

 

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但是它的PCB还保留着,内核在其中的保存了一些信息,但是他的PCB仍然会保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的具体信号,这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底的清除掉这个信息,我们知道一个进程的退出状态可以再shell中用特殊变量查看,因为shell是它的父进程,当它终止时shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程,

如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时的进程状态被称为僵尸进程,任何进程在刚终止时都是僵尸进程,在正常情况下,僵尸进程都是立即被父进程清理的。为了观察僵尸进程,我们自己写一个不正常的程序,父进程fork出子进程,子进程终止,而父进程既不终止也不调用wait清理子进程

 

 

僵尸进程不能用kill命令来清除掉的,因为kill命令只是用来终止进程你的,而僵尸进程已经种植呢了。

 

 

注释:(

libc是Linux下的ANSI C的函数库。ANSI C是基本的C语言函数库,包含了C语言最基本的 库函数。这个库可以根据 头文件划分为 15 个部分,其中包括:字符类型 ()、 错误码 ()、 浮点常数 ()、数学常数 ()、标准定义 ()、 标准 I/O ()、工具函数 ()、字符串操作 ()、 时间和日期 ()、可变参数表 ()、信号 ()、 非局部跳转 ()、本地信息 ()、程序断言 () 等等。这在其他的C语言的IDE中都是有的。

 

 

每个进程都可以通过一个特殊的设备文件/dev/tty访问它的控制终端,事实上每个终端设备都对应一个不同的设备文件,/dev/tty提供了一个通用的接口,一个进程要访问它的控制终端既可以通过/dev/tty也可以通过该终端设备对应的设备文件来访问,ttyname函数可以由文件描述符查出对应的文件名,该文件描述符必须指向一个终端设备,而不是人任意的文件,

 

 

 

 

 

 

这篇关于unix下C标准文件操作及进程相关知识的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1119390

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

MySQL追踪数据库表更新操作来源的全面指南

《MySQL追踪数据库表更新操作来源的全面指南》本文将以一个具体问题为例,如何监测哪个IP来源对数据库表statistics_test进行了UPDATE操作,文内探讨了多种方法,并提供了详细的代码... 目录引言1. 为什么需要监控数据库更新操作2. 方法1:启用数据库审计日志(1)mysql/mariad

springboot如何通过http动态操作xxl-job任务

《springboot如何通过http动态操作xxl-job任务》:本文主要介绍springboot如何通过http动态操作xxl-job任务的问题,具有很好的参考价值,希望对大家有所帮助,如有错... 目录springboot通过http动态操作xxl-job任务一、maven依赖二、配置文件三、xxl-

Oracle 数据库数据操作如何精通 INSERT, UPDATE, DELETE

《Oracle数据库数据操作如何精通INSERT,UPDATE,DELETE》在Oracle数据库中,对表内数据进行增加、修改和删除操作是通过数据操作语言来完成的,下面给大家介绍Oracle数... 目录思维导图一、插入数据 (INSERT)1.1 插入单行数据,指定所有列的值语法:1.2 插入单行数据,指

CSS3中的字体及相关属性详解

《CSS3中的字体及相关属性详解》:本文主要介绍了CSS3中的字体及相关属性,详细内容请阅读本文,希望能对你有所帮助... 字体网页字体的三个来源:用户机器上安装的字体,放心使用。保存在第三方网站上的字体,例如Typekit和Google,可以link标签链接到你的页面上。保存在你自己Web服务器上的字

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用