这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » 【每日总结】liklon从零学嵌入式Linux

共87条 3/9 1 2 3 4 5 6 ›| 跳转至
高工
2014-05-01 03:14:21     打赏
21楼

八、Linux下文件IO编程

在linux中对目录和设备的操作都等同于文件的操作,所以提高了效率。每一个文件都有自己的文件描述符,对linux而言,所有对设备和文件的操作都是使用文件描述符来进行的,文件描述符是一个非负的整数。

先试了试这几个函数:

open() read() write() lseek() close()

这几个都是文件IO操作的系统调用用到的主要几个函数。

int open(const char *pathname,int flags,int perms)

int close(int fd);

ssize_t read(int fd,void *buf,size_t count)

ssize_t write(int fd,void *buf,size_t count)

off_t lseek(int fd,off_t offset,int whence)

先编写一个程序把这几个都给用到:

将一个文件里的数据拷贝到另外一个文件里:

      
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
 

#define  OFF_SET  10240
#define  READ_SIZE 1024

int main(int argc,char **argv)
{
    int fd_src,fd_des;  // 文件描述符
    int real_read;    
    unsigned char buf[READ_SIZE];

    if(argc != 3)
    {
         printf("please enter filename\n");
         return 1;
    }
    
    if((fd_src = open(argv[1],O_RDONLY)) < 0)
    {
         printf("open file_src error\n");
         return 1;
    }
    
    if((fd_des = open(argv[2],O_WRONLY | O_CREAT,777)) < 0)
    {
         printf("open file_des error\n");
         return 1;
    }

    lseek(fd_src,-OFF_SET,SEEK_END);   //读取最后10K的数据
    
    memset(buf,0,READ_SIZE); 

    while( (real_read = read(fd_src,buf,READ_SIZE)) > 0)
    {
        write(fd_des,buf,real_read);
    }
   
    close(fd_src);
    close(fd_des);
    
    return 0;
    
}

 


在文件共享的情况下,防止多用户产生竞争的情况,Linux采用的方式是给文件上锁,避免共享的文件产生竞争状态。

文件上锁分为建议性锁和强制性锁。所谓建议性锁就是假定人们都会遵守某些规则去干一件事。例如,人与车看到红灯都会停,而看到绿灯才会继续走,我们可以称红绿等为建议锁。但这只是一种规则而已,你并不防止某些人强闯红灯。而强制性锁是你想闯红灯也闯不了。

(2)fcntl函数格式

用于建立记录锁的fcntl函数格式如表6.6 所示。

表6.6 fcntl函数语法要点所需头文件

#include 

#include 

#include 

函数原型int fcnt1(int fd, int cmd, struct flock *lock)

fd:文件描述符

F_DUPFD:复制文件描述符

F_GETFD:获得fd的close-on-exec标志,若标志未设置,则文件经过exec函数之后仍保持打开状态

F_SETFD:设置close-on-exec标志,该标志以参数arg的FD_CLOEXEC位决定

F_GETFL:得到open设置的标志

函数传入值

cmd

F_SETFL:改变open设置的标志

F_GETFK:根据lock描述,决定是否上文件锁

F_SETFK:设置lock描述的文件锁

F_SETLKW:这是F_SETLK的阻塞版本(命令名中的W表示等待(wait))。

如果存在其他锁,则调用进程睡眠;如果捕捉到信号则睡眠中断

F_GETOWN:检索将收到SIGIO和SIGURG信号的进程号或进程组号

F_SETOWN:设置进程号或进程组号

函数返回值

Lock:结构为flock,设置记录锁的具体状态,后面会详细说明

成功:0

-1:出错

这里,lock的结构如下所示:

Struct flock{

short l_type;

off_t l_start;

short l_whence;

off_t l_len;

pid_t l_pid;

}

lock结构中每个变量的取值含义如表6.7 所示。

表6.7 lock结构变量取值

F_RDLCK:读取锁(共享锁)

l_type F_WRLCK:写入锁(排斥锁)

F_UNLCK:解锁

l_stat 相对位移量(字节)

SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小

SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量

l_whence:相对位移量的起点(同lseek 的whence)。

SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小

l_len 加锁区域的长度

小技巧:

为加锁整个文件,通常的方法是将l_start 说明为0,l_whence 说明为SEEK_SET,l_len 说明为0。



高工
2014-05-02 18:31:30     打赏
22楼
int lockFile(int fd,int type)
{
    struct flock lockStruct;
    
    lockStruct.l_type = type;
    lockStruct.l_start = 0;
    lockStruct.l_whence = SEEK_SET;
    lockStruct.l_len = 0;    //选定全文
    lockStruct.l_pid = -1;
    
    //判断是否上锁
    fcntl(fd,F_GETLK,&lockStruct);
    
    if(lockStruct.l_type != F_UNLCK)
    {
         if(lockStruct.l_type == F_RDLCK)
         {
              printf("read lock already set by %d\n",lockStruct.l_pid);
         }
         else if(lockStruct.l_type == F_WRLCK)
         {
              printf("write lock already set by %d\n",lockStruct.l_pid);
         }
    }
    
    lockStruct.l_type = type; 
    //利用阻塞式解锁和上锁
    
    if(fcntl(fd,F_SETLKW,&lockStruct) < 0)
    {
        printf("file lock error!! \n");
        return 1;
    }
  
    switch(lockStruct.l_type)
    {
        case F_RDLCK: 
                    {
                         printf("read lock already by %d\n",getpid());
                    }
                    break;
        case F_WRLCK: 
                    {
                         printf("write lock already by %d\n",getpid());
                    }
                    break;
        case F_UNLCK: 
                    {
                         printf("release by %d\n",getpid());
                    }
                    break;
        default: break;
    } 
    return 0;
    
}

 

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <sys/types.h>

#include <unistd.h>

#include <sys/file.h>

#include <sys/stat.h>

#include "lktest.c"



int main(int argc,char **argv)

{

     int fd_open;     



     if(argc != 2)

     {

         printf("please enter file name\n");

         return 1;

     }

     

     if((fd_open = open(argv[1],O_RDWR | O_CREAT,777)) < 0)

     {

         printf("open file error \n");

         return 1;

     }

     lockFile(fd_open,F_WRLCK);

     getchar();

     lockFile(fd_open,F_RDLCK);

     getchar();

     

     close(fd_open);

     return 0;

}

 


菜鸟
2014-05-02 23:02:42     打赏
23楼
真好,谢谢楼主的分享,我会持续跟进您的学习帖子

高工
2014-05-04 15:43:46     打赏
24楼

九、linux中select()函数分析

   上一次fcntl()函数是解决了一下文件共享的问题。这次搞的是IO复用的情况,处理IO复用的情况总的来说处理模型有5种。

    1、阻塞I/O模型

     在所调用的I/O函数没有完成相关功能,则会使进程挂起,直到相关数据到达才会返回。对管道设备,终端设备和网络进行读写时进程会出现这种情况。

    2、非阻塞I/O模型

    进程把一个套接口设置成非阻塞是在通知内核:当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把本进程投入睡眠,而是返回一个错误。也就是说当数据没有到达时并不等待,而是以一个错误返回。

    3、I/O复用模型

   调用select或poll,在这两个系统调用中的某一个上阻塞,而不是阻塞于真正I/O系统调用。 阻塞于select调用,等待数据报套接口可读。当select返回套接口可读条件时,调用recevfrom将数据报拷贝到应用缓冲区中。

    4、信号驱动I/O模型

    首先开启套接口信号驱动I/O功能,并通过系统调用sigaction安装一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据报准备好被读时,就为该进程生成一个SIGIO信号。随即可以在信号处理程序中调用recvfrom来读数据报,井通知主循环数据已准备好被处理中。也可以通知主循环,让它来读数据报。

    5、异步I/O模型

    告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核拷贝到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别是:

    信号驱动I/O:由内核通知我们何时可以启动一个I/O操作,

     异步I/O模型:由内核通知我们I/O操作何时完成。

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 

