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

相关文章

C#中的 Dictionary常用操作

《C#中的Dictionary常用操作》C#中的DictionaryTKey,TValue是用于存储键值对集合的泛型类,允许通过键快速检索值,并且具有唯一键、动态大小和无序集合的特性,常用操作包括添... 目录基本概念Dictionary的基本结构Dictionary的主要特性Dictionary的常用操作

C# winform操作CSV格式文件

《C#winform操作CSV格式文件》这篇文章主要为大家详细介绍了C#在winform中的表格操作CSV格式文件的相关实例,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录实例一实例效果实现代码效果展示实例二实例效果完整代码实例一实例效果当在winform界面中点击读取按钮时 将csv中

linux本机进程间通信之UDS详解

《linux本机进程间通信之UDS详解》文章介绍了Unix域套接字(UDS)的使用方法,这是一种在同一台主机上不同进程间通信的方式,UDS支持三种套接字类型:SOCK_STREAM、SOCK_DGRA... 目录基础概念本机进程间通信socket实现AF_INET数据收发示意图AF_Unix数据收发流程图A

Python中多线程和多进程的基本用法详解

《Python中多线程和多进程的基本用法详解》这篇文章介绍了Python中多线程和多进程的相关知识,包括并发编程的优势,多线程和多进程的概念、适用场景、示例代码,线程池和进程池的使用,以及如何选择合适... 目录引言一、并发编程的主要优势二、python的多线程(Threading)1. 什么是多线程?2.

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表