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

相关文章

Java进程CPU使用率过高排查步骤详细讲解

《Java进程CPU使用率过高排查步骤详细讲解》:本文主要介绍Java进程CPU使用率过高排查的相关资料,针对Java进程CPU使用率高的问题,我们可以遵循以下步骤进行排查和优化,文中通过代码介绍... 目录前言一、初步定位问题1.1 确认进程状态1.2 确定Java进程ID1.3 快速生成线程堆栈二、分析

Python中文件读取操作漏洞深度解析与防护指南

《Python中文件读取操作漏洞深度解析与防护指南》在Web应用开发中,文件操作是最基础也最危险的功能之一,这篇文章将全面剖析Python环境中常见的文件读取漏洞类型,成因及防护方案,感兴趣的小伙伴可... 目录引言一、静态资源处理中的路径穿越漏洞1.1 典型漏洞场景1.2 os.path.join()的陷

Python使用Code2flow将代码转化为流程图的操作教程

《Python使用Code2flow将代码转化为流程图的操作教程》Code2flow是一款开源工具,能够将代码自动转换为流程图,该工具对于代码审查、调试和理解大型代码库非常有用,在这篇博客中,我们将深... 目录引言1nVflRA、为什么选择 Code2flow?2、安装 Code2flow3、基本功能演示

Python中OpenCV与Matplotlib的图像操作入门指南

《Python中OpenCV与Matplotlib的图像操作入门指南》:本文主要介绍Python中OpenCV与Matplotlib的图像操作指南,本文通过实例代码给大家介绍的非常详细,对大家的学... 目录一、环境准备二、图像的基本操作1. 图像读取、显示与保存 使用OpenCV操作2. 像素级操作3.

python操作redis基础

《python操作redis基础》Redis(RemoteDictionaryServer)是一个开源的、基于内存的键值对(Key-Value)存储系统,它通常用作数据库、缓存和消息代理,这篇文章... 目录1. Redis 简介2. 前提条件3. 安装 python Redis 客户端库4. 连接到 Re

Java Stream.reduce()方法操作实际案例讲解

《JavaStream.reduce()方法操作实际案例讲解》reduce是JavaStreamAPI中的一个核心操作,用于将流中的元素组合起来产生单个结果,:本文主要介绍JavaStream.... 目录一、reduce的基本概念1. 什么是reduce操作2. reduce方法的三种形式二、reduce

MySQL表空间结构详解表空间到段页操作

《MySQL表空间结构详解表空间到段页操作》在MySQL架构和存储引擎专题中介绍了使用不同存储引擎创建表时生成的表空间数据文件,在本章节主要介绍使用InnoDB存储引擎创建表时生成的表空间数据文件,对... 目录️‍一、什么是表空间结构1.1 表空间与表空间文件的关系是什么?️‍二、用户数据在表空间中是怎么

Python多进程、多线程、协程典型示例解析(最新推荐)

《Python多进程、多线程、协程典型示例解析(最新推荐)》:本文主要介绍Python多进程、多线程、协程典型示例解析(最新推荐),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 目录一、multiprocessing(多进程)1. 模块简介2. 案例详解:并行计算平方和3. 实现逻

Python对PDF书签进行添加,修改提取和删除操作

《Python对PDF书签进行添加,修改提取和删除操作》PDF书签是PDF文件中的导航工具,通常包含一个标题和一个跳转位置,本教程将详细介绍如何使用Python对PDF文件中的书签进行操作... 目录简介使用工具python 向 PDF 添加书签添加书签添加嵌套书签Python 修改 PDF 书签Pytho

C#通过进程调用外部应用的实现示例

《C#通过进程调用外部应用的实现示例》本文主要介绍了C#通过进程调用外部应用的实现示例,以WINFORM应用程序为例,在C#应用程序中调用PYTHON程序,具有一定的参考价值,感兴趣的可以了解一下... 目录窗口程序类进程信息类 系统设置类 以WINFORM应用程序为例,在C#应用程序中调用python程序