先说明两个结构体: 

第一,struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*),检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。 

第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 

具体解释select的参数: 

int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。 

fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。 

fd_set *writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。 

fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。 

struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 

返回值: 

负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
 


#define MAX_BUFFER_SIZE   1024
#define IN_FILES   3
#define TIME_DELAY   60
#define MAX(a,b)   ((a > b)?(a):(b))


int main(void)
{
    int fds[IN_FILES];
    char buf[MAX_BUFFER_SIZE];
    int i,res,real_read,maxfd;
    struct timeval tv;
    fd_set inset,tmp_inset;
    
    fds[0] = 0;
    if((fds[1] = open("in1",O_RDONLY | O_NONBLOCK)) < 0)
    {
        printf("open in1 error!!!\n");
        return 1;
    }
    if((fds[2] = open("in2",O_RDONLY | O_NONBLOCK)) < 0)
    {
        printf("open in2 error!!!\n");
        return 1;
    }
    /*找出最大的描述符*/
    maxfd = MAX(MAX(fds[1],fds[0]),fds[2]);
    
    /*初始化描述符集*/
    FD_ZERO(&inset);
    for(i = 0;i < IN_FILES;i++)
    {
         FD_SET(fds[i],&inset);
    }
    FD_SET(0,&inset);
    
    tv.tv_sec = TIME_DELAY;
    tv.tv_usec = 0;
    /*循环检测该描述符是否就绪,并调用select函数来对相关的描述符作相应的操作*/
    
    while(FD_ISSET(fds[0],&inset) || FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2],&inset))
    {
        tmp_inset = inset;
        res = select(maxfd + 1,&tmp_inset,NULL,NULL,&tv);
        
        switch(res)
        {
             case -1: 
                    {
                        printf("select error!!!\n");
                        return 1;
                    }
                    break;
             case  0:
                    {
                        printf("time out\n");
                        return 1;
                    }
                    break;
             default:
                    {
                         for(i = 0;i < IN_FILES;i++)
                         {
                             if(FD_ISSET(fds[i],&tmp_inset))
                             {
                                  memset(buf,0,MAX_BUFFER_SIZE);
                                  real_read = read(fds[i],buf,MAX_BUFFER_SIZE);
                                  
                                  if(real_read < 0)
                                  {
                                       if(errno != EAGAIN)
                                       {
                                           return 1;   
                                       }
                                  }
                                  else if(!real_read)
                                  {
 					close(fds[i]);
   					FD_CLR(fds[i],&inset);
				  }
                                  else
                                  {
					if(i == 0)   /*主程序终端控制*/
                                        {
 					    if(buf[0] == 'q' || buf[0] == 'Q')
                                            {
						return 1;
					    }
 					}
                                        else
					{
                                            buf[real_read] = '\0';
                                            printf("%s\n",buf);
					}			  
				  }
                             }
                         }/*for 结束*/
                    }
                    break;
        }
    }
    return 0;

}


编译后,开三个终端,第一个建立in1第二个建立in2第三个运行编译后的可执行文件




高工
2014-05-04 16:06:24     打赏
25楼
一起学习。偶也是菜鸟

高工
2014-05-04 18:14:55     打赏
26楼


当使用elect()函数时,存在一系列的问题,例如:内核必须检查多余的文件描述符,每次调用select后必须重置被监听的文件描述符集,而且可监听的文件个数受限制。Poll机制与select机制相比效率更高,使用范围更广。

poll函数起源于SVR3,最初局限于流设备。SVR4取消了这种限制,允许poll工作在任何描述字上。poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。

#include 

int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

               返回:就绪描述字的个数,0-超时,-1-出错

第一个参数是指向一个结构数组第一个元素的指针。每个数组元素都是一个pollfd结构,用于指定测试某个给定描述字fd的条件。

           struct pollfd{

                    int fd;              //descriptor to check

                    short events;    //events of interest on fd

                    short revents;   //events that occurred on fd 

            };

       要测试的条件由events成员指定,而返回的结果则在revents中存储。常用条件及含意说明如下:

