精华内容
下载资源
问答
  • 三、java8htmljava线程同时写一个文件java高并发环境下线程同时写入一个文件时,经过 FileLock 加锁,能够控制对文件的并发操做。同一个JVM,能够共享部份内存java第一种状况是:一个线程A有对文件加锁,另外一...

    测试&思考:

    环境:windows 七、linux centos 6.三、java8html

    java多线程同时写一个文件

    java高并发环境下多线程同时写入一个文件时,

    经过 FileLock 加锁,能够控制对文件的并发操做。同一个JVM,能够共享部份内存java

    第一种状况是:一个线程A有对文件加锁,另外一个线程B没对文件加锁

    在windows7环境下:(持有锁的能够写文件成功)。

    持有锁的线程A会有对文件的操做权限,没加锁的线程B没有对文件的操做权限,会报错退出,以下:linux

    java.io.IOException: 另外一个程序已锁定文件的一部分,进程没法访问。

    在linux centos 6.3环境下:(均可以写文件成功,表现为数据交叉写入)

    互不影响,线程A和B都有对文件的操做权限web

    第二种状况两个线程都有加锁

    在windows7环境和linux centos 6.3环境下表现同样:(持有锁的能够写文件成功)

    一个线程A竞争到锁,会有对文件的操做权限,另外一个线程B没有竞争到锁,没有对文件的操做权限,会报错退出,而不是发生阻塞。以下:shell

    java.nio.channels.OverlappingFileLockException

    在高并的这种生产状况下,须要捕获这个异常,并处理,以下:windows

    while (true) {

    try {

    flout = fcout.tryLock();

    break;

    } catch (Exception e) {

    //计数等其余操做...

    sleep(1000);

    }

    }

    多进程同时写一个文件

    若是同为java进程,则是不一样的JVM。不能够共享内存centos

    若是同为java进程,一个进程A有对文件加锁,另外一个进程B没对文件加锁

    在windows7环境下:(持有锁的能够写文件成功)。

    持有锁的进程 A会有对文件的操做权限,没加锁的进程 B没有对文件的操做权限,会报错退出,以下:服务器

    java.io.IOException: 另外一个程序已锁定文件的一部分,进程没法访问。

    在linux centos 6.3环境下:(均可以写文件成功,表现为数据交叉写入)

    互不影响,进程A和B都有对文件的操做权限多线程

    若是同为java进程,两个进程都加锁

    在windows7环境和linux centos 6.3环境下表现同样:

    谁先得到锁,谁先得到对文件的操做权限,另外一个进程则会等待第一个进程处理完成,才会得到锁,再对文件进行处理。在这里是发生阻塞,而不是抛出异常(注意与多线程加锁的区别)。

    由此能够证实:针对对多进程同时操做同一个文件,在这里应该是底层JVM层面或者本地方法接口库对这个文件进行了加锁。并发

    一个为java进程,另外一个为非Java进程

    此处操做全在服务器centos6.3上测试,非Java进程为简单的 shell 进程,例如:

    for((i=1;i<10;i++));do echo 333 >> tmp.txt;sleep 1; done

    java进程无锁的状况

    互不影响,java进程和非java进程都有对文件的操做权限

    java进程无锁的状况

    互不影响,java进程和非java进程都有对文件的操做权限

    总结

    因而可知,在java高并发(不管是多线程仍是多进程)同时操做文件时。

    若是没有文件顺序的限制,能够不加锁,在这里有操做系统为咱们兜底(这里有风险,是否是全部的操做系统都为咱们兜底呢)不会产生脏数据;

    若是有文件顺序要求的限制,在这里不管是多线程仍是多进程(前提是Java服务),均可以获得很好的并发控制

    若是能够接受加锁的开销和复杂度,只要遇到并发操做文件时均可以加锁。这样能够保证100%不会乱序,不用考虑是否操做系统会不会为咱们兜底了。

    若是是使用FileLock try() 方法,同进程内的多线程访问, lock会直接报OverlappingFileLockException, 而不是一直阻塞。 若是是多进程, lock会一直阻塞,而不会包OverlappingFileLockException

    这代表java提供的FileLock是面向整个虚拟机的,即进程级别的。合理利用FileLock,加上多线程的异常处理控制。就能够在多线程和多进程场景下实现对文件的高并发访问控制

    FileLock 做用于java的进程级别,不管独占锁、共享锁都是针对不一样的进程,线程之间不适用。

    测试代码

    package com.dxm.etl.test;

    import java.io.*;

    import java.nio.channels.FileChannel;

    import java.nio.channels.FileLock;

    public class TestFileLock {

    public static void main(String args[]) throws Exception {

    System.out.println(Thread.currentThread().getName());

    // new ThreadWriteFileWithoutLock("111").start();

    // Thread.sleep(1000);

    new ThreadWriteFileWithLock("222").start();

    }

    private static class ThreadWriteFileWithLock extends Thread {

    private String threadName;

    public ThreadWriteFileWithLock(String threadName) {

    this.threadName = threadName;

    }

    public void run() {

    long t1 = System.currentTimeMillis();

    File file = new File("tmp.txt");

    FileOutputStream output = null;

    BufferedWriter br = null;

    FileChannel fileChannel = null;

    try {

    output = new FileOutputStream(file, true);

    br = new BufferedWriter(new OutputStreamWriter(output,"UTF-8"));

    //对该文件加锁

    fileChannel = output.getChannel();

    FileLock fileLock = null;

    fileLock = fileChannel.lock(0,Long.MAX_VALUE,false);

    System.out.println(fileLock);

    System.out.println(fileLock.isShared());

    //非阻塞

    /*while (true) {

    try {

    flout = fcout.tryLock();

    break;

    } catch (Exception e) {

    System.out.println("有其余线程正在操做该文件,当前线程休眠1000毫秒");

    sleep(1000);

    }

    }*/

    for (int i = 1; i <= 10; i++) {

    sleep(1000);

    br.write(threadName+"\n");

    br.flush();

    }

    fileLock.release();

    } catch (Exception e) {

    e.printStackTrace();

    System.out.println(threadName +" err");

    } finally {

    try {

    br.close();

    fileChannel.close();

    output.close();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    System.out.println(threadName + "有锁,写文件共花了" + (System.currentTimeMillis() - t1) + "ms");

    }

    }

    public static class ThreadWriteFileWithoutLock extends Thread {

    private String threadName;

    public ThreadWriteFileWithoutLock(String threadName) {

    this.threadName = threadName;

    }

    public void run() {

    long t1 = System.currentTimeMillis();

    File file = new File("tmp.txt");

    FileOutputStream output = null;

    BufferedWriter br = null;

    try {

    output = new FileOutputStream(file, true);

    br = new BufferedWriter(new OutputStreamWriter(output,"UTF-8"));

    for (int i = 1; i <= 10; i++) {

    sleep(1000);

    br.write(threadName+"\n");

    br.flush();

    }

    } catch (Exception e) {

    e.printStackTrace();

    System.out.println(threadName +" err");

    } finally {

    try {

    br.close();

    output.close();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    System.out.println(threadName + "无锁,写文件共花了" + (System.currentTimeMillis() - t1) + "ms");

    }

    }

    }

    展开全文
  • 多进程中之文件描述符继承的消除

    千次阅读 2021-02-07 10:04:33
    当父进程创建子进程时,无论 fork 函数或者是 vfork 函数,子进程通常都会继承父进程文件描述符。所谓的继承,就是子进程可以使用相同的文件描述符,和父进程操作同一个文件对象。如图所示 这种可能会造成...

    多进程中之文件描述符继承的消除

    什么是文件描述符的继承

    读取目录的基本操作

    文件描述符的消除实现

    总结


    什么是文件描述符的继承

         当父进程创建子进程时,无论 fork 函数或者是 vfork 函数,子进程通常都会继承父进程的文件描述符。所谓的继承,就是子进程可以使用相同的文件描述符,和父进程操作同一个文件对象。如图所示

    父子进程共享文件描述符

         这种可能会造成权限安全隐患。怎么办呢?

          最简单的做法当然就是什么也不做。告诉开发人员,父子进程之间这种共享文件对象的方式很危险,你自己开着办,除了事情自己负责,当然这种处理方式,对于执行体程序库而言代价最小,因为不用添加任何代码,顶多在文档上写两句说明的话。很显然,这种方法是一种走投无路的方法。

         第二种方法,就是在刚创建完子进程后,立马把它继承而来的文件描述符给关闭了。关闭文件描述符一般使用的是 close 函数。唯一麻烦的是怎么知道进程打开了那些文件描述符?

    读取目录的基本操作

    通常在 Linux 系统中,/proc 目录记录了各个进程运行时的信息,在/proc 中每个进程都有一个目录,该目录以进程 ID 命名。而在每个进程的所属目录中,又有一个名为“fd”的目录。进程打开的文件描述,在该目录中对应了一个符号链接文件,而该文件的名称,即文件描述符的数值。好了,现在看来问题就好办多了。只要我们能遍历“/proc/PID/fd”目录,就能获得当前进程打开的文件描述符,然后我们再为其调用 close 函数即可。

    那么,如何遍历一个目录呢?

    要遍历一个目录,首先得调用 opendir 函数打开目录,然后再调用 readdir 函数获取目录中子目录名称和文件名称,最后调用 closedir 函数关闭目录。好了,下面首先看看这几个函数的原型。

    #include <dirent.h>
      DIR* opendir(const char* name)    
    

    在读取目录之处,需要首先调用 opendir。该函数接收一个参数,即需要打开的目录名称,当然这个名称需要带路径信息。当 opendir 函数调用成功时,会返回一个 DIR 结构体指针,出错则会返回 0。这个 DIR 结构体的指针,将会被用于后续的 readdir 函数和 closedir 函数的调用。好了,下面来看看 readdir 函数的原型:

    #include <dirent.h>
    struct dirent* readdir(DIR *dirp);
    

    readdir 函数只接收一个参数,即 opendir 函数的返回值,表示要从在调用 opendir 函数时指定的目录中读取信息。调用一次 readdir 函数就会返回目录中的一个文件或子目录相关的结构体实例指针,即 dirent 结构体指针。下一次再调用 readdir 函数时,又会返回目录中下一个文件或子目录对应的结构体实例指针。

    当所有文件或者子目录都返回过了之后,再调用 readdir 函数将返回 0。如此不断循环,就可以获得指定目录下的所有文件或子目录的 dirent 结构体实例了。那么现在,我们需要的文件名在哪里呢?实际上 dirent 结构体有一个 d_name 字段,该字段就是指向文件名或子目录名的字符串指针。当我们不再需要读目录时,为了释放资源,需要调用 closedir 函数关闭目录,该函数的原型如下:

    #include <dirent.h>
    int closedir(DIR* dirp);
    

    文件描述符的消除实现

    同 readdir 函数类似,closedir 函数也只接收一个参数,即 opendir 函数返回的 DIR 结构体的指针。若 closedir 函数调用成功则返回 0,否则返回-1。下面我们来看看具体的代码是怎么实现的。

       1 int Process::CloseFileDescriptor()                                                                                                       |
       2 {                                                                                                                                           
       3     string strPath = "/proc/";                                                                                                              
       4                                                                                                                                             |
       5     char id[LENGTH_OF_PROCESSID];                                                                          
       6     snprintf(id, LENGTH_OF_PROCESSID, "%d", m_ProcessID);                                                                                   
       7                                                                                                                                         |    
       8     strPath += id;                                                                                                                          
       9     strPath += "/fd";                                                                                                                       
      10     string strPath1 = strPath;                                                                                                                                                                                                                                                        
      11     strPath += "/";                                                                                                                         |~                             
      12                                                                                                                                            |~                             
      13     DIR *pDir = opendir(strPath.c_str());                                                                                                   |~                             
      14     if(pDir == 0)                                                                                                                           |~                             
      114     {                                                                                                                                       |~                             
      15             Logger::WriteLogMsg("In Process::CloseFileDescriptor(), opendir error", 0);                                                |~                 
      16             return -1;                                                                                                               |~                             
      17     }                                                                                                                                       |~                             
      18                                                                                                                                             |~                             
      19     while(struct dirent *pDirent = readdir(pDir))                                                                                           |~                             
      20     {                                                                                                                                       |~                             
      21         char captial = pDirent->d_name[0];                                                                                                  |~                             
      22         if((captial == '.') || (captial == '0') || (captial == '1') || (captial == '2'))                                                    |~                             
      23             continue;                                                                                                                       |~                             
      24                                                                                                                                             |~                             
      25          int fd = atoi(pDirent->d_name);                                                                                                     |~                             
      26          if(fd != 0)                                                                                                                         |~                             
      27         {                                                                                                                                   |~                             
      28             if(close(fd) == -1)                                                                                                             |~                             
      29             {                                                                                                                               |~                             
      30                 string errormsg = "In Process::CloseFileDescriptor(), close error, file: ";                                                 |~                             
      31                 errormsg += pDirent->d_name;                                                                                                |~                             
      32                 std::cout << errormsg << std:endl;                                                                               |~                             
      33             }                                                                                                                               |~                             
      34         }                                                                                                                                   |~                             
      35     }                      
      36    
      37    if(closedir(pDir) == -1)                                                                                                                |~                             
      38   {                                                                                                                                       |~                             
      39          Logger::WriteLogMsg("In Process::CloseFileDescriptor(), closedir error", errno);                                                |~                             
      40         return -1;                                                                                                               |~                             
      41     }                                                                                                                                       |~                             
      42                                                                                                                                             |~                             
      43     return 0;                                                                                                                    |~                             
      144 }                  
    

    这个函数作为进程类 Process 的私有成员函数放在创建新进程的成员函数中,在这里就不对我封装的进程类进行具体介绍了。Process 类的声明代码及创建子进程的 Run() 方法主要逻辑如下:

       #include <unistd.h>                                                                                                                           |
      ......                                                                                                                        |▼ Process : class
     1                                                                                                                                              |    [prototypes]
     2  class Process : public Executive                                                                                                              |   -CloseFileDescriptor()
     3  {                                                                                                                                             |   +Process(ExecutiveFunctionP
     4  public:                                                                                                                               |   -Process(const Process&)
     5    ......
     6     Process(ExecutiveFunctionProvider *pExecutiveFunctionProvider, bool bWaitForDeath);                                                       |   +~Process()
     12                                                                                                                                               |   +Run(void *pstrCmdLine = 0)
     13     virtual ~Process();                                                                                                                       |   +WaitForDeath()
     14                                                                                                                                               |   -operator =(const Process&)
     15     virtual int Run(void *pstrCmdLine = 0);                                                                                                |    [members]
     16     virtual int WaitForDeath();                                                                                                            |   -m_ProcessID
     17                                                                                                                                               |   -
     18 private:                                                                                                                                      |   -
     19     int CloseFileDescriptor();                                                                                                             |   -
     20                                                                                                                                               |~                             
     21 private:                                                                                                                                      |~                             
     22     Process(const Process&);                                                                                                                  |~                             
     23     Process& operator=(const Process&);                                                                                                       |~                             
     24                                                                                                                                               |~                             
     25     pid_t m_ProcessID;                                                                                                                        |~                             
     26                                                                                                                                               |~                             
     28    ......                                                                                                                 |~                             
     29 };                          
    

    接下来继续讨论 CloseFileDescriptor 函数,从上面的代码段我们可以看到,函数的主要逻辑,首先形成要访问的目录地址“/proc/进程 ID/fd”在第 13 行调用 opendir 函数打开指定目录,接着进入一个 while 循环遍历目录中的所有文件。

    这些文件都是进程已打开文件描述符的数值。在处理这些文件时,有几个特殊的文件需要区别对待,比如 . 和 ..。分别代表当前目录和父目录,因此不用理会它们。而文件名为 0、1、2 的文件,很显然分别对应于标准输入、标准输出和标准出错。这些文件描述符不应当被关闭,否则子进程即使成功执行 execv 函数后也无法正常执行输入输出。

    因此,子进程在第 22 行没有关闭上述文件描述符,之后,子进程在第 25 行将字符串形式的文件描述符转换成数值,然后在第 28 行调用 close 函数关闭文件。退出遍历文件的循环后,子进程又在第 37 行调用了 closedir 函数关闭刚才打开的目录。如果关闭失败,则记录日志信息。

    主要的实现介绍完了,那我们通过一个测试代码来看看这个函数。测试代码如下:

        1 int main(int argc, char *argv[])
        2 {
        3     cout << "in child main" << endl;
        4 
        5     if(write(3, "nihao", 5) == -1)
        6     cout << "child write error" << endl;
        7     else
        8     cout << "child write success" << endl;
        9 
       10     return 0;
       11 }
    

    这个测试代码很简单,首先在第 3 行向屏幕输出"in child main",然后又在第 5 行调用 write 函数向文件描述符 3 写了一条信息“nihao”,并根据 write 函数的返回值打印出相应的信息。正常情况下,除去 0、1、2 外,其余的都应该已经被关闭了。因此,write 函数应该返回出错。现在我们看下可执行程序的运行结果:

    看到这个运行结果,大家会大吃一惊吧。居然输出了“child write success”。换句话说,子进程在执行上面的 main 函数之后,向文件描述符 3 写的信息居然写成功了。

    而这个文件描述符 3 对应的是哪个文件呢? 我们打开日志文件看下:

    在这里插入图片描述

    可以看到,日志文件中的内容,正是向文件描述符 3 写入的“nihao”,也就是说,文件描述符 3 对应的就是日志文件。这就奇怪了,CloseFileDescriptor 函数找那个,不是已经把 0、1、2 之外的所有文件描述符关闭了吗,怎么 3 却没有关闭了呢?我们在看看上面 CloseFileDescriptor 函数的代码,在 37 行的 closedir 调用失败了。它一失败后,马上进行了写日志。由于之前无论父进程还是子进程都没有写日志。在这里的 WriteLogMsg 将导致日志对象被创建,而在日志对象构造中将调用 open 函数打开日志文件后,返回的文件描述符应该是 3。

    那么,在这里 closedir 函数调用失败又是怎么回事呢?在 Linux 系统,无论是目录也好、普通文件也罢,实际上都是文件的一种类型而已。那么 opendir 函数打开目录时,实际上也会执行 open 函数类似的操作,也会得到代表打开目录的文件描述符;同理,closedir 函数关闭目录时,也会关闭打开目录的文件描述符。但是在目录文件遍历代码中,除了 0、1、2 外,其他文件描述符都被关闭了,因此,在调用 closedir 函数时,对一个已经关闭了的文件描述符再执行关闭操作,理所当然关闭了。好了,closedir 函数调用出错的原因找到了,剩下的就是该怎么解决。

    大家可以在 Linux 系统上,自己注意观察“/proc/进程 ID/fd”目录中的文件,就会发现它们实际上都是符号链接文件。这些符号链接文件类似于 Windows 系统中的快捷方式,它会指向另一个文件。如果我们在遍历目录中的文件时,能够获取符号链接文件实际指向的文件名称,那事情就好办多了。

    我们可以把这个文件名称,同“/proc/进程 ID/fd”相比较,如果相同,则说明了当前正在处理的文件描述符所对应的目录,正是我们遍历的目录,不要为它调用 close 函数,而应该等待目录遍历完毕后调用 closedir 函数关闭它。现在我们看看如果读取符号链接文件实际指向的文件名称,这个很简单,可以直接调用 readlink 函数,下面给出了该函数的原型:

    #include <unistd.h>
    ssize_t readlink(const char* path, char* buf, size_t bufsize);
    

    readlink 函数接收 3 个参数,第一个就是要读取的符号链接文件的路径信息;当读出了符号链接文件实际指向的文件名称后,readlink 函数会将其写入第二个参数 buf 所指向的缓存中;而第三个参数 bufsize,即 buf 所指向的缓存大小。readlink 函数调用成功后,会返回写入缓存中的字节数,失败则返回 -1。不过有一点需要提醒,该函数向缓存填入信息时,结尾并不会填 0。因此,缓存的初始化操作需要调用者完成。好了,现在我们对刚才的 CloseFileDescriptor 函数进行修改。

      1   int Process::CloseFileDescriptor()                                                                                                       |▶ macros
         {                                                                                                                                           |
      2      string strPath = "/proc/";                                                                                                              |▼ Process* : class
      3                                                                                                                                             |    [functions]
      4      char id[LENGTH_OF_PROCESSID];                                                                                                           |    CloseFileDescriptor()
      5      snprintf(id, LENGTH_OF_PROCESSID, "%d", m_ProcessID);                                                                                   |    Process(ExecutiveFunctionP
      6                                                                                                                                             |    Process(ExecutiveFunctionP
      7      strPath += id;                                                                                                                          |    ~Process()
      8      strPath += "/fd";                                                                                                                       |    Run(void *pstrCmdLine)
      9                                                                                                                                             |    WaitForDeath()
      10     string strPath1 = strPath;                                                                                                              |~                             
      11                                                                                                                                             |~                             
      12     strPath += "/";                                                                                                                         |~                             
      13                                                                                                                                             |~                             
      14     DIR *pDir = opendir(strPath.c_str());                                                                                                   |~                             
      15     if(pDir == 0)                                                                                                                           |~                             
      16    {                                                                                                                                       |~                             
      17         Logger::WriteLogMsg("In Process::CloseFileDescriptor(), opendir error", 0);                                                         |~                             
      18         return -1;                                                                                                               |~                             
      19     }                                                                                                                                       |~                             
      20                                                                                                                                             |~                             
      21     while(struct dirent *pDirent = readdir(pDir))                                                                                           |~                             
      22     {                                                                                                                                       |~                             
      23         char captial = pDirent->d_name[0];                                                                                                  |~                             
      24         if((captial == '.') || (captial == '0') || (captial == '1') || (captial == '2'))                                                    |~                             
      25             continue;                                                                                                                       |~                             
      26                                                                                                                                             |~                             
      27         int fd = atoi(pDirent->d_name);                                                                                                     |~                             
      28         if(fd != 0)                                                                                                                         |~                             
      29         {                                                                                                                                   |~                             
      30             string strTmpPath = strPath;                                                                                                    |~                             
      31             strTmpPath += pDirent->d_name;                                                                                                  |~                             
      32                                                                                                                                             |~                             
      33             char pathname[LENGTH_OF_PATH] = {0};                                                                                            |~                             
      34             if(readlink(strTmpPath.c_str(), pathname, LENGTH_OF_PATH) == -1)                                                                |~                             
      35             {                                                                                                                               |~                             
      36                 Logger::WriteLogMsg("In Process::CloseFileDescriptor(), readlink error", errno);                                            |~                             
      37                 continue;   
      38                }                                                                                                                               |~                             
      39                                                                                                                                             |~                             
      40             if(strcmp(pathname, strPath1.c_str()) == 0)                                                                                     |~                             
      41                 continue;                                                                                                                   |~                                                                                                                                                                          |~                             
      42             if(close(fd) == -1)                                                                                                             |~                             
      43             {                                                                                                                               |~                             
      44                 string errormsg = "In Process::CloseFileDescriptor(), close error, file: ";                                                 |~                             
      45                 errormsg += pDirent->d_name;                                                                                                |~                             
      46                 Logger::WriteLogMsg(errormsg.c_str(), errno);                                                                               |~                             
      47             }                                                                                                                               |~                             
      48         }                                                                                                                                   |~                             
      49     }                                                                                                                                       |~                             
      50                                                                                                                                             |~                             
      51     if(closedir(pDir) == -1)                                                                                                                |~                             
      52     {                                                                                                                                       |~                             
      53         Logger::WriteLogMsg("In Process::CloseFileDescriptor(), closedir error", errno);                                                    |~                             
      54         return -1;                                                                                                               |~                             
      55     }                                                                                                                                       |~                             
      56                                                                                                                                             |~                             
      57     return 0;                                                                                                                    |~                             
      58 }                            
    

    以上代码是对 CloseFileDescriptor 函数的修改。主要的改动,是在遍历目录文件时,调用了 readlink 函数获取符号链接文件实际指向的文件名称,即第 34 行。然后又在第 40 行将该名称同“/proc/进程 ID/fd”相比较。如果相同则不予理会。等待退出循环后,再调用 closedir 函数关闭目录。那么这样写文件描述符 3 的问题就算是搞定了。

    总结

    由继承而来的文件描述所引发的一系列问题,在这里我们就算告一段落了。我们讨论了如何遍历目录得到进程已打开的文件描述符,如何读取符号链接文件以规避正使用的目录描述符。如果感兴趣的同学可以关注我之后的专题文章。

    展开全文
  • 进程可同时对同一个文件作共享锁定。 LOCK_EX 建立互斥锁定。一个文件同时只有一个互斥锁定。 LOCK_UN 解除文件锁定状态。 LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程。通常与LOCK_SH或LOCK_EX 做...

    文件锁

    #include <sys/file.h>
    int flock(int fd, int operation);
    

    参数说明:

    • LOCK_SH 建立共享锁定。多个进程可同时对同一个文件作共享锁定。
    • LOCK_EX 建立互斥锁定。一个文件同时只有一个互斥锁定。
    • LOCK_UN 解除文件锁定状态。
    • LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程。通常与LOCK_SH或LOCK_EX 做OR(|)组合。
      单一文件无法同时建立共享锁定和互斥锁定,而当使用dup()或fork()时文件描述词不会继承此种锁定。

    返回值说明:

    返回0表示成功,若有错误则返回-1,错误代码存于errno。

    lock()会依参数operation所指定的方式对参数fd所指的文件做各种锁定或解除锁定的动作。此函数只能锁定整个文件,无法锁定文件的某一区域。

    使用方式:

    1. flock只要在打开文件后,需要对文件读写之前flock一下就可以了,用完之后再flock一下,前面加锁,后面解锁。
    2. 进程使用flock尝试锁文件时,如果文件已经被其他进程锁住,进程会被阻塞直到锁被释放掉,或者在调用flock的时候,采用LOCK_NB参数,在尝试锁住该文件的时候,发现已经被其他服务锁住,会返回错误。
    3. 可调用LOCK_UN参数来释放文件锁,也可以通过关闭fd的方式来释放文件锁(flock的第一个参数是fd),意味着flock会随着进程的关闭而被自动释放掉

    测试

    测试用例1

    进程A:打开要操作的文件,进行加锁,在文件中写入数据,等待键盘输入字符(此时该A进程处于等待状态,立刻运行B进程),键盘输入字符后,发现加锁文件中出现text字符串

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include<sys/file.h>
    static const char jsonfile[] = "new.json";
    int main()
    {
       FILE *fp;
      char buf[1024] = {0};
      char text[] = "this is write test=========\n";
      if ((fp = fopen((jsonfile), "aw+")) == NULL)
        return 0;
    
      if (0 == flock(fileno(fp), LOCK_EX))
      {
        printf("lock...\n");
        fseek(fp, 0, SEEK_END);
        fwrite(text, strlen(text), 1, fp);
    
        getchar();
        fclose(fp);
        flock(fileno(fp), LOCK_UN);
      }
      else
      {
        printf("lock failed\n");
      }
      return 0;
    }
    

    进程B:打开要操作的文件,进行加锁并设置立即返回参数,如果返回值是-1,说明文件已被其他进程占用,然后进行持续加锁,直到成功加锁(此时刻是由于A进程在键盘中输入了字符,释放了锁)睡眠2s后退出加锁持续,对文件进行数据追加写入操作,发现在A进程中的text字段后面出现了该B进程写入的数据,即可说明文件锁生效,能成功保护文件被两个进程同时读写操作

    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/file.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    
    static const char jsonfile[] = "new.json";
    
    int main()
    {
       char texts[] = "-----------this is read test\n";
       FILE *fp;
       if ((fp = fopen(jsonfile, "aw+")) == 0)
          printf("can't open file!\n");
       else
       {
          printf("open file success!\n");//LOCK_NB 无法建立锁定时,此操作可不被阻断,马上返回进程
          int i = flock(fileno(fp), LOCK_SH | LOCK_NB); // 加锁以判断文件是否已经被加锁了,单一文件无法同时建立共享锁定和互斥锁定
          printf("%d\n", i);                          //-1表示文件被其他进程加锁,0表示文件被该进程加锁
          if (i == -1)
          {
             printf("lock by others ...\n");
             while (1)
             {
                i = flock(fileno(fp), LOCK_SH | LOCK_NB);
                if (i == 0)
                {
                   sleep(2);
                   printf("lock file success!!!!!!!!!!!\n");
                   break;
                }
             }
          }
          flock(fileno(fp), LOCK_UN);
          fwrite(texts, strlen(texts), 1, fp);
       }
    
       fclose(fp);
       return 0;
    }
    

    被两个进程同时操作的new.json数据文件内容如下:

    this is write test=========
    -----------this is read test

    第二行数据在第一行数据出现2S后才出现

    结果说明:验证了flock函数在多进程操作同一个文件时对文件的保护作用

    测试用例2

    多进程同时操作同一个文件无延时循环读写测试

    进程A:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include<sys/file.h>
    static const char jsonfile[] = "new.json";
    
    int main()
    {
      char text[] = "this is write test=========\n";
      while (1)
      {
        FILE *fp;
        if ((fp = fopen((jsonfile), "aw+")) == NULL)
          return 0;
        if (0 == flock(fileno(fp), LOCK_EX))
        {
          printf("lock...\n");
          fseek(fp, 0, SEEK_END);
          fwrite(text, strlen(text), 1, fp);
    
          //getchar();
          fclose(fp);
          flock(fileno(fp), LOCK_UN);
        }
        else
        {
          printf("lock failed\n");
        }
      }
      //usleep(500);
      return 0;
    }
    

    进程B:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include<sys/file.h>
    static const char jsonfile[] = "new.json";
    
    int main()
    {
      char text[] = "-----------this is read test\n";
      while (1)
      {
        FILE *fp;
        if ((fp = fopen((jsonfile), "aw+")) == NULL)
          return 0;
        if (0 == flock(fileno(fp), LOCK_EX))
        {
          printf("lock...\n");
          fseek(fp, 0, SEEK_END);
          fwrite(text, strlen(text), 1, fp);
    
          //getchar();
          fclose(fp);
          flock(fileno(fp), LOCK_UN);
        }
        else
        {
          printf("lock failed\n");
        }
      }
      //usleep(500);
      return 0;
    }
    

    经过测试两个进程在一个文件中连续不断写入数据,写入数据到该文件达到100M后停止写入,然后分析文件中的数据

    分析写入数据正确性的程序:

    #include <stdio.h>
    #include <string.h>
    
    static const char jsonfile[] = "/home/nfs/new.json";
    char texts[] = "this is write test=========";
    char text[] = "-----------this is read test";
    int main()
    {
      char buff[1024] = {0};
      int bufflinenum = 0, num = 0;
      FILE *fp;
      if ((fp = fopen(jsonfile, "r+")) == 0)
        printf("can't open file!\n");
      while (!feof(fp))
      {
        if (fgets(buff, sizeof(buff), fp) != NULL)
        {
          bufflinenum++;
          if((strncmp(buff, text, strlen(buff)-1) != 0 ) && strncmp(buff, texts,strlen(buff)-1) != 0 )
          {
             num++;
             printf("出错行内容:\n%s", buff);
          }
          else
          {
            //printf("%s", buff);
          }
          
        }
      }
      printf("文件总行数:%d\n出错行数:%d\n", bufflinenum, num);
    
      return 0;
    }
    

    分析结果:
    在这里插入图片描述

    展开全文
  • java多进程

    2021-03-14 01:27:06
    进程间的通讯无非就是读写文件,socket通讯或者使用共享内存。你不想用读写文件的方式,那就用共享内存或者socket通讯的方式。我个人觉得用socket比较简单,也许是因为我对socket比较熟悉。下面是一篇java实现共享...

    进程间的通讯无非就是读写文件,socket通讯或者使用共享内存。

    你不想用读写文件的方式,那就用共享内存或者socket通讯的方式。我个人觉得用socket比较简单,也许是因为我对socket比较熟悉。

    下面是一篇java实现共享内存的文章,java没法管理内存,其实他也是靠创建映像文件来实现的。

    共享内存在java中的实现

    在jdk1.4中提供的类MappedByteBuffer为我们实现共享内存提供了较好的方法。该缓冲区实际上是一个磁盘文件的内存映像。二者的变化将保持同步,即内存数据发生变化会立刻反映到磁盘文件中,这样会有效的保证共享内存的实现。

    将共享内存和磁盘文件建立联系的是文件通道类:FileChannel。该类的加入是JDK为了统一对外部设备(文件、网络接口等)的访问方法,并且加强了多线程对同一文件进行存取的安全性。例如读写操作统一成read和write。这里只是用它来建立共享内存用,它建立了共享内存和磁盘文件之间的一个通道。

    打开一个文件建立一个文件通道可以用RandomAccessFile类中的方法getChannel。该方法将直接返回一个文件通道。该文件通道由于对应的文件设为随机存取文件,一方面可以进行读写两种操作,另一方面使用它不会破坏映像文件的内容(如果用FileOutputStream直接打开一个映像文件会将该文件的大小置为0,当然数据会全部丢失)。这里,如果用 FileOutputStream和FileInputStream则不能理想的实现共享内存的要求,因为这两个类同时实现自由的读写操作要困难得多。

    下面的代码实现了如上功能,它的作用类似UNIX系统中的mmap函数。

    // 获得一个只读的随机存取文件对象

    RandomAccessFile RAFile =>FileChannel fc = RAFile.getChannel();// 取得文件的实际大小,以便映像到共享内存

    int size = (int)fc.size();// 获得共享内存缓冲区,该共享内存只读

    MappedByteBuffer mapBuf = fc.map(FileChannel.MAP_RO,0,size);// 获得一个可读写的随机存取文件对象

    RAFile = new RandomAccessFile(filename,"rw");// 获得相应的文件通道

    fc = RAFile.getChannel();// 取得文件的实际大小,以便映像到共享内存

    size = (int)fc.size();// 获得共享内存缓冲区,该共享内存可读写

    mapBuf = fc.map(FileChannel.MAP_RW,0,size);// 获取头部消息:存取权限

    mode = mapBuf.getInt();

    如果多个应用映像同一文件名的共享内存,则意味着这多个应用共享了同一内存数据。这些应用对于文件可以具有同等存取权限,一个应用对数据的刷新会更新到多个应用中。

    为了防止多个应用同时对共享内存进行写操作,可以在该共享内存的头部信息加入写操作标志。该共享内存的头部基本信息至少有:

    int Length; // 共享内存的长度。

    int mode; // 该共享内存目前的存取模式。

    共享内存的头部信息是类的私有信息,在多个应用可以对同一共享内存执行写操作时,开始执行写操作和结束写操作时,需调用如下方法:

    public boolean StartWrite()

    {

    if(mode == 0) { // 标志为0,则表示可写     mode = 1; // 置标志为1,意味着别的应用不可写该共享内存     mapBuf.flip();

    mapBuf.putInt(mode); // 写如共享内存的头部信息     return true;

    }

    else {

    return false; // 指明已经有应用在写该共享内存,本应用不可写该共享内存  }

    }public boolean StopWrite()

    {

    mode = 0; // 释放写权限  mapBuf.flip();

    mapBuf.putInt(mode);// 写入共享内存头部信息  return true;

    }

    这里提供的类文件mmap.java封装了共享内存的基本接口,读者可以用该类扩展成自己需要的功能全面的类。

    如果执行写操作的应用异常中止,那么映像文件的共享内存将不再能执行写操作。为了在应用异常中止后,写操作禁止标志自动消除,必须让运行的应用获知退出的应用。在多线程应用中,可以用同步方法获得这样的效果,但是在多进程中,同步是不起作用的。方法可以采用的多种技巧,这里只是描述一可能的实现:采用文件锁的方式。写共享内存应用在获得对一个共享内存写权限的时候,除了判断头部信息的写权限标志外,还要判断一个临时的锁文件是否可以得到,如果可以得到,则即使头部信息的写权限标志为1(上述),也可以启动写权限,其实这已经表明写权限获得的应用已经异常退出,这段代码如下:

    // 打开一个临时的文件,注意同一共享内存,该文件名要相同,可以在共享文件名后加后缀“.lock”。

    RandomAccessFile fis = new RandomAccessFile("shm.lock","rw");// 获得文件通道

    FileChannel lockfc = fis.getChannel();// 获得文件的独占锁,该方法不产生堵塞,立刻返回

    FileLock flock = lockfc.tryLock();// 如果为空,则表明已经有应用占有该锁

    if(flock == null) {...// 不能执行写操作

    }else {...// 可以执行写操作

    }

    进程间通信的主要方法有: (1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。 (2)命名管道(named> Signal sig = new Signal("USR2"); Signal.handle(sig,> Process process = rt.exec("java com.test.process.T3"); 2、 ProcessBuilder> Process p = pb.start(); 二、Java父、子进程通信方式(管道方式) 父进程获取子进程输出流方式 BufferedInputStream> BufferedReader br = new BufferedReader(new InputStreamReader(in)); String>   System.out.println(s); } 子进程获取父进程输入流方式 package> while (true) { String> if (strLine != null) { System.out.println("hi:" +> StringBuilder> for(int k=0;k<1;k++){ sbuilder.append("hello"); } int> TestOut out[] = new TestOut[outSize]; for(int> out[i] = new TestOut(p,sbuilder.toString().getBytes()); new Thread(out[i]).start(); } int> TestIn in[] = new TestIn[inSize]; for(int j=0;j while (true) { String> if (strLine != null) { System.out.println(strLine);//这个地方的输出在子进程控制台是无法输出的,只可以在父进程获取子进程的输出 }else { return; } } } } TestIn类 package> public TestIn(Process process){ p => BufferedReader bfr = new BufferedReader(new InputStreamReader(in)); String> if(rd != null){ System.out.println(rd);//输出子进程返回信息(即子进程中的System.out.println()内容) }else{ return; } //注意这个地方,如果关闭流则子进程的返回信息无法获取,如果不关闭只有当子进程返回字节为8192时才返回,为什么是8192下面说明. //bfr.close(); //in.close(); }> private byte []b = null; public TestOut(Process> b = byt; } @Override public> //System.out.println("out--"+b.length); ops.write(b); //注意这个地方如果关闭,则父进程只可以给子进程发送一次信息,如果这个地方开启close()则父进程给子进程不管发送大小多大的数据,子进程都可以返回 //如果这个地方close()不开启,则父进程给子进程发送数据累加到8192子进程才返回。 //ops.close(); }> TestOut> new Thread(out).start(); TestIn> new Thread(ti).start(); Thread.sleep(3000); TestOut> new Thread(out2).start(); TestIn> new Thread(ti2).start(); } } 执行后输出结果为: Hello> TestOut> new Thread(out).start(); TestIn> new Thread(ti).start(); Process> TestOut out2 = new TestOut(p2,"-Hello-everyone".getBytes()); new Thread(out2).start(); TestIn> new Thread(ti2).start(); } } 输出结果: Hello> public Process>             throw new IllegalArgumentException("Empty command"); StringTokenizer> String[] cmdarray = new String[st.countTokens()]; for (int>     cmdarray[i] = st.nextToken(); return> stdout_fd = new FileDescriptor(); stderr_fd => stdin_fd, stdout_fd, stderr_fd); java.security.AccessController.doPrivileged(    >     new BufferedOutputStream(new FileOutputStream(stdin_fd)); stdout_stream =    >     new FileInputStream(stderr_fd); return> 另外Java中还提供了PipedInputStream、PipedOutputStream类,但这2个类用在多进程间交互是无法实现的。 总结: 1、如果Java中要涉及到多进程之间交互,子进程只是简单的做一些功能处理的话建议使用 Process> p.getOutputStream() p.getInputStream() 的方式进行输入、输出流的方式进行通信 如果涉及到大量的数据需要在父子进程之间交互不建议使用该方式,该方式子类中所有的System都会返回到父类中,另该方式不太适合大并发多线程 2、内存共享(MappedByteBuffer) 该方法可以使用父子进程之间通信,但在高并发往内存内写数据、读数据时需要对文件内存进行锁机制,不然会出现读写内容混乱和不一致性,Java里面提供了文件锁FileLock,但这个在父/子进程中锁定后另一进程会一直等待,效率确实不够高。 RandomAccessFile> FileChannel fc = raf.getChannel();   MappedByteBuffer> FileLock fl = fc.lock();//文件锁 3、Socket 这个方式可以实现,需要在父子进程间进行socket通信 4、队列机制 这种方式也可以实现,需要父/子进程往队列里面写数据,子/父进程进行读取。不太好的地方是需要在父子进程之间加一层队列实现,队列实现有ActiveMQ,FQueue等 5、通过JNI方式,父/子进程通过JNI对共享进程读写 6、基于信号方式,但该方式只能在执行kill -12或者在cmd下执行ctrl+c 才会触发信息发生。 OperateSignal> Signal sig = new Signal("SEGV");//SEGV 这个linux和window不同 Signal.handle(sig, operateSignalHandler); public class OperateSignal implements SignalHandler{ @Override public void handle(Signal arg0) { System.out.println("信号接收"); } } 7、要是在线程间也可以使用Semaphore 8、说明一下Java中没有命名管道

    展开全文
  • 当前位置:我的异常网» J2EE»java.io.FileNotFoundException: D:\MyEclipse\apacjava.io.FileNotFoundException: D:\MyEclipse\apache-tomcat-(另一个程序正在使用此文件进程无法访问。)解决思路...
  • 是否可以同时从两个或个不同的进程读取同一个文件?我想在Java应用程序中做这样的事情:final File f = new File("read-only-file");final FileInputStream in = new FileInputStream(f);int b;while((b = in.read...
  • 其中IP地址会自动绑定到程序运行所在机器的IP,端口号是5001,访问方式是post 多进程,多线程部署 通过配置app.run()中的参数实现多进程或者多线程部署,默认是多线程的,即多个客户端同时调用访问restful接口,这个...
  • python Flask logging日志报错“另一个程序正在使用此文件进程无法访问” 2. 代码示例 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author: craftsman import os import datetime import decimal import ...
  • python 多进程同步运行多个函数

    千次阅读 2021-11-12 16:02:06
    场景:一个进程控制吃饭函数,另一个进程控制睡觉函数,同步进行。 结论,运行时间:不用进程 > 函数形式进程 == 类形式进程 目录 1.不用进程 2.函数形式进程multiprocessing.Process() 3.类形式进程...
  • Python的多进程并行计算库与多进程爬虫

    万次阅读 多人点赞 2021-07-14 00:13:40
    本文再继续介绍多进程的使用方法。 相关概念 并发和并行的区别 在 Python 中,并发并不是指同一时刻有多个操作同时进行。相反,某个特定的时刻,它只允许有一个操作发生,只不过线程 / 任务之间会互相切换,直到完成...
  • 简单介绍:nginx 采用的是多进程(单线程) + io多路复用(epoll)模型 实现高并发 二、nginx 多进程 启动nginx 解析初始化配置文件后会 创建(fork)一个master进程 之后 这个进程会退出 master 进程会 变为...
  • 要说线程 我们明确几个概念 1.1 名词 1.1.1 操作系统 操作系统是计算机系统中的一个系统软件,是一些程序模块的集合 它们能以尽量有效、合理的方式组合和管理计算机的软硬资源 系统效率,资源利用率 CPU利用率...
  • 但有的网友的电脑却出现了进程拒绝访问的提示,这该怎么办呢...2018-01-15 14:36:44很时候程序死机后,需要在任务管理器中结束进程才能再次打开,可是有些win7用户在任务管理器结束死机程序的时候弹出了无法中止...
  • Python 线程,文件io

    千次阅读 2021-01-13 20:04:46
    import os #目录操作def writeFile():fo = open("foo.txt", "a+") #打开一个文件,第二个参数为打开的模式:r 只读,r+读写 w只写 w+读写 wb二进制方式只写 a 追加print ("文件名: ", fo.name)print("是否已关闭 : ...
  • 前文尝试了软件来源分析,结合APT攻击中常见的判断方法,...本文将分享Procmon软件基本用法及文件进程、注册表查看,这是一款微软推荐的系统监视工具,功能非常强大可用来检测恶意软件。基础性文章,希望对您有所帮助~
  • 打比方就是,进程是火车,线程是火车厢,车厢内人员可以流动(数据共享)二、python中的多线程和多进程当遇到大文件读写或处理计算时,需要加速,则用上多线程和多进程,最常见的例子是网页爬虫,每次访问后等待时间很...
  • Python Multiprocessing多进程 使用tqdm显示进度条的实现1.背景在python运行一些,计算复杂度比较高的函数时,服务器端单核CPU的情况比较耗时,因此需要多CPU使用多进程加快速度2.函数要求笔者使用的是:pathos....
  • Linux文件访问权限

    2021-05-09 07:08:35
    一.设置用户ID和设置组ID1....但是可以在文件模式模式字中设置一个特殊标志,将进程的有效用户ID设置为文件所有者的用户ID。分别设置用户ID位和设置组ID位即可。方法如下:chmod u+s filename 与 chmo...
  • 万文长篇,深入浅出地从进程讲起,用最简单明了的图片带你串起进程列表、进程控制块、inode节点、文件描述符列表、文件实体、文件系统等知识(深度好文,建议收藏)前言总览一、进程、线程1.1 什么是程序?...
  • 可执行文件的装载与进程 进程虚拟地址空间 程序是一个静态的概念,是一些预先编译好的指令和数据集合的一个文件 进程是一个动态的概念,是程序运行时的一个过程 C语言指针大小的位数与虚拟空间的位数相同 #include&...
  • 纯新多进程学习基础

    2021-11-09 11:06:02
    什么是进程进程的相关函数
  • 4.lsof使用实例一、查找谁在使用文件系统在...那么通过lsof可以找出那些进程在使用当前要卸载的文件系统,如下:# lsof /GTES11/COMMAND PID USER FD TYPE DEVICE SIZE NODE NAMEbash 4208 root cwd DIR 3,1 4096 ...
  • Python:同时运行进程

    千次阅读 2021-01-14 09:59:41
    我试图在python中创建一个程序,它在不同的处理器上同时运行个函数实例(15).我一直在研究这个问题,并使用处理过程工具设置以下程序.不幸的是,程序顺序执行函数的每个实例(它似乎等待一个完成后再移动到循环的下一...
  • 永远不要忘记:跨平台特性...删除了某进程正在使用的文件,则在该进程没有关闭之前,哪怕文件在目录结构中消失了,也仍然能被访问到,直到没有任何进程打开了这个文件(inode节点的使用者数量降为0)后才会真正删除。...
  • 底层文件访问

    千次阅读 2021-11-15 15:14:40
    每个运行中的程序被称为进程( process),它有一些与之关联的文件描述符。这是一些小值整数,你可以通过它们访问打开的文件或设备。有多少文件描述符可用取决于系统的配置情况。当一个程序开始运行时,它一般会有3个...
  • 进程间通信(一)(管道文件操作)

    千次阅读 2021-04-04 10:55:42
    通信原理:多进程只要能够访问同一块内核中的缓冲区(管道)就能实现通信 分类:匿名管道 命名管道 匿名管道:(无标识符,无法被其他进程找到)只能用于具有亲缘关系的进程间通信 无标识符,无法被其他进程找到,...
  • Linux -- 任务机制(任务、进程、线程)介绍

    千次阅读 多人点赞 2021-10-18 17:40:01
    任务处理是指用户可以在同一时间内运行个应用程序,每个正在执行的应用程序被称为一个任务。 Linux就是一个支持任务的操作系统,任务操作系统使用...任务操作系统中通常有3个基本概念:任务、进程、线程。
  • 文件名称startOrStropJar.sh脚本#!/bin/bash#这里可替换为你自己的执行程序,其他代码无需更改APP_NAME=xxxxxx.jar#使用说明,用来提示输入参数usage() {echo "Usage: sh startOrStropJar.sh [start|stop|restart|...
  • fork一个进程后,复制出来的task_struct结构与系统的堆栈空间是父进程独立的,但其他资源却是与父进程共享的,比如文件指针,socket描述符等不同的进程使用不同的地址空间,子进程被创建后,父进程的全局变量,静态...
  • 本文笔者在研究某著名软件的安全性时,碰到要修改进程中只读数据段(.rdata)中的常量 值的场景。笔者将这方的方法技巧及相关的经验向广大读者朋友进行分享,同时由于笔者技术水平有限,本文中若有错误恳请不吝赐教...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 731,798
精华内容 292,719
关键字:

多进程访问文件