Linux下C语言 编写程序python 创建进程3个进程,三个进程分别完成不同的事情

3597人阅读
【Linux C】(8)
多任务的概念人们已经非常熟悉了,它是指用户可以在同一时间内运行多个应用程序。Linux就是一种支持多任务的操作系统,它支持多进程、多线程等多任务处理和任务之间的多种通信机制。
Linux下多任务机制的介绍
多任务处理是指用户在同一时间内运行多个应用程序,每个应用程序被称做一个任务。Linux就是一个支持多任务的操作系统,它比单任务系统的功能增强了许多。当多任务操作系统使用某种任务调度策略允许两个或更多进程并发共享一个处理器时,事实上处理器在某一时刻只会给一个任务提供服务。由于任务调度机制保证了不同的任务之间的切换速度十分迅速,因此给人多个任务同时运行的错觉。多任务系统中有3个功能单位:任务、进程和线程,下面分别进程介绍。
任务是一个逻辑概念,指由一个软件完成的活动,或者是一系列共同达到某一个目的的操作。通常一个任务是一个程序的一次运行,一个任务包含一个或多个完成独立功能的子任务,这个独立的子任务就是进程或线程。例如,一个杀毒软件的一次运行是一个任务,目的是从各种病毒的侵害中保护计算机系统,这个任务包含多个独立功能的子任务(进程或线程),包含实时监控功能、定时查杀功能、防火墙功能及用户交互功能等。个人理解:就好比假设一个应用程序中由一个或多个可执行文件共同执行组成,那么此应用程序的一次执行就是一个任务,而这些可执行文件的执行就是一个进程的执行,而可执行文件是由一个线程或多个线程构成的,当只有一个线程构成了这个进程,则此时进程和线程就是一样的概念(可执行文件的一次运行)。
进程的基本概念
进程是一个具有独立功能的程序在某个数据集上的一次动态执行过程,它是系统进行资源分配和调度的基本单位(个人理解:系统好比是一个大型的任务,由多个进程(可执行文件)构成,而资源分配和资源调度分别都是一个进程,所以进程是系统进行资源分配和调度的基本单位)。一次任务的运行可以并发激活多个进程,这些进程相互合作来完成该任务的一个最终的目标。
进程具有并发性、动态性、交互性、独立性和异步性等主要特性
并发性:指的是系统中多个进程可以同时并发执行,相互之间不受干扰。动态性:指的是进程都有完整的生命周期,而且在进程的生命周期内,进程的状态是不断变化的。另外,进程具有动态的地址空间(包括代码、数据和进程控制块)。交互性:指的是进程在执行过程中可能会与其他进程发生直接和间接的交互操作,如进程同步和进程互斥等,需要为此添加一定的进程处理机制。独立性:指的是进程是一个相对完整的资源分配和调度的基本单位,各个进程的地址空间是相互独立的,只有采用某些特定的通信机制才能实现进程间的通信。异步性:指的是每个进程都按照各自独立的、不可预知的速度向前执行。
进程和程序是有本质的区别:程序是静态的一段代码,是一些保存在非易失性存储器的指令的有序集合,没有任何执行的概念;而进程是一个动态的概念,它是程序执行的过程,包括动态创建、调度和消亡的整个过程,它是程序执行和资源管理的最小单位。
Linux系统中包括以下几种类型的过程:
交互式过程:这类进程进程与用户进程交互,因此要花很多时间等待用户的交互操作(键盘和鼠标操作等)。当接收到用户的交互操作后,这类进程应该很快被允许,而且相应时间的变化也应该很小,否则用户就会觉得系统反应迟钝或者不太稳定。典型的交互式进程有shell命令进程、文本编辑器和图形应用程序运行等。批处理进程:这类进程不必与用户进行交互,因此进程在后台运行。因为这类进程通常不必很快地相应,因此往往受到调度器的“慢待”。典型的批处理进程有编译器的编译操作、数据库搜索引擎等。实时进程:这类进程通常对调度响应时间有很高的要求,一般不会被低优先级的进程阻塞。它们不仅要求很短的响应时间,而且更重要的是响应时间的变化应该很小。典型的实时进程有视频和音频的应用程序、实时数据采集系统程序等。
Linux下的进程结构
进程不但包括程序的指令和数据,而且包括程序计数器和处理器的所有寄存器以及存储临时数据的进程堆栈,因此正在执行的进程包括处理器当前的一切活动。
因为Linux是一个多进程的操作系统,所以其他的进程必须等到系统将处理器使用权分配各自己之后才能运行。当正在运行的进程等待其他的系统资源时,Linux内核将取得处理器的控制权,并将处理器分配给其他正在等待的进程,它按照内核中的调度算法决定处理器分配给哪个进程。
内核将所有进程存放在双向循环链表(进程链表)中,其中链表的头是init_task描述符。链表的每一项都是类型为task_struct,称为进程描述符的结构,该结构包含了与一个进程相关的所有信息,定义在&include/linux/sched.h&文件中。task_struct内核结构比较大,它能完整地描述一个进程,如进程的状态、进程的基本信息、进程标识符、内存相关信息、父进程相关信息、与进程相关的终端信息、当前工作目录、打开的文件信息、所接收到的信号信息等。
下面详细讲解task_struct结构中最为重要的两个域:state(进程状态)和pid(进程标识符,即进程号)。
1)进程状态,Linux中的进程有以下几种状态
运行状态(TASK_RUNNING):进程当前正在运行,或者正在运行队列中等待调度。
创建一个task.c文件,task.c文件内容如下:
保存后,输入gcc task.c -o task编译生成二进制代码task,输入./task运行task进程
打开另一个终端,输入ps -aux查看进程状态:(ps -axjf
可查看进程有哪些子进程,ps -e 也 可以查到进程的状态,但只显示进程的PID、TTY、TIME和CMD)
ps工具标识进程的5中状态码:
D 不可中断 uninterruptible sleep (usually IO)
R 运行 runnable (on run queue)
S 中断 sleeping
T 停止 traced or stopped
Z 僵尸 a defunct (&zombie&) process
注:其它状态还包括W(无驻留页),&(高优先级进程),N(低优先级进程),L(内存锁页)
每列对应关系:
USER:进程所有者 & & &
PID:进程ID & &
%CPU:占用CPU的使用率 &&
%MEM:占用内存的使用率 &&
VSZ:占用虚拟内存大小&
&RSS:占用内存大小&
TTY:终端次要装置号码 &&
STAT:进程状态 &
START:进程启动时间 & &
TIME:进程消耗cup时间 &&
COMMAND:命令的名称和参数
可中断的阻塞状态(TASK_INTERRUPTIBLE):进程处于阻塞(睡眠)状态,正在等待某些事件发生或能够占用某些资源。处在这种状态下的进程可以被信号中断。接收到信号或被显式的唤醒呼叫(如调用wake_up系列宏:wake_up、wake_up_interruptible等)唤醒之后,进程转变为TASK_RUNNING状态。不可中断的阻塞状态(TASK_UNINTERRUPTIBLE):此进程状态类似于可中断的阻塞状态(TASK_INTERRUPTILBE),只是它不会处理信号,把信号传递到这种状态下的进程不能改变它的状态。在一些特定的情况下(进程必须等待,直到某些不可被中断的事件发生),这种状态是很有用的。只有在它所等待的事件发生时,进程被显式的唤醒呼叫唤醒。可终止的阻塞状态(TASK_KILLABLE):Linux内核2.6.25引入了一种新的进程状态,名为TASK_KILLABLE。该状态的运行机制类似于TASK_UNINTERRUPTILBE,只不过在该状态下的进程可以响应致命信号。它可以替代有效但可能无法终止的不可中断的阻塞状态,以及易于唤醒安全性欠佳的可中断的阻塞状态。暂停状态(TASK_STOPPED):进程的执行被暂停,当进程收到SIGTOP、SIGTSTP、SIGTTIN、SIGTTOU等信号时,就会进入暂停状态。跟踪状态(TASK_TRACED):进程的执行被调试器暂停。当一个进程被另一个进程监控是(如调试器使用ptrace()系统调用监控测试程序),任何信号都可以把这个进程置于跟踪状态。僵尸状态(EXIT_ZOMBIE):进程运行结束,父进程尚未使用wait函数族(如使用waitpid()函数)等系统调用来“收尸”,即等待父进程销毁它。处于该状态下的进程“实体”已经放弃了几乎所有的内存空间,没有任何可执行代码,也不能调度,仅仅在进程列表保留一个位置,记载该进程的退出状态等信息供其他进程收集。僵尸撤销状态(EXIT_DEAD):这是最终状态,父进程调用wait函数族“收尸”后,进程彻底有系统删除。
它们之间的转换关系如图所示:
内核可以使用set_task_state和set_current_state宏来改变指定进程的状态和当前执行进程的状态。
2)进程标识符
Linux内核通过唯一的进程标识符PID来标识每个进程。PID存放进程描述符的pid字段中,新创建的PID通常是前一个进程的PID加1,不过PID的值有上限(最大值 = PID_MAX_DEFAULT - 1,通常为32767),我们可以在终端输入 vim /proc/sys/kernel/pid_max 来确定该系统的进程数上限。
当系统启动后,内核通常作为一个进程的代表。一个指向task_struct的宏current用来记录正在运行的进程。current经常作为进程描述符结构指针的形式出现在内核代码中,例如,current-&pid表示处理器正在执行进程的PID。当系统需要查看所有的进程时,则调用for_each_process()宏,这将比系统搜索数组的速度要快得多。
在Linux中获得当前进程的进程号(PID)和父进程号(PPID)的系统调用函数分别为getpid()和getppid()。
测试代码:
测试结果:
输入 ps -axjf 命令查看所有进程与父进程
我们在次输入ps -aux命令查看所有进程,可以得知父进程为bash
进程的创建、执行和终止
1)进程的创建和执行
许多操作系统提供的都是产生进程的机制,也就是说,首先在新的地址空间里创建进程、读入可执行文件、最后在开始执行。Linux中进程的穿件很特别,它把上述步骤分解到两个单独的函数中去执行:fork()和exec函数族。首先fork()函数通过复制当前进程创建一个子进程,子进程与父进程的区别在于不同的PID、PPID和某些资源及统计量。exec函数族负责读取可执行文件并将其载入地址空间开始运行。
要注意的是,Linux中的fork()函数使用的是写时复制页的技术,也就是内核在创建进程时,其资源并没有被复制过来,资源的复制仅仅只有在需要写入数据时才发生,在此之前只是以只读的方式共享数据。写时复制技术可以使Linux拥有快速执行的能力,因此这个优化是非常重要的。
2)进程的终止
进程终结也需要做很多繁琐的收尾工作,系统必须保证回收进程所占的资源,并通知父进程。Linux首先把终止的进程设置为僵尸状态,这时,进程无法投入运行,它的存在只为父进程提供信息,申请死亡。父进程得到信息后,开始调用wait函数族,最后终止子进程,子进程占用的所有资源被全部释放。
进程的内存结构
Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自互不干涉的进程地址空间。该地址空间是大小为4GB的线性虚拟空间(当然是指32位系统),用户所看到和接触到的都是该虚拟地址,无法看到实际的物理内存地址。利用这种虚拟地址不但能起到保护操作系统的效果(用户不能直接访问物理内存),而且更重要的是,用户程序可以使用比实际物理内存更大的地址空间。
我们可以通过命令getconf LONG_BIT 来查询当前自己的系统是多少位的?
我的安装的UbuntKylin是64位的,即实际内存最大可能达到2^64 = 128GB。(2^10 = 1kb,2^30 = 1GB)。
4GB的进程地址空间会被分出两部分:用户空间与内核空间。用户地址空间是从0~3GB(0xC0000000),内存地址空间占据3GB~4GB。用户进程通常情况下只能访问用户控件的虚拟地址,不能访问内核空间的虚拟地址。只有用户进程使用系统调用(代表用户进程在内核执行)时可以访问内核空间的虚拟空间。每当进程切换时,用户空间就会跟着变化;而内核空间有内核负责映射,它并不会跟着进程改变而改变,是固定的。内核空间地址有自己对应的页表,用户进程各自用不同的页表。每个进程用户空间都是完全独立、互不相干的。进程的虚拟内存地址空间如图所示:
其中用户空间包括以下几个功能区域:
只读段:包含程序代码(.init和.exit)和只读数据(.rodata)数据段:存放的是全局变量和静态变量。其中可读可写数据段(.data)存放已经初始化的全局变量和静态变量,BSS数据段(.bss)存放未初始化的全局变量和静态变量堆:由系统自动分配释放,存放函数的参数值、局部变量的值、返回地址等堆栈:存放动态分配的数据,一般由程序员动态分配和释放。若程序员不释放,程序结束时可能由操作系统回收。共享库的内存映射区域:这是Linux动态连接器和其他共享库代码的映射区域。
由于在Linux系统中每一个进程都会有/proc文件系统下与之对应的一个目录(如将init进程的相关信息在/proc/1 目录下的文件中描述,1表示init进程的进程号),因此通过proc文件系统可以查看某个进程的地址空间的映射情况。
测试代码:
运行此程序:
输入 size task
text:存放的是代码 & & data:存放的是初始化过的全局变量或静态变量 & bss:存放的是未初始化的全局变量或静态变量
输入命令 cat /proc/3834/maps & 其中3834是task的PID
前面已经提到,进程是系统中程序执行和资源分配的基本单位。每个进程都拥有自己的数据段、代码段和堆栈段,这就造成了进程进程切换等操作时需要较复杂的上下文切换等动作。为了进一步减少处理机制的空转时间,支持多处理器及减少上下文切换开销,进程在演化中出现了另一个概念——线程。它是进程内独立的一条运行路线,是处理器调用的最小单元,也可以成为轻量级进程。线程可以对进程的内存空间和资源进程访问,并与同一个进程中的其他线程共享。因此,线程上下文切换的开销比创建进程小得多。
一个进程可以拥有多个线程,每个线程必须有一个父进程。线程不拥有系统资源,它只具有运行所必需的一些数据,如堆栈、寄存器与线程控制块(TCB),线程与其父进程的其他线程共享该进程所拥有的全部资源。要注意的是,由线程共享了进程的资源和地址空间,因此,任何线程对系统资源的操作都会给其他线程带来影响。由此可知,多线程中的同步是非常重要的问题。在多线程系统中,进程与线程的关系如图所示:
在Linux系统中,线程可以分为以下3种:
用户级线程
用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自己选择决定,在运行时不需要特定的内核支持。在这里,操作系统往往会提供一个用户空间的线程库,该线程库提供了线程的创建、调度和撤销等功能,而内核仍然仅对进程进行管理。如果一个进程中的某一个线程调用了一个阻塞的系统调用函数,那么该进程好吧该进程中的其他所有线程也同时被阻塞。这种用户级线程的主要缺点是在一个进程的多个线程的调度中无法发挥多处理器的优势。
轻量级进程
轻量级进程是内核支持的用户线程,是内核线程的一种抽象对象。每个线程拥有一个或多个轻量级进程,而每个轻量级进程分别被绑定在一个内核线程上。
内核线程允许不同进程中的线程按照同一相对优先调度方法进行调度,这样就可以发挥多处理器的并发优势。现在大多数系统都采用用户级线程与核心级线程并存的方法。一个用户级线程可以对应一个或几个核心级线程,也就是“一对一”或“多对一”模型。这样既可以满足多处理系统的需要,也可以最大限度地减少调度开销。
使用线程机制大大加快了上下文切换速度,而节省了很多资源。但是因为在用户态和内核态均要实现调度管理,所有会增加实现的复杂度和引起优先级翻转的可能性。同时,一个多线程程序的同步设计与调试也会增加程序实现的难道。
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:171715次
积分:2206
积分:2206
排名:第19680名
原创:51篇
(10)(16)(25)(5)网站已改版,请使用新地址访问:
threecodes 1、 用C语言写一个程序名字为procs4, 运行过程中共有4个进程, ( Linux-Unix program 238万源代码下载- www.pudn.com
&文件名称: threecodes
& & & & &&]
&&所属分类:
&&开发工具: Unix_Linux
&&文件大小: 2 KB
&&上传时间:
&&下载次数: 56
&&提 供 者:
&详细说明:1、 用C语言写一个程序名字为procs4,该程序运行过程中共有4个进程,procs4程序(父进程)创建2个子进程p1和p2,p1子进程再创建一个子进程p3。4个进程完成如下工作:
父进程并打印字符串“I am main process”;p1子进程打印“I am child process p1”字符串;p2子进程打印“I am child process p2”;子进程p3打印字符串“I am child process p3”,然后使用exec(族)系统调用打印当前目录下文件和子目录下的详细信息。并且每个进程的要打印自己的pid。
2、 使用任何一种通信机制实现p1进程和p2进程之间的通信,可以使用的通信机制如:管道、消息队列、共享内存等。P1进程发送“Child process p1 is sending a message!”信息给p2进程,P2进程发送“Child process p2 is sending a message!” 信息给p1进程,p1和p2两个子进程接受信息后分别打印着两个字符串。
3、 编写程序实现生产者-消费者问题。使用linux的Pthread线程库,创建生产者和消费者两个线程,生产者线程计算当前的时间并放入缓冲区,每次计算一个时间数据;消费者线程从缓冲区读出并打印生产者计算的时间,每次打印一个时间数据。缓冲区大小为5个,生产和消费的消息数为10个,即生产和消费分别为10次。-1, using C language to write a program named procs4, the program is running a total of four processes, procs4 process (parent process) to create two sub-processes p1 and p2, p1 sub-process re-creates a child process p3. 4 key process to complete the following work:
The parent process and print the string "I am main process"
p1 child process print "I am child process p1" string
p2 child process print "I am child process p2"
child process p3 print the string "I am child process p3 ", and then use the exec (tribe) system call to print the current directory files and subdirectories under the details. And each process, you want to print their own pid.
2, using any kind of communication mechanism to achieve the p1 and p2 the process of communication between processes, you can use the communication mechanisms such as: pipes, message queues, shared memory, and so on. Process P1 sends "Child process p1 is sending a message!" Message to the p2 process, P2 process sends "Child process p2 i
文件列表(点击判断是否您需要的文件,如果是垃圾请在下面评价投诉):
&&2.txt&&3.txt&&1.txt
&[]:很好,推荐下载
&相关搜索:
&输入关键字,在本站238万海量源码库中尽情搜索:
&[] - 用以演示他提出的信号量机制。本作业要求设计在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。
在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么
&[] - 根据自定义的协议规范,使用Socket编程接口编写基本的网络应用软件。
使用操作系统提供的Socket编程接口,而不使用任何封装Socket接口的类库或组件
协议可以自己定义,只要完成指定的功能即可
程序界面不做要求,使用命令行或最简单的窗
&[] - linux下多线程实现生产者消费者及用并行算法求圆周率等经典算法
&[] - linux下的关于操作系统的,生产者消费者的问题的程序。
&[] - 支持向量机和BP神经网络虽然都可以用来做非线性回归,但它们所基于的理论基础不同,回归的机理也不相同。支持向量机基于结构风险最小化理论,普遍认为其泛化能力要比神经网络的强。为了验证这种观点,本文编写了支持向量机非线性回归的通用Matlab程序和基于神经网络工具箱的BP神经网络仿真模块,仿真结果证实,支
&[] - c++进程间通信,消息队列实例,需要的朋友情看看!
&[] - 使用c语言实现linux下的ls命令,功能齐全,代码精简,适合初学者进行学习与研究
&[] - 编写程序实现生产者-消费者问题。使用Linux的pthread线程库,创建2个生产者线程和2
个消费者线程。生产者线程计算当前的时间,把时间、第几次计算时间的序号(循环次
数)和线程ID作为一个消息,把消息放入缓冲区,消费者线程从缓冲区读出一个消息
并显示消息。缓冲区大小为5个,每个生产者线程查看: 2911|回复: 12
linux操作系统下c语言编程入门
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
才可以下载或查看,没有帐号?
linux操作系统下c语言编程入门 &BR&整理编写:007xiong &BR&原文:Hoyt等 &BR&&BR&(一)目录介绍 &BR&&BR&1)Linux程序设计入门--基础知识 &BR&2)Linux程序设计入门--进程介绍 &BR&3)Linux程序设计入门--文件操作 &BR&4)Linux程序设计入门--时间概念 &BR&5)Linux程序设计入门--信号处理 &BR&6)Linux程序设计入门--消息管理 &BR&7)Linux程序设计入门--线程操作 &BR&8)Linux程序设计入门--网络编程 &BR&9)Linux下C开发工具介绍 &BR&&BR&(二)具体内容 &BR&&BR&1)Linux程序设计入门--基础知识 &BR&Linux下C语言编程基础知识 &BR&前言: &BR&这篇文章介绍在LINUX下进行C语言编程所需要的基础知识.在这篇文章当中,我们将 &BR&会学到以下内容: &BR&源程序编译 &BR&Makefile的编写 &BR&程序库的链接 &BR&程序的调试 &BR&头文件和系统求助 &BR&---------------------------------------------------------------------------- &BR&---- &BR&1.源程序的编译 &BR&在Linux下面,如果要编译一个C语言源程序,我们要使用GNU的gcc编译器. 下面我们 &BR&以一个实例来说明如何使用gcc编译器. &BR&假设我们有下面一个非常简单的源程序(hello.c): &BR&int main(int argc,char **argv) &BR&{ &BR&printf(&Hello Linux\n&); &BR&} &BR&要编译这个程序,我们只要在命令行下执行: &BR&gcc -o hello hello.c &BR&gcc 编译器就会为我们生成一个hello的可执行文件.执行./hello就可以看到程序的输出 &BR&结果了.命令行中 gcc表示我们是用gcc来编译我们的源程序,-o 选项表示我们要求编译 &BR&器给我们输出的可执行文件名为hello 而hello.c是我们的源程序文件. &BR&gcc编译器有许多选项,一般来说我们只要知道其中的几个就够了. -o选项我们已经知道 &BR&了,表示我们要求输出的可执行文件名. -c选项表示我们只要求编译器输出目标代码,而 &BR&不必要输出可执行文件. -g选项表示我们要求编译器在编译的时候提供我们以后对程序 &BR&进行调试的信息. &BR&知道了这三个选项,我们就可以编译我们自己所写的简单的源程序了,如果你想要知道更 &BR&多的选项,可以查看gcc的帮助文档,那里有着许多对其它选项的详细说明. &BR&2.Makefile的编写 &BR&假设我们有下面这样的一个程序,源代码如下: &BR&/* main.c */ &BR&#include &mytool1.h& &BR&#include &mytool2.h& &BR&int main(int argc,char **argv) &BR&{ &BR&mytool1_print(&hello&); &BR&mytool2_print(&hello&); &BR&} &BR&/* mytool1.h */ &BR&#ifndef _MYTOOL_1_H &BR&#define _MYTOOL_1_H &BR&void mytool1_print(char *print_str); &BR&#endif &BR&/* mytool1.c */ &BR&#include &mytool1.h& &BR&void mytool1_print(char *print_str) &BR&{ &BR&printf(&This is mytool1 print %s\n&,print_str); &BR&} &BR&/* mytool2.h */ &BR&#ifndef _MYTOOL_2_H &BR&#define _MYTOOL_2_H &BR&void mytool2_print(char *print_str); &BR&#endif &BR&/* mytool2.c */ &BR&#include &mytool2.h& &BR&void mytool2_print(char *print_str) &BR&{ &BR&printf(&This is mytool2 print %s\n&,print_str); &BR&} &BR&当然由于这个程序是很短的我们可以这样来编译 &BR&gcc -c main.c &BR&gcc -c mytool1.c &BR&gcc -c mytool2.c &BR&gcc -o main main.o mytool1.o mytool2.o &BR&这样的话我们也可以产生main程序,而且也不时很麻烦.但是如果我们考虑一下如果有一 &BR&天我们修改了其中的一个文件(比如说mytool1.c)那么我们难道还要重新输入上面的命令 &BR&?也许你会说,这个很容易解决啊,我写一个SHELL脚本,让她帮我去完成不就可以了.是的 &BR&对于这个程序来说,是可以起到作用的.但是当我们把事情想的更复杂一点,如果我们的程 &BR&序有几百个源程序的时候,难道也要编译器重新一个一个的去编译? &BR&为此,聪明的程序员们想出了一个很好的工具来做这件事情,这就是make.我们只要执行以 &BR&下make,就可以把上面的问题解决掉.在我们执行make之前,我们要先编写一个非常重要的 &BR&文件.--Makefile.对于上面的那个程序来说,可能的一个Makefile的文件是: &BR&# 这是上面那个程序的Makefile文件 &BR&main:main.o mytool1.o mytool2.o &BR&gcc -o main main.o mytool1.o mytool2.o &BR&main.main.c mytool1.h mytool2.h &BR&gcc -c main.c &BR&mytool1.mytool1.c mytool1.h &BR&gcc -c mytool1.c &BR&mytool2.mytool2.c mytool2.h &BR&gcc -c mytool2.c &BR&有了这个Makefile文件,不过我们什么时候修改了源程序当中的什么文件,我们只要执行 &BR&make命令,我们的编译器都只会去编译和我们修改的文件有关的文件,其它的文件她连理 &BR&都不想去理的. &BR&下面我们学习Makefile是如何编写的. &BR&在Makefile中也#开始的行都是注释行.Makefile中最重要的是描述文件的依赖关系的说 &BR&明.一般的格式是: &BR&target: components &BR&TAB rule &BR&第一行表示的是依赖关系.第二行是规则. &BR&比如说我们上面的那个Makefile文件的第二行 &BR&main:main.o mytool1.o mytool2.o &BR&表示我们的目标(target)main的依赖对象(components)是main.o mytool1.o mytool2.o &BR&当倚赖的对象在目标修改后修改的话,就要去执行规则一行所指定的命令.就象我们的上 &BR&面那个Makefile第三行所说的一样要执行 gcc -o main main.o mytool1.o mytool2.o &BR&注意规则一行中的TAB表示那里是一个TAB键 &BR&Makefile有三个非常有用的变量.分别是$@,$^,$&代表的意义分别是: &BR&$@--目标文件,$^--所有的依赖文件,$&--第一个依赖文件. &BR&如果我们使用上面三个变量,那么我们可以简化我们的Makefile文件为: &BR&# 这是简化后的Makefile &BR&main:main.o mytool1.o mytool2.o &BR&gcc -o $@ $^ &BR&main.main.c mytool1.h mytool2.h &BR&gcc -c $& &BR&mytool1.mytool1.c mytool1.h &BR&gcc -c $& &BR&mytool2.mytool2.c mytool2.h &BR&gcc -c $& &BR&经过简化后我们的Makefile是简单了一点,不过人们有时候还想简单一点.这里我们学习 &BR&一个Makefile的缺省规则 &BR&..c. &BR&gcc -c $& &BR&这个规则表示所有的 .o文件都是依赖与相应的.c文件的.例如mytool.o依赖于mytool.c &BR&这样Makefile还可以变为: &BR&# 这是再一次简化后的Makefile &BR&main:main.o mytool1.o mytool2.o &BR&gcc -o $@ $^ &BR&..c. &BR&gcc -c $& &BR&好了,我们的Makefile 也差不多了,如果想知道更多的关于Makefile规则可以查看相应的 &BR&文档. &BR&3.程序库的链接 &BR&试着编译下面这个程序 &BR&/* temp.c */ &BR&#include &math.h& &BR&int main(int argc,char **argv) &BR&{ &BR& &BR&printf(&Value:%f\n&,value); &BR&} &BR&这个程序相当简单,但是当我们用 gcc -o temp temp.c 编译时会出现下面所示的错误. &BR&&BR&/tmp/cc33Kydu. In function `main': &BR&/tmp/cc33Kydu.o(.text+0xe): undefined reference to `log' &BR&collect2: ld returned 1 exit status &BR&出现这个错误是因为编译器找不到log的具体实现.虽然我们包括了正确的头文件,但是我 &BR&们在编译的时候还是要连接确定的库.在Linux下,为了使用数学函数,我们必须和数学库 &BR&连接,为此我们要加入 -lm 选项. gcc -o temp temp.c -lm这样才能够正确的编译.也许 &BR&有人要问,前面我们用printf函数的时候怎么没有连接库呢?是这样的,对于一些常用的函 &BR&数的实现,gcc编译器会自动去连接一些常用库,这样我们就没有必要自己去指定了. 有时 &BR&候我们在编译程序的时候还要指定库的路径,这个时候我们要用到编译器的 -L选项指定 &BR&路径.比如说我们有一个库在 /home/hoyt/mylib下,这样我们编译的时候还要加上 -L/h &BR&ome/hoyt/mylib.对于一些标准库来说,我们没有必要指出路径.只要它们在起缺省库的路 &BR&径下就可以了.系统的缺省库的路径/lib /usr/lib /usr/local/lib 在这三个路径下面 &BR&的库,我们可以不指定路径. &BR&还有一个问题,有时候我们使用了某个函数,但是我们不知道库的名字,这个时候怎么办呢 &BR&?很抱歉,对于这个问题我也不知道答案,我只有一个傻办法.首先,我到标准库路径下面去 &BR&找看看有没有和我用的函数相关的库,我就这样找到了线程(thread)函数的库文件(libp &BR&thread.a). 当然,如果找不到,只有一个笨方法.比如我要找sin这个函数所在的库. 就只 &BR&好用 nm -o /lib/*.so|grep sin&~/sin 命令,然后看~/sin文件,到那里面去找了. 在s &BR&in文件当中,我会找到这样的一行libm-2.1.2.s00009fa0 W sin 这样我就知道了sin在 &BR&libm-2.1.2.so库里面,我用 -lm选项就可以了(去掉前面的lib和后面的版本标志,就剩 &BR&下m了所以是 -lm). 如果你知道怎么找,请赶快告诉我,我回非常感激的.谢谢! &BR&4.程序的调试 &BR&我们编写的程序不太可能一次性就会成功的,在我们的程序当中,会出现许许多多我 &BR&们想不到的错误,这个时候我们就要对我们的程序进行调试了. &BR&最常用的调试软件是gdb.如果你想在图形界面下调试程序,那么你现在可以选择xxgdb.记 &BR&得要在编译的时候加入 -g选项.关于gdb的使用可以看gdb的帮助文件.由于我没有用过这 &BR&个软件,所以我也不能够说出如何使用. 不过我不喜欢用gdb.跟踪一个程序是很烦的事情 &BR&,我一般用在程序当中输出中间变量的值来调试程序的.当然你可以选择自己的办法,没有 &BR&必要去学别人的.现在有了许多IDE环境,里面已经自己带了调试器了.你可以选择几个试 &BR&一试找出自己喜欢的一个用. &BR&5.头文件和系统求助 &BR&有时候我们只知道一个函数的大概形式,不记得确切的表达式,或者是不记得着函数 &BR&在那个头文件进行了说明.这个时候我们可以求助系统. &BR&比如说我们想知道fread这个函数的确切形式,我们只要执行 man fread 系统就会输出着 &BR&函数的详细解释的.和这个函数所在的头文件&stdio.h&说明了. 如果我们要write这个函 &BR&数的说明,当我们执行man write时,输出的结果却不是我们所需要的. 因为我们要的是w &BR&rite这个函数的说明,可是出来的却是write这个命令的说明.为了得到write的函数说明 &BR&我们要用 man 2 write. 2表示我们用的write这个函数是系统调用函数,还有一个我们常 &BR&用的是3表示函数是C的库函数. &BR&记住不管什么时候,man都是我们的最好助手. &BR&------------------------------------------------------------------------ &BR&好了,这一章就讲这么多了,有了这些知识我们就可以进入激动人心的Linux下的C程序探 &BR&险活动. &BR&
回复:(风花雪月)linux操作系统下c语言编程入门
2)Linux程序设计入门--进程介绍 &BR&Linux下进程的创建 &BR&前言: &BR&这篇文章是用来介绍在Linux下和进程相关的各个概念.我们将会学到: &BR&进程的概念 &BR&进程的身份 &BR&进程的创建 &BR&守护进程的创建 &BR&---------------------------------------------------------------------------- &BR&---- &BR&1。进程的概念 &BR&Linux操作系统是面向多用户的.在同一时间可以有许多用户向操作系统发出各种命 &BR&令.那么操作系统是怎么实现多用户的环境呢? 在现代的操作系统里面,都有程序和进程 &BR&的概念.那么什么是程序,什么是进程呢? 通俗的讲程序是一个包含可以执行代码的文件 &BR&,是一个静态的文件.而进程是一个开始执行但是还没有结束的程序的实例.就是可执行文 &BR&件的具体实现. 一个程序可能有许多进程,而每一个进程又可以有许多子进程.依次循环 &BR&下去,而产生子孙进程. 当程序被系统调用到内存以后,系统会给程序分配一定的资源(内 &BR&存,设备等等)然后进行一系列的复杂操作,使程序变成进程以供系统调用.在系统里面只 &BR&有进程没有程序,为了区分各个不同的进程,系统给每一个进程分配了一个ID(就象我们的 &BR&身份证)以便识别. 为了充分的利用资源,系统还对进程区分了不同的状态.将进程分为新 &BR&建,运行,阻塞,就绪和完成五个状态. 新建表示进程正在被创建,运行是进程正在运行,阻 &BR&塞是进程正在等待某一个事件发生,就绪是表示系统正在等待CPU来执行命令,而完成表示 &BR&进程已经结束了系统正在回收资源. 关于进程五个状态的详细解说我们可以看《操作系 &BR&统》上面有详细的解说。 &BR&2。进程的标志 &BR&上面我们知道了进程都有一个ID,那么我们怎么得到进程的ID呢?系统调用getpid可 &BR&以得到进程的ID,而getppid可以得到父进程(创建调用该函数进程的进程)的ID. &BR&#include &unistd& &BR&pid_t getpid(void); &BR&pid_t getppid(void); &BR&进程是为程序服务的,而程序是为了用户服务的.系统为了找到进程的用户名,还为进程和 &BR&用户建立联系.这个用户称为进程的所有者.相应的每一个用户也有一个用户ID.通过系统 &BR&调用getuid可以得到进程的所有者的ID.由于进程要用到一些资源,而Linux对系统资源是 &BR&进行保护的,为了获取一定资源进程还有一个有效用户ID.这个ID和系统的资源使用有关 &BR&,涉及到进程的权限. 通过系统调用geteuid我们可以得到进程的有效用户ID. 和用户ID &BR&相对应进程还有一个组ID和有效组ID系统调用getgid和getegid可以分别得到组ID和有效 &BR&组ID &BR&#include &unistd& &BR&#include &sys/types.h& &BR&&BR&uid_t getuid(void); &BR&uid_t geteuid(void); &BR&gid_t getgid(void); &BR&git_t getegid(void); &BR&有时候我们还会对用户的其他信息感兴趣(登录名等等),这个时候我们可以调用getpwui &BR&d来得到. &BR&struct passwd { &BR&char *pw_ /* 登录名称 */ &BR&char *pw_ /* 登录口令 */ &BR&uid_t pw_ /* 用户ID */ &BR&gid_t pw_ /* 用户组ID */ &BR&char *pw_ /* 用户的真名 */ &BR&char *pw_ /* 用户的目录 */ &BR&char *pw_ /* 用户的SHELL */ &BR&}; &BR&#include &pwd.h& &BR&#include &sys/types.h& &BR&&BR&struct passwd *getpwuid(uid_t uid); &BR&下面我们学习一个实例来实践一下上面我们所学习的几个函数: &BR&#include &unistd.h& &BR&#include &pwd.h& &BR&#include &sys/types.h& &BR&#include &stdio.h& &BR&int main(int argc,char **argv) &BR&{ &BR&pid_t my_pid,parent_ &BR&uid_t my_uid,my_ &BR&gid_t my_gid,my_ &BR&struct passwd *my_ &BR&my_pid=getpid(); &BR&parent_pid=getppid(); &BR&my_uid=getuid(); &BR&my_euid=geteuid(); &BR&my_gid=getgid(); &BR&my_egid=getegid(); &BR&my_info=getpwuid(my_uid); &BR&printf(&Process ID:%ld\n&,my_pid); &BR&printf(&Parent ID:%ld\n&,parent_pid); &BR&printf(&User ID:%ld\n&,my_uid); &BR&printf(&Effective User ID:%ld\n&,my_euid); &BR&printf(&Group ID:%ld\n&,my_gid); &BR&printf(&Effective Group ID:%ld\n&,my_egid): &BR&if(my_info) &BR&{ &BR&printf(&My Login Name:%s\n& ,my_info-&pw_name); &BR&printf(&My Password :%s\n& ,my_info-&pw_passwd); &BR&printf(&My User ID :%ld\n&,my_info-&pw_uid); &BR&printf(&My Group ID :%ld\n&,my_info-&pw_gid); &BR&printf(&My Real Name:%s\n& ,my_info-&pw_gecos); &BR&printf(&My Home Dir :%s\n&, my_info-&pw_dir); &BR&printf(&My Work Shell:%s\n&, my_info-&pw_shell); &BR&} &BR&} &BR&3。进程的创建 &BR&创建一个进程的系统调用很简单.我们只要调用fork函数就可以了. &BR&#include &unistd.h& &BR&&BR&pid_t fork(); &BR&当一个进程调用了fork以后,系统会创建一个子进程.这个子进程和父进程不同的地方只 &BR&有他的进程ID和父进程ID,其他的都是一样.就象符进程克隆(clone)自己一样.当然创建 &BR&两个一模一样的进程是没有意义的.为了区分父进程和子进程,我们必须跟踪fork的返回 &BR&值. 当fork掉用失败的时候(内存不足或者是用户的最大进程数已到)fork返回-1,否则f &BR&ork的返回值有重要的作用.对于父进程fork返回子进程的ID,而对于fork子进程返回0.我 &BR&们就是根据这个返回值来区分父子进程的. 父进程为什么要创建子进程呢?前面我们已经 &BR&说过了Linux是一个多用户操作系统,在同一时间会有许多的用户在争夺系统的资源.有时 &BR&进程为了早一点完成任务就创建子进程来争夺资源. 一旦子进程被创建,父子进程一起从 &BR&fork处继续执行,相互竞争系统的资源.有时候我们希望子进程继续执行,而父进程阻塞直 &BR&到子进程完成任务.这个时候我们可以调用wait或者waitpid系统调用. &BR&#include &sys/types.h& &BR&#include &sys/wait.h& &BR&&BR&pid_t wait(int *stat_loc); &BR&pid_t waitpid(pid_t pid,int *stat_loc,int options); &BR&wait系统调用会使父进程阻塞直到一个子进程结束或者是父进程接受到了一个信号.如果 &BR&没有父进程没有子进程或者他的子进程已经结束了wait回立即返回.成功时(因一个子进 &BR&程结束)wait将返回子进程的ID,否则返回-1,并设置全局变量errno.stat_loc是子进程的 &BR&退出状态.子进程调用exit,_exit 或者是return来设置这个值. 为了得到这个值Linux定 &BR&义了几个宏来测试这个返回值. &BR&WIFEXITED:判断子进程退出值是非0 &BR&WEXITSTATUS:判断子进程的退出值(当子进程退出时非0). &BR&WIFSIGNALED:子进程由于有没有获得的信号而退出. &BR&WTERMSIG:子进程没有获得的信号号(在WIFSIGNALED为真时才有意义). &BR&waitpid等待指定的子进程直到子进程返回.如果pid为正值则等待指定的进程(pid).如果 &BR&为0则等待任何一个组ID和调用者的组ID相同的进程.为-1时等同于wait调用.小于-1时等 &BR&待任何一个组ID等于pid绝对值的进程. stat_loc和wait的意义一样. options可以决定 &BR&父进程的状态.可以取两个值 WNOHANG:父进程立即返回当没有子进程存在时. WUNTACHE &BR&D:当子进程结束时waitpid返回,但是子进程的退出状态不可得到. &BR&父进程创建子进程后,子进程一般要执行不同的程序.为了调用系统程序,我们可以使用系 &BR&统调用exec族调用.exec族调用有着5个函数. &BR&#include &unistd.h& &BR&int execl(const char *path,const char *arg,...); &BR&int execlp(const char *file,const char *arg,...); &BR&int execle(const char *path,const char *arg,...); &BR&int execv(const char *path,char *const argv[]); &BR&int execvp(const char *file,char *const argv[]): &BR&exec族调用可以执行给定程序.关于exec族调用的详细解说可以参考系统手册(man exec &BR&l). 下面我们来学习一个实例.注意编译的时候要加 -lm以便连接数学函数库. &BR&#include &unistd.h& &BR&#include &sys/types.h& &BR&#include &sys/wait.h& &BR&#include &stdio.h& &BR&#include &errno.h& &BR&#include &math.h& &BR&void main(void) &BR&{ &BR&pid_ &BR& &BR&printf(&This will demostrate how to get child status\n&); &BR&if((child=fork())==-1) &BR&{ &BR&printf(&Fork Error :%s\n&,strerror(errno)); &BR&exit(1); &BR&} &BR&else if(child==0) &BR&{ &BR& &BR&printf(&I am the child:%ld\n&,getpid()); &BR&for(i=0;i&1000000;i++) sin(i); &BR&i=5; &BR&printf(&I exit with %d\n&,i); &BR&exit(i); &BR&} &BR&while(((child=wait(&status))==-1)&(errno==EINTR)); &BR&if(child==-1) &BR&printf(&Wait Error:%s\n&,strerror(errno)); &BR&else if(!status) &BR&printf(&Child %ld terminated normally return status is zero\n&, &BR&child); &BR&else if(WIFEXITED(status)) &BR&printf(&Child %ld terminated normally return status is %d\n&, &BR&child,WEXITSTATUS(status)); &BR&else if(WIFSIGNALED(status)) &BR&printf(&Child %ld terminated due to signal %d znot caught\n&, &BR&child,WTERMSIG(status)); &BR&} &BR&strerror函数会返回一个指定的错误号的错误信息的字符串. &BR&4。守护进程的创建 &BR&如果你在DOS时代编写过程序,那么你也许知道在DOS下为了编写一个常驻内存的程序 &BR&我们要编写多少代码了.相反如果在Linux下编写一个&常驻内存&的程序却是很容易的.我 &BR&们只要几行代码就可以做到. 实际上由于Linux是多任务操作系统,我们就是不编写代码 &BR&也可以把一个程序放到后台去执行的.我们只要在命令后面加上&符号SHELL就会把我们的 &BR&程序放到后台去运行的. 这里我们&开发&一个后台检查邮件的程序.这个程序每个一个指 &BR&定的时间回去检查我们的邮箱,如果发现我们有邮件了,会不断的报警(通过机箱上的小喇 &BR&叭来发出声音). 后面有这个函数的加强版本加强版本 &BR&后台进程的创建思想: 首先父进程创建一个子进程.然后子进程杀死父进程(是不是很无 &BR&情?). 信号处理所有的工作由子进程来处理. &BR&#include &unistd.h& &BR&#include &sys/types.h& &BR&#include &sys/stat.h& &BR&#include &stdio.h& &BR&#include &errno.h& &BR&#include &fcntl.h& &BR&#include &signal.h& &BR&/* Linux 的默任个人的邮箱地址是 /var/spool/mail/用户的登录名 */ &BR&#define MAIL &/var/spool/mail/hoyt& &BR&/* 睡眠10秒钟 */ &BR&&BR&#define SLEEP_TIME 10 &BR&main(void) &BR&{ &BR&pid_ &BR&if((child=fork())==-1) &BR&{ &BR&printf(&Fork Error:%s\n&,strerror(errno)); &BR&exit(1); &BR&} &BR&else if(child&0) &BR&while(1); &BR&if(kill(getppid(),SIGTERM)==-1) &BR&{ &BR&printf(&Kill Parent Error:%s\n&,strerror(errno)); &BR&exit(1); &BR&} &BR&{ &BR& &BR&while(1) &BR&{ &BR&if((mailfd=open(MAIL,O_RDONLY))!=-1) &BR&{ &BR&fprintf(stderr,&%s&,&\007&); &BR&close(mailfd); &BR&} &BR&sleep(SLEEP_TIME); &BR&} &BR&} &BR&} &BR&你可以在默认的路径下创建你的邮箱文件,然后测试一下这个程序.当然这个程序还有很 &BR&多地方要改善的.我们后面会对这个小程序改善的,再看我的改善之前你可以尝试自己改 &BR&善一下.比如让用户指定邮相的路径和睡眠时间等等.相信自己可以做到的.动手吧,勇敢 &BR&的探险者. &BR&好了进程一节的内容我们就先学到这里了.进程是一个非常重要的概念,许多的程序都会 &BR&用子进程.创建一个子进程是每一个程序员的基本要求! &BR&
回复:(风花雪月)linux操作系统下c语言编程入门
3)&B &Linux&/B&程序设计入门--文件操作 &BR&&B &Linux&/B&下文件的操作 &BR&前言: &BR&我们在这一节将要讨论&B &linux&/B&下文件操作的各个函数. &BR&文件的创建和读写 &BR&文件的各个属性 &BR&目录文件的操作 &BR&管道文件 &BR&---------------------------------------------------------------------------- &BR&---- &BR&1。文件的创建和读写 &BR&我假设你已经知道了标准级的文件操作的各个函数(fopen,fread,fwrite等等).当然 &BR&如果你不清楚的话也不要着急.我们讨论的系统级的文件操作实际上是为标准级文件操作 &BR&服务的. &BR&当我们需要打开一个文件进行读写操作的时候,我们可以使用系统调用函数open.使用完 &BR&成以后我们调用另外一个close函数进行关闭操作. &BR&#include &fcntl.h& &BR&#include &unistd.h& &BR&#include &sys/types.h& &BR&#include &sys/stat.h& &BR&&BR&int open(const char *pathname,int flags); &BR&int open(const char *pathname,int flags,mode_t mode); &BR&int close(int fd); &BR&open函数有两个形式.其中pathname是我们要打开的文件名(包含路径名称,缺省是认为在 &BR&当前路径下面).flags可以去下面的一个值或者是几个值的组合. &BR&O_RDONLY:以只读的方式打开文件. &BR&O_WRONLY:以只写的方式打开文件. &BR&O_RDWR:以读写的方式打开文件. &BR&O_APPEND:以追加的方式打开文件. &BR&O_CREAT:创建一个文件. &BR&O_EXEC:如果使用了O_CREAT而且文件已经存在,就会发生一个错误. &BR&O_NOBLOCK:以非阻塞的方式打开一个文件. &BR&O_TRUNC:如果文件已经存在,则删除文件的内容. &BR&前面三个标志只能使用任意的一个.如果使用了O_CREATE标志,那么我们要使用open的第 &BR&二种形式.还要指定mode标志,用来表示文件的访问权限.mode可以是以下情况的组合. &BR&----------------------------------------------------------------- &BR&S_IRUSR 用户可以读 S_IWUSR 用户可以写 &BR&S_IXUSR 用户可以执行 S_IRWXU 用户可以读写执行 &BR&----------------------------------------------------------------- &BR&S_IRGRP 组可以读 S_IWGRP 组可以写 &BR&S_IXGRP 组可以执行 S_IRWXG 组可以读写执行 &BR&----------------------------------------------------------------- &BR&S_IROTH 其他人可以读 S_IWOTH 其他人可以写 &BR&S_IXOTH 其他人可以执行 S_IRWXO 其他人可以读写执行 &BR&----------------------------------------------------------------- &BR&S_ISUID 设置用户执行ID S_ISGID 设置组的执行ID &BR&----------------------------------------------------------------- &BR&我们也可以用数字来代表各个位的标志.&B &Linux&/B&总共用5个数字来表示文件的各种权限. &BR&00000.第一位表示设置用户ID.第二位表示设置组ID,第三位表示用户自己的权限位,第四 &BR&位表示组的权限,最后一位表示其他人的权限. &BR&每个数字可以取1(执行权限),2(写权限),4(读权限),0(什么也没有)或者是这几个值的和 &BR&.. &BR&比如我们要创建一个用户读写执行,组没有权限,其他人读执行的文件.设置用户ID位那么 &BR&我们可以使用的模式是--1(设置用户ID)0(组没有设置)7(1+2+4)0(没有权限,使用缺省) &BR&5(1+4)即10705: &BR&open(&temp&,O_CREAT,10705); &BR&如果我们打开文件成功,open会返回一个文件描述符.我们以后对文件的所有操作就可以 &BR&对这个文件描述符进行操作了. &BR&当我们操作完成以后,我们要关闭文件了,只要调用close就可以了,其中fd是我们要关闭 &BR&的文件描述符. &BR&文件打开了以后,我们就要对文件进行读写了.我们可以调用函数read和write进行文件的 &BR&读写. &BR&#include &unistd.h& &BR&ssize_t read(int fd, void *buffer,size_t count); &BR&ssize_t write(int fd, const void *buffer,size_t count); &BR&fd是我们要进行读写操作的文件描述符,buffer是我们要写入文件内容或读出文件内容的 &BR&内存地址.count是我们要读写的字节数. &BR&对于普通的文件read从指定的文件(fd)中读取count字节到buffer缓冲区中(记住我们必 &BR&须提供一个足够大的缓冲区),同时返回count. &BR&如果read读到了文件的结尾或者被一个信号所中断,返回值会小于count.如果是由信号中 &BR&断引起返回,而且没有返回数据,read会返回-1,且设置errno为EINTR.当程序读到了文件 &BR&结尾的时候,read会返回0. &BR&write从buffer中写count字节到文件fd中,成功时返回实际所写的字节数. &BR&下面我们学习一个实例,这个实例用来拷贝文件. &BR&#include &unistd.h& &BR&#include &fcntl.h& &BR&#include &stdio.h& &BR&#include &sys/types.h& &BR&#include &sys/stat.h& &BR&#include &errno.h& &BR&#include &string.h& &BR&#define BUFFER_SIZE 1024 &BR&int main(int argc,char **argv) &BR&{ &BR&int from_fd,to_ &BR&int bytes_read,bytes_ &BR&char buffer[BUFFER_SIZE]; &BR&char * &BR&if(argc!=3) &BR&{ &BR&fprintf(stderr,&Usage:%s fromfile tofile\n\a&,argv[0]); &BR&exit(1); &BR&} &BR&/* 打开源文件 */ &BR&if((from_fd=open(argv[1],O_RDONLY))==-1) &BR&{ &BR&fprintf(stderr,&Open %s Error:%s\n&,argv[1],strerror(errno)); &BR&exit(1); &BR&} &BR&/* 创建目的文件 */ &BR&if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1) &BR&{ &BR&fprintf(stderr,&Open %s Error:%s\n&,argv[2],strerror(errno)); &BR&exit(1); &BR&} &BR&/* 以下代码是一个经典的拷贝文件的代码 */ &BR&while(bytes_read=read(from_fd,buffer,BUFFER_SIZE)) &BR&{ &BR&/* 一个致命的错误发生了 */ &BR&if((bytes_read==-1)&&(errno!=EINTR)) &BR&else if(bytes_read&0) &BR&{ &BR&ptr= &BR&while(bytes_write=write(to_fd,ptr,bytes_read)) &BR&{ &BR&/* 一个致命错误发生了 */ &BR&if((bytes_write==-1)&&(errno!=EINTR)) &BR&/* 写完了所有读的字节 */ &BR&else if(bytes_write==bytes_read) &BR&/* 只写了一部分,继续写 */ &BR&else if(bytes_write&0) &BR&{ &BR&ptr+=bytes_ &BR&bytes_read-=bytes_ &BR&} &BR&} &BR&/* 写的时候发生的致命错误 */ &BR&if(bytes_write==-1) &BR&} &BR&} &BR&close(from_fd); &BR&close(to_fd); &BR&exit(0); &BR&} &BR&2。文件的各个属性 &BR&文件具有各种各样的属性,除了我们上面所知道的文件权限以外,文件还有创建时间 &BR&,大小等等属性. &BR&有时侯我们要判断文件是否可以进行某种操作(读,写等等).这个时候我们可以使用acce &BR&ss函数. &BR&#include &unistd.h& &BR&&BR&int access(const char *pathname,int mode); &BR&pathname:是文件名称,mode是我们要判断的属性.可以取以下值或者是他们的组合. &BR&R_OK文件可以读,W_OK文件可以写,X_OK文件可以执行,F_OK文件存在.当我们测试成功时 &BR&,函数返回0,否则如果有一个条件不符时,返回-1. &BR&如果我们要获得文件的其他属性,我们可以使用函数stat或者fstat. &BR&#include &sys/stat.h& &BR&#include &unistd.h& &BR&int stat(const char *file_name,struct stat *buf); &BR&int fstat(int filedes,struct stat *buf); &BR&struct stat { &BR&dev_t st_ /* 设备 */ &BR&ino_t st_ /* 节点 */ &BR&mode_t st_ /* 模式 */ &BR&nlink_t st_ /* 硬连接 */ &BR&uid_t st_ /* 用户ID */ &BR&gid_t st_ /* 组ID */ &BR&dev_t st_ /* 设备类型 */ &BR&off_t st_ /* 文件字节数 */ &BR&unsigned long st_ /* 块大小 */ &BR&unsigned long st_ /* 块数 */ &BR&time_t st_ /* 最后一次访问时间 */ &BR&time_t st_ /* 最后一次修改时间 */ &BR&time_t st_ /* 最后一次改变时间(指属性) */ &BR&}; &BR&stat用来判断没有打开的文件,而fstat用来判断打开的文件.我们使用最多的属性是st_ &BR&mode.通过着属性我们可以判断给定的文件是一个普通文件还是一个目录,连接等等.可以 &BR&使用下面几个宏来判断. &BR&S_ISLNK(st_mode):是否是一个连接.S_ISREG是否是一个常规文件.S_ISDIR是否是一个目 &BR&录S_ISCHR是否是一个字符设备.S_ISBLK是否是一个块设备S_ISFIFO是否 是一个FIFO文 &BR&件.S_ISSOCK是否是一个SOCKET文件. 我们会在下面说明如何使用这几个宏的. &BR&3。目录文件的操作 &BR&在我们编写程序的时候,有时候会要得到我们当前的工作路径。C库函数提供了get &BR&cwd来解决这个问题。 &BR&#include &unistd.h& &BR&&BR&char *getcwd(char *buffer,size_t size); &BR&我们提供一个size大小的buffer,getcwd会把我们当前的路径考到buffer中.如果buffer &BR&太小,函数会返回-1和一个错误号. &BR&&B &Linux&/B&提供了大量的目录操作函数,我们学习几个比较简单和常用的函数. &BR&#include &dirent.h& &BR&#include &unistd.h& &BR&#include &fcntl.h& &BR&#include &sys/types.h& &BR&#include &sys/stat.h& &BR&int mkdir(const char *path,mode_t mode); &BR&DIR *opendir(const char *path); &BR&struct dirent *readdir(DIR *dir); &BR&void rewinddir(DIR *dir); &BR&off_t telldir(DIR *dir); &BR&void seekdir(DIR *dir,off_t off); &BR&int closedir(DIR *dir); &BR&struct dirent { &BR&long d_ &BR&off_t d_ &BR&unsigned short d_ &BR&char d_name[NAME_MAX+1]; /* 文件名称 */ &BR&mkdir很容易就是我们创建一个目录,opendir打开一个目录为以后读做准备.readdir读一 &BR&个打开的目录.rewinddir是用来重读目录的和我们学的rewind函数一样.closedir是关闭 &BR&一个目录.telldir和seekdir类似与ftee和fseek函数. &BR&下面我们开发一个小程序,这个程序有一个参数.如果这个参数是一个文件名,我们输出这 &BR&个文件的大小和最后修改的时间,如果是一个目录我们输出这个目录下所有文件的大小和 &BR&修改时间. &BR&#include &unistd.h& &BR&#include &stdio.h& &BR&#include &errno.h& &BR&#include &sys/types.h& &BR&#include &sys/stat.h& &BR&#include &dirent.h& &BR&#include &time.h& &BR&static int get_file_size_time(const char *filename) &BR&{ &BR& &BR&if(stat(filename,&statbuf)==-1) &BR&{ &BR&printf(&Get stat on %s Error:%s\n&, &BR&filename,strerror(errno)); &BR&return(-1); &BR&} &BR&if(S_ISDIR(statbuf.st_mode))return(1); &BR&if(S_ISREG(statbuf.st_mode)) &BR&printf(&%s size:%ld bytes\tmodified at %s&, &BR&filename,statbuf.st_size,ctime(&statbuf.st_mtime)); &BR&&BR&return(0); &BR&} &BR&int main(int argc,char **argv) &BR&{ &BR&DIR * &BR&struct dirent * &BR& &BR&if(argc!=2) &BR&{ &BR&printf(&Usage:%s filename\n\a&,argv[0]); &BR&exit(1); &BR&} &BR&if(((stats=get_file_size_time(argv[1]))==0)||(stats==-1))exit(1); &BR&if((dirp=opendir(argv[1]))==NULL) &BR&{ &BR&printf(&Open Directory %s Error:%s\n&, &BR&argv[1],strerror(errno)); &BR&exit(1); &BR&} &BR&while((direntp=readdir(dirp))!=NULL) &BR&if(get_file_size_time(direntp-&d_name)==-1) &BR&closedir(dirp); &BR&exit(1); &BR&} &BR&4。管道文件 &BR&&B &Linux&/B&提供了许多的过滤和重定向程序,比如more cat &BR&等等.还提供了& & | &&等等重定向操作符.在这些过滤和重 定向程序当中,都用到了管 &BR&道这种特殊的文件.系统调用pipe可以创建一个管道. &BR&#include&unistd.h& &BR&&BR&int pipe(int fildes[2]); &BR&pipe调用可以创建一个管道(通信缓冲区).当调用成功时,我们可以访问文件描述符fild &BR&es[0],fildes[1].其中fildes[0]是用来读的文件描述符,而fildes[1]是用来写的文件描 &BR&述符. &BR&在实际使用中我们是通过创建一个子进程,然后一个进程写,一个进程读来使用的. &BR&关于进程通信的详细情况请查看进程通信 &BR&#include &stdio.h& &BR&#include &stdlib.h& &BR&#include &unistd.h& &BR&#include &string.h& &BR&#include &errno.h& &BR&#include &sys/types.h& &BR&#include &sys/wait.h& &BR&#define BUFFER 255 &BR&int main(int argc,char **argv) &BR&{ &BR&char buffer[BUFFER+1]; &BR&int fd[2]; &BR&if(argc!=2) &BR&{ &BR&fprintf(stderr,&Usage:%s string\n\a&,argv[0]); &BR&exit(1); &BR&} &BR&if(pipe(fd)!=0) &BR&{ &BR&fprintf(stderr,&Pipe Error:%s\n\a&,strerror(errno)); &BR&exit(1); &BR&} &BR&if(fork()==0) &BR&{ &BR&close(fd[0]); &BR&printf(&Child[%d] Write to pipe\n\a&,getpid()); &BR&snprintf(buffer,BUFFER,&%s&,argv[1]); &BR&write(fd[1],buffer,strlen(buffer)); &BR&printf(&Child[%d] Quit\n\a&,getpid()); &BR&exit(0); &BR&} &BR&else &BR&{ &BR&close(fd[1]); &BR&printf(&Parent[%d] Read from pipe\n\a&,getpid()); &BR&memset(buffer,'\0',BUFFER+1); &BR&read(fd[0],buffer,BUFFER); &BR&printf(&Parent[%d] Read:%s\n&,getpid(),buffer); &BR&exit(1); &BR&} &BR&} &BR&为了实现重定向操作,我们需要调用另外一个函数dup2. &BR&#include &unistd.h& &BR&&BR&int dup2(int oldfd,int newfd); &BR&dup2将用oldfd文件描述符来代替newfd文件描述符,同时关闭newfd文件描述符.也就是说 &BR&, &BR&所有向newfd操作都转到oldfd上面.下面我们学习一个例子,这个例子将标准输出重定向 &BR&到一个文件. &BR&#include &unistd.h& &BR&#include &stdio.h& &BR&#include &errno.h& &BR&#include &fcntl.h& &BR&#include &string.h& &BR&#include &sys/types.h& &BR&#include &sys/stat.h& &BR&#define BUFFER_SIZE 1024 &BR&int main(int argc,char **argv) &BR&{ &BR& &BR&char buffer[BUFFER_SIZE]; &BR&if(argc!=2) &BR&{ &BR&fprintf(stderr,&Usage:%s outfilename\n\a&,argv[0]); &BR&exit(1); &BR&} &BR&if((fd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))==-1) &BR&{ &BR&fprintf(stderr,&Open %s Error:%s\n\a&,argv[1],strerror(errno)); &BR&exit(1); &BR&} &BR&if(dup2(fd,STDOUT_FILENO)==-1) &BR&{ &BR&fprintf(stderr,&Redirect Standard Out Error:%s\n\a&,strerror(errno)); &BR&exit(1); &BR&} &BR&fprintf(stderr,&Now,please input string&); &BR&fprintf(stderr,&(To quit use CTRL+D)\n&); &BR&while(1) &BR&{ &BR&fgets(buffer,BUFFER_SIZE,stdin); &BR&if(feof(stdin)) &BR&write(STDOUT_FILENO,buffer,strlen(buffer)); &BR&} &BR&exit(0); &BR&} &BR&好了,文件一章我们就暂时先讨论到这里,学习好了文件的操作我们其实已经可以写出一 &BR&些比较有用的程序了.我们可以编写一个实现例如dir,mkdir,cp,mv等等常用的文件操作 &BR&命令了. &BR&想不想自己写几个试一试呢? &BR&
回复:(风花雪月)linux操作系统下c语言编程入门
4)程序设计入门--时间概念 &BR&前言:&B &Linux&/B&下的时间概念 &BR&这一章我们学习&B &Linux&/B&的时间表示和计算函数 &BR&时间的表示 &BR&时间的测量 &BR&计时器的使用 &BR&1。时间表示 在程序当中,我们经常要输出系统当前的时间,比如我们使用date命令 &BR&的输出结果.这个时候我们可以使用下面两个函数 &BR&#include &time.h& &BR&&BR&time_t time(time_t *tloc); &BR&char *ctime(const time_t *clock); &BR&time函数返回从日0点以来的秒数.存储在time_t结构之中.不过这个函数的返 &BR&回值对于我们来说没有什么实际意义.这个时候我们使用第二个函数将秒数转化为字符串 &BR&.. 这个函数的返回类型是固定的:一个可能值为. Thu Dec 7 14:58:59 2000 这个字符串 &BR&的长度是固定的为26 &BR&2。时间的测量 有时候我们要计算程序执行的时间.比如我们要对算法进行时间分析 &BR&..这个时候可以使用下面这个函数. &BR&#include &sys/time.h& &BR&&BR&int gettimeofday(struct timeval *tv,struct timezone *tz); &BR&strut timeval { &BR&long tv_ /* 秒数 */ &BR&long tv_ /* 微秒数 */ &BR&}; &BR&gettimeofday将时间保存在结构tv之中.tz一般我们使用NULL来代替. &BR&#include &sys/time.h& &BR&#include &stdio.h& &BR&#include &math.h& &BR&void function() &BR&{ &BR&unsigned int i,j; &BR& &BR&for(i=0;i&1000;i++) &BR&for(j=0;j&1000;j++) &BR&y=sin((double)i); &BR&} &BR&main() &BR&{ &BR&struct timeval tpstart, &BR& &BR&gettimeofday(&tpstart,NULL); &BR&function(); &BR&gettimeofday(&tpend,NULL); &BR&timeuse=1000000*(tpend.tv_sec-tpstart.tv_sec)+ &BR&tpend.tv_usec-tpstart.tv_ &BR&timeuse/=1000000; &BR&printf(&Used Time:%f\n&,timeuse); &BR&exit(0); &BR&} &BR&这个程序输出函数的执行时间,我们可以使用这个来进行系统性能的测试,或者是函数算 &BR&法的效率分析.在我机器上的一个输出结果是: Used Time:0.556070 &BR&3。计时器的使用 &B &Linux&/B&操作系统为每一个进程提供了3个内部间隔计时器. &BR&ITIMER_REAL:减少实际时间.到时的时候发出SIGALRM信号. &BR&ITIMER_VIRTUAL:减少有效时间(进程执行的时间).产生SIGVTALRM信号. &BR&ITIMER_PROF:减少进程的有效时间和系统时间(为进程调度用的时间).这个经常和上面一 &BR&个使用用来计算系统内核时间和用户时间.产生SIGPROF信号. &BR&具体的操作函数是: &BR&#include &sys/time.h& &BR&int getitimer(int which,struct itimerval *value); &BR&int setitimer(int which,struct itimerval *newval, &BR&struct itimerval *oldval); &BR&struct itimerval { &BR&struct timeval it_ &BR&struct timeval it_ &BR&} &BR&getitimer函数得到间隔计时器的时间值.保存在value中 setitimer函数设置间隔计时器 &BR&的时间值为newval.并将旧值保存在oldval中. which表示使用三个计时器中的哪一个. &BR&itimerval结构中的it_value是减少的时间,当这个值为0的时候就发出相应的信号了. 然 &BR&后设置为it_interval值. &BR&#include &sys/time.h& &BR&#include &stdio.h& &BR&#include &unistd.h& &BR&#include &signal.h& &BR&#include &string.h& &BR&#define PROMPT &时间已经过去了两秒钟\n\a& &BR&char *prompt=PROMPT; &BR& &BR&void prompt_info(int signo) &BR&{ &BR&write(STDERR_FILENO,prompt,len); &BR&} &BR&void init_sigaction(void) &BR&{ &BR& &BR&act.sa_handler=prompt_ &BR&act.sa_flags=0; &BR&sigemptyset(&act.sa_mask); &BR&sigaction(SIGPROF,&act,NULL); &BR&} &BR&void init_time() &BR&{ &BR&st &BR&value.it_value.tv_sec=2; &BR&value.it_value.tv_usec=0; &BR&value.it_interval=value.it_ &BR&setitimer(ITIMER_PROF,&value,NULL); &BR&} &BR&int main() &BR&{ &BR&len=strlen(prompt); &BR&init_sigaction(); &BR&init_time(); &BR&while(1); &BR&exit(0); &BR&} &BR&这个程序每执行两秒中之后会输出一个提示. &BR&
回复:(风花雪月)linux操作系统下c语言编程入门
5)&B &Linux&/B&程序设计入门--信号处理 &BR&&B &Linux&/B&下的信号事件 &BR&前言:这一章我们讨论一下&B &Linux&/B&下的信号处理函数. &BR&&B &Linux&/B&下的信号处理函数: &BR&信号的产生 &BR&信号的处理 &BR&其它信号函数 &BR&一个实例 &BR&1。信号的产生 &BR&&B &Linux&/B&下的信号可以类比于DOS下的INT或者是Windows下的事件.在有一个信号发生时 &BR&候相信的信号就会发送给相应的进程.在&B &Linux&/B&下的信号有以下几个. 我们使用 kill -l &BR&命令可以得到以下的输出结果: &BR&1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL &BR&5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE &BR&9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 &BR&13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD &BR&18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN &BR&22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ &BR&26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO &BR&30) SIGPWR &BR&关于这些信号的详细解释请查看man 7 signal的输出结果. 信号事件的发生有两个来源 &BR&:一个是硬件的原因(比如我们按下了键盘),一个是软件的原因(比如我们使用系统函数或 &BR&者是命令发出信号). 最常用的四个发出信号的系统函数是kill, raise, alarm和setit &BR&imer函数. setitimer函数我们在计时器的使用 那一章再学习. &BR&#include &sys/types.h& &BR&#include &signal.h& &BR&#include &unistd.h& &BR&int kill(pid_t pid,int sig); &BR&int raise(int sig); &BR&unisigned int alarm(unsigned int seconds); &BR&kill系统调用负责向进程发送信号sig. &BR&如果pid是正数,那么向信号sig被发送到进程pid. &BR&如果pid等于0,那么信号sig被发送到所以和pid进程在同一个进程组的进程 &BR&如果pid等于-1,那么信号发给所有的进程表中的进程,除了最大的哪个进程号. &BR&如果pid由于-1,和0一样,只是发送进程组是-pid. &BR&我们用最多的是第一个情况.还记得我们在守护进程那一节的例子吗?我们那个时候用这 &BR&个函数杀死了父进程守护进程的创建 &BR&raise系统调用向自己发送一个sig信号.我们可以用上面那个函数来实现这个功能的. &BR&alarm函数和时间有点关系了,这个函数可以在seconds秒后向自己发送一个SIGALRM信号 &BR&.. 下面这个函数会有什么结果呢? &BR&#include &unistd.h& &BR&main() &BR&{ &BR& &BR&alarm(1); &BR&for(i=0;1;i++) &BR&printf(&I=%d&,i); &BR&} &BR&SIGALRM的缺省操作是结束进程,所以程序在1秒之后结束,你可以看看你的最后I值为多少 &BR&,来比较一下大家的系统性能差异(我的是2232). &BR&2。信号操作 有时候我们希望进程正确的执行,而不想进程受到信号的影响,比如我 &BR&们希望上面那个程序在1秒钟之后不结束.这个时候我们就要进行信号的操作了. &BR&信号操作最常用的方法是信号屏蔽.信号屏蔽要用到下面的几个函数. &BR&#include &signal.h& &BR&int sigemptyset(sigset_t *set); &BR&int sigfillset(sigset_t *set); &BR&int sigaddset(sigset_t *set,int signo); &BR&int sigdelset(sigset_t *set,int signo); &BR&int sigismember(sigset_t *set,int signo); &BR&int sigprocmask(int how,const sigset_t *set,sigset_t *oset); &BR&sigemptyset函数初始化信号集合set,将set设置为空.sigfillset也初始化信号集合,只 &BR&是将信号集合设置为所有信号的集合.sigaddset将信号signo加入到信号集合之中,sigd &BR&elset将信号从信号集合中删除.sigismember查询信号是否在信号集合之中. &BR&sigprocmask是最为关键的一个函数.在使用之前要先设置好信号集合set.这个函数的作 &BR&用是将指定的信号集合set加入到进程的信号阻塞集合之中去,如果提供了oset那么当前 &BR&的进程信号阻塞集合将会保存在oset里面.参数how决定函数的操作方式. &BR&SIG_BLOCK:增加一个信号集合到当前进程的阻塞集合之中. &BR&SIG_UNBLOCK:从当前的阻塞集合之中删除一个信号集合. &BR&SIG_SETMASK:将当前的信号集合设置为信号阻塞集合. &BR&以一个实例来解释使用这几个函数. &BR&#include &signal.h& &BR&#include &stdio.h& &BR&#include &math.h& &BR&#include &stdlib.h& &BR&int main(int argc,char **argv) &BR&{ &BR& &BR&sigset_ &BR&int i,repeat_ &BR&if(argc!=2) &BR&{ &BR&fprintf(stderr,&Usage:%s repeat_factor\n\a&,argv[0]); &BR&exit(1); &BR&} &BR&if((repeat_factor=atoi(argv[1]))&1)repeat_factor=10; &BR&sigemptyset(&intmask); /* 将信号集合设置为空 */ &BR&sigaddset(&intmask,SIGINT); /* 加入中断 Ctrl+C 信号*/ &BR&while(1) &BR&{ &BR&/*阻塞信号,我们不希望保存原来的集合所以参数为NULL*/ &BR&sigprocmask(SIG_BLOCK,&intmask,NULL); &BR&fprintf(stderr,&SIGINT signal blocked\n&); &BR&for(i=0;i&repeat_i++)y=sin((double)i); &BR&fprintf(stderr,&Blocked calculation is finished\n&); &BR&/* 取消阻塞 */ &BR&sigprocmask(SIG_UNBLOCK,&intmask,NULL); &BR&fprintf(stderr,&SIGINT signal unblocked\n&); &BR&for(i=0;i&repeat_i++)y=sin((double)i); &BR&fprintf(stderr,&Unblocked calculation is finished\n&); &BR&} &BR&exit(0); &BR&} &BR&程序在运行的时候我们要使用Ctrl+C来结束.如果我们在第一计算的时候发出SIGINT信号 &BR&,由于信号已经屏蔽了,所以程序没有反映.只有到信号被取消阻塞的时候程序才会结束. &BR&注意我们只要发出一次SIGINT信号就可以了,因为信号屏蔽只是将信号加入到信号阻塞 &BR&集合之中,并没有丢弃这个信号.一旦信号屏蔽取消了,这个信号就会发生作用. &BR&有时候我们希望对信号作出及时的反映的,比如当拥护按下Ctrl+C时,我们不想什么事情 &BR&也不做,我们想告诉用户你的这个操作不好,请不要重试,而不是什么反映也没有的. 这个 &BR&时候我们要用到sigaction函数. &BR&#include &signal.h& &BR&&BR&int sigaction(int signo,const struct sigaction *act, &BR&struct sigaction *oact); &BR&struct sigaction { &BR&void (*sa_handler)(int signo); &BR&void (*sa_sigaction)(int siginfo_t *info,void *act); &BR&sigset_t sa_ &BR&int sa_ &BR&void (*sa_restore)(void); &BR&} &BR&这个函数和结构看起来是不是有点恐怖呢.不要被这个吓着了,其实这个函数的使用相当 &BR&简单的.我们先解释一下各个参数的含义. signo很简单就是我们要处理的信号了,可以是 &BR&任何的合法的信号.有两个信号不能够使用(SIGKILL和SIGSTOP). act包含我们要对这个 &BR&信号进行如何处理的信息.oact更简单了就是以前对这个函数的处理信息了,主要用来保 &BR&存信息的,一般用NULL就OK了. &BR&信号结构有点复杂.不要紧我们慢慢的学习. &BR&sa_handler是一个函数型指针,这个指针指向一个函数,这个函数有一个参数.这个函数就 &BR&是我们要进行的信号操作的函数. sa_sigaction,sa_restore和sa_handler差不多的,只 &BR&是参数不同罢了.这两个元素我们很少使用,就不管了. &BR&sa_flags用来设置信号操作的各个情况.一般设置为0好了.sa_mask我们已经学习过了 &BR&在使用的时候我们用sa_handler指向我们的一个信号操作函数,就可以了.sa_handler有 &BR&两个特殊的值:SIG_DEL和SIG_IGN.SIG_DEL是使用缺省的信号操作函数,而SIG_IGN是使用 &BR&忽略该信号的操作函数. &BR&这个函数复杂,我们使用一个实例来说明.下面这个函数可以捕捉用户的CTRL+C信号.并输 &BR&出一个提示语句. &BR&#include &signal.h& &BR&#include &stdio.h& &BR&#include &string.h& &BR&#include &errno.h& &BR&#include &unistd.h& &BR&#define PROMPT &你想终止程序吗?& &BR&char *prompt=PROMPT; &BR&void ctrl_c_op(int signo) &BR&{ &BR&write(STDERR_FILENO,prompt,strlen(prompt)); &BR&} &BR&int main() &BR&{ &BR& &BR&act.sa_handler=ctrl_c_ &BR&sigemptyset(&act.sa_mask); &BR&act.sa_flags=0; &BR&if(sigaction(SIGINT,&act,NULL)&0) &BR&{ &BR&fprintf(stderr,&Install Signal Action Error:%s\n\a&,strerror(errno)); &BR&exit(1); &BR&} &BR&while(1); &BR&} &BR&在上面程序的信号操作函数之中,我们使用了write函数而没有使用fprintf函数.是因为 &BR&我们要考虑到下面这种情况.如果我们在信号操作的时候又有一个信号发生,那么程序该 &BR&如何运行呢? 为了处理在信号处理函数运行的时候信号的发生,我们需要设置sa_mask成 &BR&员. 我们将我们要屏蔽的信号添加到sa_mask结构当中去,这样这些函数在信号处理的时 &BR&候就会被屏蔽掉的. &BR&3。其它信号函数 由于信号的操作和处理比较复杂,我们再介绍几个信号操作函数. &BR&&BR&#include &unistd.h& &BR&#include &signal.h& &BR&int pause(void); &BR&int sigsuspend(const sigset_t *sigmask); &BR&pause函数很简单,就是挂起进程直到一个信号发生了.而sigsuspend也是挂起进程只是在 &BR&调用的时候用sigmask取代当前的信号阻塞集合. &BR&#include &sigsetjmp& &BR&int sigsetjmp(sigjmp_buf env,int val); &BR&void siglongjmp(sigjmp_buf env,int val); &BR&还记得goto函数或者是setjmp和longjmp函数吗.这两个信号跳转函数也可以实现程序的 &BR&跳转让我们可以从函数之中跳转到我们需要的地方. &BR&由于上面几个函数,我们很少遇到,所以只是说明了一下,详细情况请查看联机帮助. &BR&4。一个实例 还记得我们在守护进程创建的哪个程序吗?守护进程在这里我们把那个 &BR&程序加强一下. 下面这个程序会在也可以检查用户的邮件.不过提供了一个开关,如果用 &BR&户不想程序提示有新的邮件到来,可以向程序发送SIGUSR2信号,如果想程序提供提示可以 &BR&发送SIGUSR1信号. &BR&#include &unistd.h& &BR&#include &stdio.h& &BR&#include &errno.h& &BR&#include &fcntl.h& &BR&#include &signal.h& &BR&#include &string.h& &BR&#include &pwd.h& &BR&#include &sys/types.h& &BR&#include &sys/stat.h& &BR&/* &B &Linux&/B& 的默任个人的邮箱地址是 /var/spool/mail/ */ &BR&#define MAIL_DIR &/var/spool/mail/& &BR&/* 睡眠10秒钟 */ &BR&#define SLEEP_TIME 10 &BR&#define MAX_FILENAME 255 &BR&unsigned char notifyflag=1; &BR&long get_file_size(const char *filename) &BR&{ &BR& &BR&if(stat(filename,&;buf)==-1) &BR&{ &BR&if(errno==ENOENT)return 0; &BR&else return -1; &BR&} &BR&return (long)buf.st_ &BR&} &BR&void send_mail_notify(void) &BR&{ &BR&fprintf(stderr,&New mail has arrived\007\n&); &BR&} &BR&void turn_on_notify(int signo) &BR&{ &BR&notifyflag=1; &BR&} &BR&void turn_off_notify(int signo) &BR&{ &BR&notifyflag=0; &BR&} &BR&int check_mail(const char *filename) &BR&{ &BR&long old_mail_size,new_mail_ &BR&sigset_t blockset, &BR&sigemptyset(&;blockset); &BR&sigemptyset(&;emptyset); &BR&sigaddset(&;blockset,SIGUSR1); &BR&sigaddset(&;blockset,SIGUSR2); &BR&old_mail_size=get_file_size(filename); &BR&if(old_mail_size&0)return 1; &BR&if(old_mail_size&0) send_mail_notify(); &BR&sleep(SLEEP_TIME); &BR&while(1) &BR&{ &BR&if(sigprocmask(SIG_BLOCK,&;blockset,NULL)&0) return 1; &BR&while(notifyflag==0)sigsuspend(&;emptyset); &BR&if(sigprocmask(SIG_SETMASK,&;emptyset,NULL)&0) return 1; &BR&new_mail_size=get_file_size(filename); &BR&if(new_mail_size&old_mail_size)send_mail_ &BR&old_mail_size=new_mail_ &BR&sleep(SLEEP_TIME); &BR&} &BR&} &BR&int main(void) &BR&{ &BR&char mailfile[MAX_FILENAME]; &BR&str &BR&struct passwd * &BR&if((pw=getpwuid(getuid()))==NULL) &BR&{ &BR&fprintf(stderr,&Get Login Name Error:%s\n\a&,strerror(errno)); &BR&exit(1); &BR&} &BR&strcpy(mailfile,MAIL_DIR); &BR&strcat(mailfile,pw-&pw_name); &BR&newact.sa_handler=turn_on_ &BR&newact.sa_flags=0; &BR&sigemptyset(&;newact.sa_mask); &BR&sigaddset(&;newact.sa_mask,SIGUSR1); &BR&sigaddset(&;newact.sa_mask,SIGUSR2); &BR&if(sigaction(SIGUSR1,&;newact,NULL)&0) &BR&fprintf(stderr,&Turn On Error:%s\n\a&,strerror(errno)); &BR&newact.sa_handler=turn_off_ &BR&if(sigaction(SIGUSR1,&;newact,NULL)&0) &BR&fprintf(stderr,&Turn Off Error:%s\n\a&,strerror(errno)); &BR&check_mail(mailfile); &BR&exit(0); &BR&} &BR&信号操作是一件非常复杂的事情,比我们想象之中的复杂程度还要复杂,如果你想彻底的 &BR&弄清楚信号操作的各个问题,那么除了大量的练习以外还要多看联机手册.不过如果我们 &BR&只是一般的使用的话,有了上面的几个函数也就差不多了. 我们就介绍到这里了. &BR&
回复:(风花雪月)linux操作系统下c语言编程入门
6)&B &Linux&/B&程序设计入门--消息管理 &BR&前言:&B &Linux&/B&下的进程通信(IPC) &BR&&B &Linux&/B&下的进程通信(IPC) &BR&POSIX无名信号量 &BR&System V信号量 &BR&System V消息队列 &BR&System V共享内存 &BR&1。POSIX无名信号量 如果你学习过操作系统,那么肯定熟悉PV操作了.PV操作是原子 &BR&操作.也就是操作是不可以中断的,在一定的时间内,只能够有一个进程的代码在CPU上面 &BR&执行.在系统当中,有时候为了顺利的使用和保护共享资源,大家提出了信号的概念. 假设 &BR&我们要使用一台打印机,如果在同一时刻有两个进程在向打印机输出,那么最终的结果会 &BR&是什么呢.为了处理这种情况,POSIX标准提出了有名信号量和无名信号量的概念,由于Li &BR&nux只实现了无名信号量,我们在这里就只是介绍无名信号量了. 信号量的使用主要是用 &BR&来保护共享资源,使的资源在一个时刻只有一个进程所拥有.为此我们可以使用一个信号 &BR&灯.当信号灯的值为某个值的时候,就表明此时资源不可以使用.否则就表&示可以使用. &BR&为了提供效率,系统提供了下面几个函数 &BR&POSIX的无名信号量的函数有以下几个: &BR&#include &semaphore.h& &BR&int sem_init(sem_t *sem,int pshared,unsigned int value); &BR&int sem_destroy(sem_t *sem); &BR&int sem_wait(sem_t *sem); &BR&int sem_trywait(sem_t *sem); &BR&int sem_post(sem_t *sem); &BR&int sem_getvalue(sem_t *sem); &BR&sem_init创建一个信号灯,并初始化其值为value.pshared决定了信号量能否在几个进程 &BR&间共享.由于目前&B &Linux&/B&还没有实现进程间共享信号灯,所以这个值只能够取0. sem_dest &BR&roy是用来删除信号灯的.sem_wait调用将阻塞进程,直到信号灯的值大于0.这个函数返回 &BR&的时候自动的将信号灯的值的件一.sem_post和sem_wait相反,是将信号灯的内容加一同 &BR&时发出信号唤醒等待的进程..sem_trywait和sem_wait相同,不过不阻塞的,当信号灯的值 &BR&为0的时候返回EAGAIN,表示以后重试.sem_getvalue得到信号灯的值. &BR&由于&B &Linux&/B&不支持,我们没有办法用源程序解释了. &BR&这几个函数的使用相当简单的.比如我们有一个程序要向一个系统打印机打印两页.我们 &BR&首先创建一个信号灯,并使其初始值为1,表示我们有一个资源可用.然后一个进程调用se &BR&m_wait由于这个时候信号灯的值为1,所以这个函数返回,打印机开始打印了,同时信号灯 &BR&的值为0 了. 如果第二个进程要打印,调用sem_wait时候,由于信号灯的值为0,资源不可 &BR&用,于是被阻塞了.当第一个进程打印完成以后,调用sem_post信号灯的值为1了,这个时候 &BR&系统通知第二个进程,于是第二个进程的sem_wait返回.第二个进程开始打印了. &BR&不过我们可以使用线程来解决这个问题的.我们会在后面解释什么是线程的.编译包含上 &BR&面这几个函数的程序要加上 -lrt选贤,以连接librt.so库 &BR&2。System V信号量 为了解决上面哪个问题,我们也可以使用System V信号量.很幸运的 &BR&是&B &Linux&/B&实现了System V信号量.这样我们就可以用实例来解释了. System V信号量的函 &BR&数主要有下面几个. &BR&#include &sys/types.h& &BR&#include &sys/ipc.h& &BR&#include &sys/sem.h& &BR&key_t ftok(char *pathname,char proj); &BR&int semget(key_t key,int nsems,int semflg); &BR&int semctl(int semid,int semnum,int cmd,union semun arg); &BR&int semop(int semid,struct sembuf *spos,int nspos); &BR&struct sembuf { &BR&short sem_ /* 使用那一个信号 */ &BR&short sem_ /* 进行什么操作 */ &BR&short sem_ /* 操作的标志 */ &BR&}; &BR&ftok函数是根据pathname和proj来创建一个关键字.semget创建一个信号量.成功时返回 &BR&信号的ID,key是一个关键字,可以是用ftok创建的也可以是IPC_PRIVATE表明由系统选用 &BR&一个关键字. nsems表明我们创建的信号个数.semflg是创建的权限标志,和我们创建一个 &BR&文件的标志相同. &BR&semctl对信号量进行一系列的控制.semid是要操作的信号标志,semnum是信号的个数,cm &BR&d是操作的命令.经常用的两个值是:SETVAL(设置信号量的值)和IPC_RMID(删除信号灯). &BR&arg是一个给cmd的参数. &BR&semop是对信号进行操作的函数.semid是信号标志,spos是一个操作数组表明要进行什么 &BR&操作,nspos表明数组的个数. 如果sem_op大于0,那么操作将sem_op加入到信号量的值中 &BR&,并唤醒等待信号增加的进程. 如果为0,当信号量的值是0的时候,函数返回,否则阻塞直 &BR&到信号量的值为0. 如果小于0,函数判断信号量的值加上这个负值.如果结果为0唤醒等待 &BR&信号量为0的进程,如果小与0函数阻塞.如果大于0,那么从信号量里面减去这个值并返回 &BR&.. &BR&下面我们一以一个实例来说明这几个函数的使用方法.这个程序用标准错误输出来代替我 &BR&们用的打印机. &BR&#include &stdio.h& &BR&#include &unistd.h& &BR&#include &limits.h& &BR&#include &errno.h& &BR&#include &string.h& &BR&#include &stdlib.h& &BR&#include &sys/stat.h& &BR&#include &sys/wait.h& &BR&#include &sys/ipc.h& &BR&#include &sys/sem.h& &BR&#define PERMS S_IRUSR|S_IWUSR &BR&void init_semaphore_struct(struct sembuf *sem,int semnum, &BR&int semop,int semflg) &BR&{ &BR&/* 初始话信号灯结构 */ &BR&sem-&sem_num= &BR&sem-&sem_op= &BR&sem-&sem_flg= &BR&} &BR&int del_semaphore(int semid) &BR&{ &BR&/* 信号灯并不随程序的结束而被删除,如果我们没删除的话(将1改为0) &BR&可以用ipcs命令查看到信号灯,用ipcrm可以删除信号灯的 &BR&*/ &BR&#if 1 &BR&return semctl(semid,0,IPC_RMID); &BR&#endif &BR&} &BR&int main(int argc,char **argv) &BR&{ &BR&char buffer[MAX_CANON],*c; &BR&int i,n; &BR&int semid,semop_ret, &BR&pid_ &BR&struct sembuf semwait, &BR&if((argc!=2)||((n=atoi(argv[1]))&1)) &BR&{ &BR&fprintf(stderr,&Usage:%s number\n\a&,argv[0]); &BR&exit(1); &BR&} &BR&/* 使用IPC_PRIVATE 表示由系统选择一个关键字来创建 */ &BR&/* 创建以后信号灯的初始值为0 */ &BR&if((semid=semget(IPC_PRIVATE,1,PERMS))==-1) &BR&{ &BR&fprintf(stderr,&[%d]:Acess Semaphore Error:%s\n\a&, &BR&getpid(),strerror(errno)); &BR&exit(1); &BR&} &BR&/* semwait是要求资源的操作(-1) */ &BR&init_semaphore_struct(&semwait,0,-1,0); &BR&/* semsignal是释放资源的操作(+1) */ &BR&init_semaphore_struct(&semsignal,0,1,0); &BR&/* 开始的时候有一个系统资源(一个标准错误输出) */ &BR&if(semop(semid,&semsignal,1)==-1) &BR&{ &BR&fprintf(stderr,&[%d]:Increment Semaphore Error:%s\n\a&, &BR&getpid(),strerror(errno)); &BR&if(del_semaphore(semid)==-1) &BR&fprintf(stderr,&[%d]:Destroy Semaphore Error:%s\n\a&, &BR&getpid(),strerror(errno)); &BR&exit(1); &BR&} &BR&/* 创建一个进程链 */ &BR&for(i=0;i&n;i++) &BR&if(childpid=fork()) &BR&sprintf(buffer,&[i=%d]--&[Process=%d]--&[Parent=%d]--&[Child=%d]\n&, &BR&i,getpid(),getppid(),childpid); &BR&c= &BR&/* 这里要求资源,进入原子操

我要回帖

更多关于 windows 创建进程 的文章

 

随机推荐