poll函数可用的测试值常量说明POLLIN普通或优先级带数据可读POLLRDNORM普通数据可读POLLRDBAND优先级带数据可读POLLPRI高优先级数据可读POLLOUT普通数据可写POLLWRNORM普通数据可写POLLWRBAND优先级带数据可写POLLERR发生错误POLLHUP发生挂起POLLNVAL描述字不是一个打开的文件

 注意:后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。

poll()接受一个指向结构''''''''structpollfd''''''''列表的指针,其中包括了你想测试的文件描述符和事件。事件由一个在结构中事件域的比特掩码确定。当前的结构在调用后将被填写并在事件发生后返回。在SVR4(可能更早的一些版本)中的 "poll.h"文件中包含了用于确定事件的一些宏定义。事件的等待时间精确到毫秒(但令人困惑的是等待时间的类型却是int),当等待时间为0时,poll()函数立即返回,-1则使poll()一直挂起直到一个指定事件发生。下面是pollfd的结构。 struct pollfd {

int fd; /* 文件描述符 */

short events; /* 等待的事件 */

short revents; /* 实际发生了的事件 */

};

于 select()十分相似,当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定事件内没有事件发生。如发现返回为负则应该立即查看 errno,因为这代表有错误发生。

如果没有事件发生,revents会被清空,所以你不必多此一举。





#include <stdio.h>


#include <stdlib.h>


#include <fcntl.h>


#include <unistd.h>


#include <string.h>


#include <time.h>


#include <errno.h>


#include <poll.h>




#define  BUF_SIZE  1024 

#define  IN_FILE   3

#define  TIME_DELAY   60



//int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

/*

struct pollfd{

                    int fd;              //descriptor to check

                    short events;    //events of interest on fd

                    short revents;   //events that occurred on fd 

            };



*/

int main(void)

{

    struct pollfd  fd_file[IN_FILE]; //结构体数组

    int i,res,real_read;

    char  buf[BUF_SIZE];

    

    fd_file[0].fd = 0;

    if((fd_file[1].fd = open("in1",O_RDONLY | O_NONBLOCK)) < 0)  //获取fd

    {

        printf("open in1 error\n");

        return 1;

    } 

    

    if((fd_file[2].fd = open("in2",O_RDONLY | O_NONBLOCK)) < 0)

    {

        printf("open in2 error\n");

        return 1;

    } 



    for(i = 0;i < IN_FILE;i++)

    {

         fd_file[i].events = POLLIN;

    }

    



    

    while(fd_file[0].events || fd_file[1].events || fd_file[2].events)

    {

         if((res = poll(fd_file,IN_FILE,0)) < 0)

         {

              printf("poll error\n");

              return 1;           

         }

         

         for(i = 0;i < IN_FILE;i++)       

         {

              if(fd_file[i].revents)

              {

                  memset(buf,0,BUF_SIZE);

                  real_read = read(fd_file[i].fd,buf,BUF_SIZE); //读取数据

                  if(real_read < 0)

                  { 

                      if(errno != EAGAIN)

                      {

                           return 1;

                      }        

                  }

                  else if(!real_read)

                  {

                      close(fd_file[i].fd);

                      fd_file[i].events = 0;

                  }

                  else

                  {

                      if(0 == i)

                      {

                          if(buf[0] == 'q' || buf[0] == 'Q')

                          {

                               return 1;

                          }

                      }

                      else

                      {

                          buf[real_read] = '\0';

                          printf("%s\n",buf);

                      }

                  }

              }//if结束

         }//for结束

         

    }

    return 0;

    

}


同样开三个终端:



高工
2014-05-05 16:36:06     打赏
27楼

十、Linux进程控制

进程简单的说就是一个程序一次执行的过程,它是一个动态的概念。按照教科书上的定义,进程是程序执行的实例,是linux的基本调度单位。 

对于程序员来说,最重要的就是要区分进程和程序的区别,程序是指一段完成功能的代码,或者说是一个工具,它是一个静态的概念,而进程,它是动态的,比如,linux的vi编辑器,它就是一段在linux下用于文本编辑的工具,那么它是一个程序,而我们在linux终端中,可以分别开启两个vi编辑器的进程。一旦提到进程,我们的脑子里就应该产生——程序从代码的第一句动态的执行到最后一句这样的一个思路。 

一个进程由如下元素组成: 

1.> 进程的当前上下文,即进程的当前执行状态; 

2.> 进程的当前执行目录 

3.> 进程访问的文件和目录 

4.> 进程的访问权限,比如它的文件模式和所有权 

5.> 内存和其他分配给进程的系统资源 

在linux系统中,内核使用进程来控制对CPU和其他系统资源的访问,并且使用进程来决定在CPU上运行哪个程序,运行多久以及采用什么特性运行它。内核的调度器负责在所有的进程间分配CPU执行时间,称为时间片(time slice),它轮流在每个进程分得的时间片用完后从进程那里抢回控制权。 

OS会为每个进程分配一个唯一的整型ID,做为进程的标识号(pid)。进程除了自身的ID外,还有父进程ID(ppid),所有进程的祖先进程是同一个进程,它叫做init进程,ID为1,init进程是内核自检后的一个启动的进程。init进程负责引导系统、启动守护(后台)进程并且运行必要的程序。 

简单来说,Linux的进程就相当于Windows系统中的任务,每个在linux下运行的程序都是一个进程,每个进程包含(属于自己唯一的)进程标识符及数据,这些数据包含进程变量,外部变量及进程堆栈等。

1.fork()

 一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。

       fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

    1)在父进程中,fork返回新创建子进程的进程ID;

    2)在子进程中,fork返回0;

 3)如果出现错误,fork返回一个负值;

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
 
int main(void)
{
    pid_t who_pid;
    
    who_pid = fork(); //建立一个子进程
    
    if(who_pid < 0)
    {
        printf("fork error\n");
        return 1;
    }
    
    if(who_pid == 0)  //为0表示是在子进程中
    {
        printf("child process id is %d\n",getpid());
     
    }
    else 
    {
        printf("parent process id is %d\n",getpid());
     
    }
    return who_pid;       
}



2.exit()和_exit()

1>exit和_exit函数都是用来终止进程的。 当程序执行到exit或_exit时,系统无条件的停止剩下所有操作,清除包括PCB在内的各种数据结构,并终止本进程的运行。 

2>exit在头文件stdlib.h中声明,而_exit()声明在头文件unistd.h中声明。 exit中的参数exit_code为0代表进程正常终止,若为其他值表示程序执行过程中有错误发生。 

3>exit()和_exit()的区别: 

a._exit()执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核。 

b. 调用_exit函数时,其会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数,但不会刷新流(stdin, stdout, stderr ...). exit函数是在_exit函数之上的一个封装,其会调用_exit,并在调用之前先刷新流。

    

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


int main(void)

{

    printf("---liklon----\n");

    printf("---eepw-------");

    exit(0);   //exit()

}


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) { printf("---liklon----\n"); printf("---eepw-------"); _exit(0); //_exit() }






3.wait()和waitpid()

  

wait(等待子进程中断或结束)

定义函数 pid_t wait (int * status);

函数说明:

 wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用 wait()时子进程已经结束,则 wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数 status 可以设成 NULL。    

如果执行成功则返回子进程识别码(PID) ,如果有错误发生则返回返回值-1。失败原因存于 errno 中。

waitpid(等待子进程中断或结束)

定义函数  pid_t waitpid(pid_t pid,int * status,int options);

函数说明:

   waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用 wait()时子进程已经结束,则 wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数 status 返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数 status 可以设成 NULL。

  参数 pid 为欲等待的子进程识别码,其他数值意义如下:

       pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。

       pid=-1 等待任何子进程,相当于 wait()。            

         pid=0  等待进程组识别码与目前进程相同的任何子进程。       

        pid>0   等待任何子进程识别码为 pid 的子进程。

   参数 option :

    WNOHANG 如果没有任何已经结束的子进程则马上返回, 不予以等待。

    WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

如果执行成功则返回子进程识别码(PID) ,如果有错误发生则返回返回值-1。失败原因存于 errno 中。

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>
 

int main(void)
{
     pid_t pc,pr;
     
     if((pc = fork()) < 0)
     {
          printf("fork error\n");
          exit(1);
     }
     
     if(pc == 0)    //子进程
     {
         sleep(5);
         exit(0);
     }
     else
     {
         do
         {
              pr = waitpid(pc,NULL,WNOHANG);
              if(pr == 0) 
              {
                  printf("child process has not exit\n");
                  sleep(1);
              }   
         }while(pr == 0);  
 
         if(pr == pc)
         {
              printf("child process exit %d\n",pr);
              exit(0);
         }
         else
         {
              printf("error\n");
              exit(1);
         }
     }

}



4、守护进程

    

守护进程

  Linux大多数服务都是通过守护进程实现的,完成许多系统任务

  0: 调度进程,称为交换进程(swapper),内核一部分,系统进程

  1: init进程内核调用,负责内核启动后启动Linux系统没有终端限制

让某个进程不因为用户、终端或者其他的变化而受到影响,那么就必须把这个进程变成一个守护进程

进程组:一个或多个进程的集合

会话一个或多个进程组的集合开始于用户登录终止于用户退出此期间所有进程都属于这个会话期

  

守护进程编程步骤

  1. 创建子进程,父进程退出

    •所有工作在子进程中进行

    •形式上脱离了控制终端

  2. 在子进程中创建新会话

    •setsid()函数

    •使子进程完全独立出来,脱离控制

  3. 改变当前目录为根目录

    •chdir()函数

    •防止占用可卸载的文件系统

    •也可以换成其它路径

  4. 重设文件权限掩码

    •umask()函数

    •防止继承的文件创建屏蔽字拒绝某些权限

    •增加守护进程灵活性

  5. 关闭文件描述符

    •继承的打开文件不会用到,浪费系统资源,无法卸载

    •getdtablesize()

    •返回所在进程的文件描述符表的项数,即该进程打开的文件数目

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
 
 

int main(void)
{
     pid_t res;
     int i,fd_test;
     char *buf = "hi eepw--liklon daemon test\n";
     if((res = fork()) < 0)
     {
           printf("fork error\n");
           exit(1);
     }
     
     else if(res > 0)
     {
         exit(0);  //主进程退出
     }
         
     setsid();  //建立新的会话
     chdir("/");//改变工作路径
     umask(0);  //重设文件掩码
     
     for(i = 0;i < getdtablesize();i++)
     {
          close(i);   //关闭文件描述符
     } 
     
     while(1)
     {
         if((fd_test = open("/tmp/daemon.log",O_RDWR | O_CREAT | O_APPEND,777)) < 0) 
         {
              printf("open file error\n");
              exit(1);
         }
         write(fd_test,buf,strlen(buf) + 1);
         close(fd_test);
         sleep(10);
    
     }
     exit(0);
}




院士
2014-05-05 20:47:14     打赏
28楼

linux编程是一个多么深奥的编程啊~~

版主 加油啊~~


高工
2014-05-07 01:11:23     打赏
29楼

十一、进程间通信--管道

     1.无名管道

    无名管道:管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。

     单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

    数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。(有点像队列哈)

#include <unistd.h>

int pipe(int fd[2])

    该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由 pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。

    向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。

   

   管道的创建和关闭

   管道是基于文件描述符的通信方式,当一个管道建立时会产生两个文件描述符,fds[0]fds[1]fds[0]专用于读管道,fds[1]专用于写管道。关闭时只需要用close函数关闭各个文件描述符。

   先创建一个管道,然后创建子进程,子进程会继承父进程所创建的管道fds[0]fds[1]

那么父子进程就可以互相通信了,比如现在需要父进程写,子进程读。那么可以关闭父进程的读fds[0]同时关闭子进程的写fds[1]

   

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <errno.h>

#include <stdlib.h>

#include <string.h>



int main()

{

    pid_t pid_tmp;

    int fd_p[2],real_write,real_read;

    char buf[20];

    const char strWrite[] = "eepw--liklon--"; 

    

    if(pipe(fd_p) < 0)

    {

        printf("pipe error\n");

        exit(1);       //建立无名管道

    } 

    pid_tmp = fork();  //建立子进程

    

    if(pid_tmp == 0)    //子进程

    {    

        close(fd_p[1]); //关掉写 

        memset((void *)buf,0,20);

        sleep(5);  //睡眠       

        

        if((real_read = read(fd_p[0],buf,20)) > 0)

        {

             printf("child read:%s\n",buf);

        }     

        close(fd_p[0]);

        exit(0);

    }

    else if(pid_tmp > 0)

    {

        close(fd_p[0]);  //关掉读

        sleep(1);

        if((real_write = write(fd_p[1],strWrite,strlen(strWrite))) != -1)

        {

             printf("parent write:%s\n",strWrite);

        }

        close(fd_p[1]);  //关闭写

        waitpid(pid_tmp,NULL,0);

        exit(0);

    }

}

 


高工
2014-05-07 01:12:13     打赏
30楼


2.名管道

   

无名管道是只能用于具有亲缘关系的进程之间,大大限制了管道的使用,有名管道可以是互补相关的两个进程实现彼此的通信。该管道可以通过路径名来指出并且在文件系统是可见的。在建立管道后,两个进程可以把它当作普通文件一样进行读写操作。FIFO是遵循先进先出的规则,对管道及FIFO的读总是从开始出返回数据,对他们的写则把数据添加到末尾。不支持如lseek()等文件定位操作。

1.管道是阻塞打开

  对于读进程,当前FIFO内没有数据,读进程将会一直阻塞到有数据写入。

  对于写进程,写操作将一直阻塞到数据可以被写入

 2.管道是非阻塞打开

   对于读进程,无论FIFO内是否有数据,读进程都会立即执行读操作,如果没有数据读函数将返回0

   对于写进程,此时如果不能写入全部数据,则进行部分写入或者调用失败


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h> 
#define fifoName  "/tmp/myfifo"  //管道路径名
#define MAX_SIZE  PIPE_BUF

int main()
{
    char buf[MAX_SIZE];
    int  fd;
    int  real_read;
    
  
    if( access(fifoName,F_OK) < 0)   //用来判断文件是否存在
    {
        if(mkfifo(fifoName,0666) < 0 && errno != EEXIST)
        {
             printf("mkfifo error\n"); //创建管道失败
             exit(1);
        }
    }
    if((fd = open(fifoName,O_RDONLY)) < 0)   //打开文件
    {
        printf("open file error\n");
        exit(1);
    }
    
    while(1)
    {
         memset(buf,0,MAX_SIZE);
         real_read = read(fd,buf,MAX_SIZE);
         if(real_read > 0)
         {
              printf("read:%s\n",buf);
         }
    }
    close(fd);
    exit(0);

}


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>

#define fifoName  "/tmp/myfifo"  //管道路径名
#define MAX_SIZE  PIPE_BUF

int main(int argc,char *argv[])
{
    char buf[MAX_SIZE];
    int  fd;
    int  real_write;
    
    if(argc != 2)
    {
        printf("please enter write message\n");
        exit(1);
    }

    if( access(fifoName,F_OK) < 0)   //用来判断文件是否存在
    {
        if(mkfifo(fifoName,0666) < 0 && errno != EEXIST)
        {
             printf("mkfifo error\n"); //创建管道失败
             exit(1);
        }
    }
    if((fd = open(fifoName,O_WRONLY)) < 0)   //打开文件
    {
        printf("open file error\n");
        exit(1);
    }
    
    sscanf(argv[1],"%s",buf);
    printf("write: %s\n",buf);
    real_write = write(fd,buf,MAX_SIZE);
    close(fd);
    exit(0);

}


开两个终端进行测试:





共87条 3/9 1 2 3 4 5 6 ›| 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]