Linux系统编程

文章目录

Linux系统编程

视频观看于 哔哩哔哩 linux系统编程(李慧琴)

书籍 - 《 Linux系统编程》—哈工大计算机学院IBM俱乐部

一、文件IO

1.系统IO

1.1 文件描述符

是一个整型,其实是一个数组的下标,这个数组保存文件的地址。

1.2 open,close

1.基本语法

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
flags : 
O_RDONLY ,O_WRONLY, O_RDWR,O_CREAT,O_TRUNC,O_APPEND
*/
int open(const char* pathname, int flags);

/*
flags要含有O_CREAT,必须使用下面这个函数
mode : 指定创建的文件权限
*/
int open(const char* pathname, int flags, mode_t mode);

失败返回-1,成功返回新的文件描述符。

int close(int fd);
1.3 read,write
#include <unistd.h>
/*
fd : 文件描述符
buf : 写到/提取元素的位置
count : 读取/写入数量
*/
ssize_t read(int fd, void* buf,size_t count);
ssize_t write(int fd, const void* buf,size_t count);

成功返回读取数目,失败返回-1

1.4 lseek
#include <sys/types.h>
#include <unistd.h>

/*
whence,它能拥有下列值之一: SEEK_SET(首) 、 SEEK_CUR 、 SEEK_END 
*/
off_t lseek(int fd, off_t offset, int whence);
1.5 fileno()–(标准=>系统IO)
int fileno(FILE* fp);
1.6 fdopen()–(系统IO=>标准IO)
FILE* fdopen(int fd, const char* mode);
1.7 dup --(复制一个文件描述符)
#include <unistd.h>

int dup(int oldFd);//返回一个新的文件描述符。

//把old复制给new
int dup2(int oldFd, int newFd);//原子操作
1.8 fcntl,iocntl
#include <unistd.h>
#include <fcntl.h>

//管理文件描述符
int fcntl(int fd,int cmd,参数...);
1.9 截断文件

改变文件的大小,把文件截断到指定的大小。可以缩小文件,也可以放大文件。

成功返回0,失败返回-1。

#include <unistd.h>
#include <sys/types.h>

int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);

2.标准IO

2.1 fopen() --(打开文件)

1.文件打开方式

名称含义
r只读形式
r+读写形式打开
w存在就清空,不存在就创建
w+读写形式打开,有清空,无创建
a追加+写
a+追加+读+写
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cerrno>

using namespace std;

int main()
{
    FILE* fp = nullptr;
    fp = fopen("temp","r");

    if(fp == NULL)
    {
        // cerr << "fopen() failed! errno = " << errno << endl;
        perror("fopen()"); //输出错误信息
        
        fclose(fp);//关闭文件
        return 1;
    }

    return 0;
}

不更改默认环境的情况下,文件最多打开1024个文件,实际你能打开1021个,因为标准输入,标准输出,错误输出,这三个已经占了3个位置。

2.2 fgetc() --(读一个字符)

1.基本语法

int fgetc(FILE* stream);

成功时为作为 unsigned char 获得并转换为 int 的字符,失败时为 EOF

2.案例 实现copy文件功能

#include <cstdio>
#include <cstdlib>
#include <cerrno>


int main(int argc, char** argv)
{
    if(argc <3)
    {
        fprintf(stderr,"Usage:%s <src_file> <dest_file> \n",argv[1]);
        return 1;
    }

    FILE* ifp = NULL;
    FILE* ofp = NULL;

    ifp = fopen(argv[1], "r");
    if(ifp == NULL)
    {
        perror("fopen()");
        fclose(ifp);
        return 1;
    }

    ofp = fopen(argv[2], "w");

    int ch(-1);

    while(true)
    {
        ch = fgetc(ifp);

        if(ch == EOF)
        {
            break;
        }

        fputc(ch, ofp);

    }

    fclose(ofp);
    fclose(ifp);

    return 0;
}
2.3 fgets() --(读字符串)

1.基本语法

/*
str - 指向 char 数组元素的指针 
count - 写入的最大字符数(典型的为 str 的长度) 
stream - 读取数据来源的文件流 
*/
char *fgets( char *restrict str, int count, FILE *restrict stream );

char *fputs( char *restrict str, FILE *restrict stream );

从给定文件流读取最多 count - 1 个字符并将它们存储于 str 所指向的字符数组。若文件尾出现或发现换行符则终止分析,后一情况下 str 将包含一个换行符。若读入字节且无错误发生,则紧随写入到 str 的最后一个字符后写入空字符。

2.案例 实现copy文件功能

#include <cstdio>
#include <cstdlib>
#include <cerrno>

const int MYBUFFSIZE = 1024;

int main(int argc, char** argv)
{
    if(argc <3)
    {
        fprintf(stderr,"Usage:%s <src_file> <dest_file> \n",argv[1]);
        return 1;
    }

    FILE* ifp = NULL;
    FILE* ofp = NULL;
    char buff[MYBUFFSIZE];

    ifp = fopen(argv[1], "r");
    if(ifp == NULL)
    {
        perror("fopen()");
        fclose(ifp);
        return 1;
    }

    ofp = fopen(argv[2], "w");

    while(fgets(buff, MYBUFFSIZE, ifp) != NULL)
    {
        fputs(buff, ofp);

    }

    fclose(ofp);
    fclose(ifp);

    return 0;
}
2.4 fread() --(读二进制流)

1.基本语法

/*
buffer - 指向要读取的数组中首个对象的指针 
size - 每个对象的字节大小 
count - 要读取的对象数 
stream - 读取来源的输入文件流 
*/
size_t fread( void *restrict buffer, size_t size, size_t count,FILE *restrict stream );

/*
buffer - 指向数组中要被写入的首个对象的指针 
size - 每个对象的大小 
count - 要被写入的对象数 
stream - 指向输出流的指针 
*/
size_t fwrite( const void *restrict buffer, size_t size, size_t count,FILE *restrict stream );

成功返回写入的字数,

2.5 fseek()–(文件位置指针)

1.基本语法

/*
stream - 要修改的文件流 
offset - 相对 origin 迁移的字符数 
origin - offset 所加上的位置。它能拥有下列值之一: SEEK_SET(首) 、 SEEK_CUR 、 SEEK_END 
*/

int fseek( FILE *stream, long offset, int origin );

成功时为 0 ,否则为非零。

2.6 ftell()–(当前文件指针位置)

1.基本语法

long ftell( FILE *stream );

返回流 stream 的文件位置指示器。成功时为文件位置指示器,若失败发生则为 -1L

2.案例:计算文件大小

#include <cstdio>
#include <cstdlib>
#include <cerrno>

int main(int argc,char** argv)
{
    if(argc < 2)
    {
       fprintf(stderr,"Usage:%s <src_file> \n",argv[1]);
        return 1;
    }

    FILE* fp = NULL;
    fp = fopen(argv[1],"r");
    if(fp == NULL)
    {
        perror("fopen()");
        return 1;
    }

    long count = 0;
    fseek(fp,0,SEEK_END);
    count = ftell(fp);
    fclose(fp);
    printf("fileSize = %ld \n",count);

    return 0;

}

2.7 rewind()
void rewind( FILE *stream );

移动文件位置指示器到给定文件流的起始。

2.8 fflush() --(刷新缓冲区)
void fflush( FILE *stream );
void fflush();//刷新全部stream
2.9 getline() --(获取一行数据)

1.基本语法

/*
lineptr - 指针,指向空指针,或指向指向最初缓冲区的指针 
n - 指向最初缓冲区大小的指针 
delimiter - 分隔字符 
stream - fopen 打开的合法输入流 
*/

ssize_t getline(char **lineptr, size_t *n, FILE *stream);
ssize_t getdelim(char ** restrict lineptr, size_t * restrict n,int delimiter, FILE *stream);

2.案例 输出每行元素个数

#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>

int main(int argc, char** argv)
{
    if(argc < 2)
    {
        fprintf(stderr,"Usge <src_file> \n");
        return 1;
    }

    FILE* fp = NULL;
    fp = fopen(argv[1],"r");

    if(fp == NULL)
    {
        perror("fopen()");
        return 1;
    }

    size_t buffSize(0);
    char* buff = NULL;

    while (true)
    {
        if(getline(&buff,&buffSize,fp) < 0) break;
        printf("%d\n",strlen(buff));
    }

    fclose(fp);

    return 0;
}

二、文件系统

1.获取文件的属性信息

1.1 stat,fstat,lstat

1.基本语法

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/*
path : 文件路径
buf : 给一个地址,它回填到buf里面
fd :文件描述符
*/

int stat(const char* path, struct stat* buf);
int fstat(int fd, struct stat* buf);
int lstat(const char* path, struct stat* buf);

成功返回0,失败返回-1。

struct stat 
{
  dev_t   st_dev;     /* ID of device containing file */
  ino_t   st_ino;     /* inode number */
  mode_t  st_mode;    /* file type and mode */
  nlink_t   st_nlink; /* number of hard links */
  uid_t     st_uid;   /* user ID of owner */
  gid_t     st_gid;   /* group ID of owner */
  dev_t     st_rdev;  /* device ID (if special file) */
  off_t     st_size;  /* total size, in bytes */
  blksize_t st_blksize;/* blocksize for filesystem I/O */
  blkcnt_t  st_blocks; /* number of 512B blocks allocated */

  struct timespec st_atim;/* time of last access */
  struct timespec st_mtim;/* time of last modification */
  struct timespec st_ctim;/*time of last status change */

  #define st_atime st_atim.tv_sec   /* Backward compatibility */
  #define st_mtime st_mtim.tv_sec
  #define st_ctime st_ctim.tv_sec
};

2.案例 计算文件大小

#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

off_t getFileSize(const char* fileName)
{
    struct stat mystat;

    stat(fileName, &mystat);

    return mystat.st_size;
    
}

int main(int argc, char** argv)
{
    if(argc < 2 || argc > 2)
    {
        fprintf(stderr, "Usage <src_file> \n");
        return 1;
    }

    printf("fileSize = %lld \n",getFileSize(argv[1]));

    return 0;
}

输出效果

/**
-rw-rw-r--. 1 root1 root1  442 Jul 17 23:09 1.stat.cpp
-rwxrwxr-x. 1 root1 root1 8648 Jul 17 23:11 getFileSize
[root1@localhost FileSystem]$ ./getFileSize  1.stat.cpp 
fileSize = 442 
*/

2.文件权限

2.1 chmod,fchmod
#include <sys/stat.h>

/*
更改文件权限
**/
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

3.目录的创建和销毁

3.1 mkdir,rmdir
#include <sys/stat.h>
#include <sys/types.h>

int mkdir(const char *pathname, mode_t mode);

成功返回0,失败返回-1.

 #include <unistd.h>

int rmdir(const char *pathname);

成功返回0,失败返回-1.

4.更改当前工作路径

4.1 chdir,fchdir
#include <unistd.h>

int chdir(const char *path);
int fchdir(int fd);

成功返回0,失败返回-1.

5.获取当前工作路径

5.1 getcwd
 #include <unistd.h>

char *getcwd(char *buf, size_t size);
char *getwd(char *buf);
char *get_current_dir_name(void);

成功返回当前工作路径,失败返回NULL

6.分析目录/读取目录内容

6.1 glob

1.基本语法

#include <glob.h>

/**
第三个参数,保存错误输出的路径
*/
int glob(const char *pattern, int flags,
         int (*errfunc) (const char *epath, int eerrno),
          glob_t *pglob);

void globfree(glob_t *pglob);
 typedef struct 
 {
size_t   gl_pathc;    /* Count of paths matched so far  */
char   **gl_pathv;    /* List of matched pathnames.  */
size_t   gl_offs;     /* Slots to reserve in gl_pathv.  */
 } glob_t;

2.案例 打印目录内容

#include <cstdio>
#include <cstdlib>
#include <glob.h>

const char* PATH = "/home/root1/myCode/C++/LinuxAPI/FileSystem/*";

int main()
{
    glob_t myGlon_t;

   int ret = glob(PATH, 0, NULL, &myGlon_t);

   if(ret)
   {
        perror("glob()");
        globfree(&myGlon_t);
        return 0;
   }

    for(int i = 0; i < myGlon_t.gl_pathc; ++i)
    {
        printf("%s \n", myGlon_t.gl_pathv[i]);
    }

    globfree(&myGlon_t);

    return 0;
}

输出效果

/*
/home/root1/myCode/C++/LinuxAPI/FileSystem/1.stat.cpp 
/home/root1/myCode/C++/LinuxAPI/FileSystem/2.glob 
/home/root1/myCode/C++/LinuxAPI/FileSystem/2.glob.cpp 
/home/root1/myCode/C++/LinuxAPI/FileSystem/getFileSize 
*/

三、系统数据文件和信息

1. 用户信息文件 和函数

保存用户信息的文件为 /etc/passwd

获取用户信息的函数为:

#include <sys/types.h>
#include <pwd.h>

 struct passwd *getpwnam(const char *name);

 struct passwd *getpwuid(uid_t uid);
struct passwd
{
    char   *pw_name;       /* username */
    char   *pw_passwd;     /* user password */
    uid_t   pw_uid;        /* user ID */
    gid_t   pw_gid;        /* group ID */
    char   *pw_gecos;      /* user information */
    char   *pw_dir;        /* home directory */
    char   *pw_shell;      /* shell program */
};

2. 组信息 和函数

保存组信息的文件为 /etc/group

#include <sys/types.h>
#include <grp.h>

struct group *getgrnam(const char *name);

struct group *getgrgid(gid_t gid);
struct group 
{
     char   *gr_name;       /* group name */
     char   *gr_passwd;     /* group password */
     gid_t   gr_gid;        /* group ID */
     char  **gr_mem;        /* group members */
};

3.时间戳

3.1 time()
#include <time.h>

time_t time(time_t *t);

//使用方法一
time_t myTime;

time(&myTime);

//方法二
myTime = time(NULL);

返回值以秒为单位。

3.2 gmtime()
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);

struct tm 
{
   int tm_sec;         /* seconds */
   int tm_min;         /* minutes */
   int tm_hour;        /* hours */
   int tm_mday;        /* day of the month */
   int tm_mon;         /* month */
   int tm_year;        /* year */
   int tm_wday;        /* day of the week */
   int tm_yday;        /* day in the year */
   int tm_isdst;       /* daylight saving time */
};
3.3 localtime()
 struct tm *localtime(const time_t *timep);
 struct tm *localtime_r(const time_t *timep, struct tm *result);
3.4 mktime()
 time_t mktime(struct tm *tm);

mktime可以判断参数tm是否合法,若不合法则会修改参数tm,修改成正常的时间。

3.5 strftime()
#include <time.h>

/*
s - 指向待输出 char 数组首元素的指针 
max - 最大写入字节数 
format - 指向指定转换格式的空终止多字节字符串指针。  

*/
size_t strftime(char *s, size_t max, const char *format,
                const struct tm *tm);
3.6 案例

按照固定格式,把时间写入某文件

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <unistd.h>
 
int main()
{
    //打开文件
    FILE* fp = NULL;

    fp = fopen("/tmp/timeLog", "a+");

    if(fp == NULL)
    {
        perror("open()");
        return 1;
    }
    int row(0);
    time_t curTime;
    struct tm* myTm = NULL;

    while(true)
    {
        //获取时间
        curTime = time(NULL);

        //转化成struct
        myTm = localtime(&curTime);

        //按照特定格式输出时间

        fprintf(fp, "%-4d%d-%d-%d %d:%d:%d \n",++row,
        myTm->tm_year + 1900, myTm->tm_mon +1,
        myTm->tm_mday,
        myTm->tm_hour, myTm->tm_min, myTm->tm_sec);

        fflush(fp);

        sleep(1);
    }
    return 0;
}

四、进程环境

4.1 钩子函数

#include <stdlib.h>

int atexit(void (*function)(void));

类似于C++中的析构函数,当程序遇到==exit()==时,就会调用这个函数。

4.2 命令行参数的分析

1.获取命令行参数的函数如下:

#include <unistd.h>

int getopt(int argc, char * const argv[],
           const char *optstring);
#include <getopt.h>

int getopt_long(int argc, char * const argv[],
                const char *optstring,
                const struct option *longopts, 
                int *longindex);

2.案例

根据命令行输入的格式把时间格式输出

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <unistd.h>
#include <string>

using namespace std;
 
int main(int argc, char** argv)
{
   
   
    time_t curTime;
    struct tm* myTm = NULL;

    int cin(0);
    string str("");
    char buf[70];

    while(true)
    {
        //获取命令行输入的参数
        cin = getopt(argc,argv,"HSMymd");

        if (cin < 0)
        {
           break;
        }

        switch (cin)
        {
        case 'y':
            str += "%Y-";
            break;
        case 'm':
            str += "%m-";
            break;
        case 'd':
            str += "%d ";
            break;
        case 'H':
            str += "%H:";
            break;
        case 'M':
            str += "%M:";
            break;
        case 'S':
            str += "%S";
            break;
        default:
            break;
        }

    }
	//获取时间
    curTime = time(NULL);

     //转化成struct
     myTm = localtime(&curTime);

     //按照特定格式输出时间
     strftime(buf,sizeof buf,str.c_str(),myTm);
     puts(buf);

    return 0;
}

4.3 环境变量

命令行输入export,可以查看环境变量内容。

#include <stdlib.h>
char *getenv( const char *name );

#include <stdlib.h>
//不存在就是添加,存在的话就是修改
int setenv(const char *name, const char *value, 
           int overwrite);
//删除
int unsetenv(const char *name);

//功能和setenv相同,但是没有setenv安全
int putenv(char *string);

4.4 获取资源

#include <sys/time.h>
#include <sys/resource.h>

int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
struct rlimit 
{
   rlim_t rlim_cur;  /* Soft limit */
   rlim_t rlim_max;  /* Hard limit (ceiling for rlim_cur) */
};

五、进程

1.进程标识符 pid

1.1 类型为 pid_t
1.2 命令ps,打印当前进程的信息。
1.3 进程号是顺次向下使用的。
1.4 获得pid的函数如下:
#include <sys/types.h>
#include <unistd.h>

//获得当前进程的pid
pid_t getpid(void);

//获得当前进程的父id
pid_t getppid(void);

2.进程的产生fork

2.1 fork函数如下:
#include <unistd.h>
pid_t fork(void);

成功后父进程被返回子进程的pid,子进程返回0,

失败返回-1.

2.2 fork后父子进程的区别
  • fork的返回值不一样
  • 父子进程的ppid不同
  • 父子进程的pid不同
  • 未决信号和文件锁不继承,资源利用量清0
2.3 案例
#include <unistd.h>
#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
    pid_t pid;

    cout << " begin fork" << endl;

    pid = fork();

    if(pid > 0)//父进程
    {
        cout << " 我是父进程,我的pid = "<< getpid() << " 我父进程的pid = " << getppid() << endl;
        
        sleep(2);
    }
    else if (pid == 0)//子进程
    {
         cout << " 我是子进程,我的pid = "<< getpid() << " 我父进程的pid = " << getppid() << endl;
    }
    else //fork失败
    {
        perror("fork() failure");
        return 0;
    }
    return 0;
}

输出效果:

/**
 begin fork
 我是父进程,我的pid = 10700 我父进程的pid = 6548
 我是子进程,我的pid = 10701 我父进程的pid = 10700
*/

3.进程的消亡及释放资源

3.1 wait(),waitpid()
#include <sys/types.h>
#include <sys/wait.h>

//status传入一个int地址即可。

pid_t wait(int *status);
//成功,返回pid,失败返回-1

pid_t waitpid(pid_t pid, int *status, int options);
//失败返回-1,

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, 			   int options);

4.exec函数族

4.1 基本语法
#include <unistd.h>
extern char **environ;

//以 NULL结束参数输入
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
           ..., char * const envp[]);

int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
            char *const envp[]);
4.2 案例

输出目前时间

#include <unistd.h>
#include <cstdio>
#include <iostream>
using namespace std;

int main()
{
    cout << "begin" << endl;

    execl("/bin/date","date",NULL);

    cout << "end" << endl;


    return 0;
}

运行结果:

/**
begin
2024年 07月 19日 星期五 20:57:38 CST
*/
4.3 和fork搭配使用
#include <unistd.h>
#include <cstdio>
#include <iostream>
#include <stdlib.h>
#include <wait.h>
using namespace std;
int main()
{
    pid_t pid;

    cout << " begin fork" << endl;

    pid = fork();

    if(pid > 0)//父进程
    {
        cout << " 我是父进程,我的pid = "<< getpid() << " 我父进程的pid = " << getppid() << endl;
        wait(NULL);

        cout << "end" << endl;
    }
    else if (pid == 0)//子进程
    {
        
        cout << " 我是子进程,我的pid = "<< getpid() << " 我父进程的pid = " << getppid() << endl;
        execl("/bin/date","date",NULL);
    }
    else //fork失败
    {
        perror("fork() failure");
        return 0;
    }

    return 0;
}

运行结果如下:

/**
begin fork
 我是父进程,我的pid = 15201 我父进程的pid = 6548
 我是子进程,我的pid = 15202 我父进程的pid = 15201
2024年 07月 19日 星期五 21:09:07 CST
end
*/

5.用户权限及组权限

5.1 获得用户权限和组权限
#include <unistd.h>
#include <sys/types.h>

uid_t getuid(void);
uid_t geteuid(void);

/**
getuid() returns the real user ID of the calling process.

geteuid() returns the effective user ID of the calling process.
*/
#include <unistd.h>
#include <sys/types.h>

gid_t getgid(void);
gid_t getegid(void);
/**
getgid() returns the real group ID of the calling process.

getegid() returns the effective group ID of the calling process.
*/
5.2 设置用户权限和组权限
#include <sys/types.h>
#include <unistd.h>

int setuid(uid_t uid);

#include <sys/types.h>
#include <unistd.h>

int setgid(gid_t gid);
#include <sys/types.h>
#include <unistd.h>

//交换2个id,原子性操作
int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);
#include <sys/types.h>
#include <unistd.h>

int seteuid(uid_t euid);
int setegid(gid_t egid);

6.system()

6.1 基本语法
/**
execute a shell command
**/

#include <stdlib.h>

int system(const char *command);
6.2 案例
#include <iostream>
#include <cstdlib>
using namespace std;

int main()
{
    system("date");
    return 0;
}

运行结果如下:

/**
2024年 07月 19日 星期五 22:52:14 CST
*/

7.进程会计

7.1 acct()
#include <unistd.h>

int acct(const char *filename);

8.进程时间

8.1 times()
#include <sys/times.h>

/* get process and waited-for child process times */
clock_t times(struct tms *buffer);


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

9.守护进程

守护进程是指一直在后台运行的模块。

9.1 创建守护进程函数

该函数只能子进程调用,让子进程创建新的session(会话)。

#include <unistd.h>

//creates a session and sets the process group ID
pid_t setsid(void);
/*
 setsid() creates a new session if the calling process is not a process group leader.  The calling process is the
leader of the new session, the process group leader of the new process group, and has no  controlling  terminal.

The process group ID and session ID of the calling process are set to the PID of the calling process.  

The calling process will be the only process in this new process group and in this new session.

*/
9.2获取进程组的id

以下函数都可以获得id

int setpgid(pid_t pid, pid_t pgid);
pid_t getpgid(pid_t pid);

pid_t getpgrp(void);                 /* POSIX.1 version */
pid_t getpgrp(pid_t pid);            /* BSD version */
9.3 案例

成为守护进程,每秒向一个文件内写入数字。

#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

const char* FILENAME = "/home/root1/myCode/a/out";

int creatProtectProcess()
{
    pid_t pid;

    pid = fork();

    if(pid <0) //失败
    {
        perror("fork()");
        return -1;
    }
    else if (pid > 0)//父进程
    {
        return -1;
    }
    else //子进程
    {
        int fd = open("/dev/null",O_RDWR); 

        if(fd < 0)
        {
            perror("open()");
            return -1;
        }
        //把3个标准切换掉
        dup2(fd,0);
        dup2(fd,1);
        dup2(fd,2);

        if(fd > 2)
           close(fd);
        
        //成为守护进程
        setsid();

        //资源管理
        chdir("/");

        return 0;

    }
    
}

int main()
{

    if(creatProtectProcess())
    {
        return 0;
    }
    
    FILE* fp = NULL;
    fp = fopen(FILENAME,"w");

    if(fp < 0)
    {
        perror("fopen()");
        return 0;
    }

    for(int i=0; ; ++i)
    {
        fprintf(fp,"%d\n",i);
        fflush(fp);
        sleep(1);
    }

    return 0;
}

10.系统日志

10.1 基本语法

把系统日志交给syslogd,让它去写系统日志。

#include <syslog.h>

void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
/*
   option
The option argument to openlog() is an OR of any of these:

LOG_CONS       Write directly to system console if there is an error while sending to system logger.

 LOG_NDELAY     Open the connection immediately (normally, the connection is opened when  the  first  message  is
                      logged).

LOG_NOWAIT     Don't  wait for child processes that may have been created while logging the message.  (The GNU C
                      library does not create a child process, so this option has no effect on Linux.)

LOG_ODELAY     The converse of LOG_NDELAY; opening of the connection is delayed until syslog() is called.  (This
                      is the default, and need not be specified.)

LOG_PERROR     (Not in POSIX.1-2001 or POSIX.1-2008.)  Print to stderr as well.

LOG_PID        Include PID with each message.
*/

具体查man手册

10.2 案例
#include <syslog.h>

openlog("这个名字随便起",LOG_PID,LOG_AUTH);

if(true)
{
	syslog(LOG_INF,"success");
}
else
{
	syslog(LOG_ERR, "erro");
}

closelog();

六、信号

1.信号的概念

信号是软件中断。

2.signal()

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

信号会打断一个堵塞的系统调用

2.2 案例
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <csignal>

void int_handler(int s)
{
    write(1,"!",1);
}

int main()
{
    // 打断程序,输入一个!
    signal(SIGINT, int_handler);

    for (int i = 0; i < 10; ++i)
    {
        write(1,"*",1);
        sleep(1);
    }
    return 0;
}

运行效果:

/*
******^C!**^C!**^C!
*/

3.信号的不可靠

是指信号的行为不可靠。第一次调用未结束,第二次调用就开始了。导致了第一次调用产生了未定义行为(产生的效果是,消失)。

4.可重入函数

为了解决信号的不可靠。

  • 所有的系统调用都是可重入的。
  • 一部分库函数也是可重入的。
  • 函数模型 xxx_r(),

5.信号的响应过程

  • 标准信号从收到到响应有一个不可避免的延迟。
  • 标准信号的响应没有严格的顺序。

6.常用函数

6.1 kill()

给一个进程发信号

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);
pid描述
> 0给pid = pid的进程发信号
= 0给进程组内全部进程发信号
= -1给系统内全部进程发(自己拥有权限),除了init进程
< -1给组内pid = |pid|的进程发信号
6.2 raise()

给当前进程发送一个信号

#include <signal.h>

int raise(int sig);
6.3 alarm()

当seconds秒结束后,给当前进程发送一个SIGALM信号。

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
6.4 pause()

等待一个信号的到来。

#include <unistd.h>

int pause(void);
6.5 abort()

人为的制造异常,杀掉进程,得到一个现场报告。

#include <stdlib.h>

void abort(void);
6.6 system()
6.7 sleep()
#include <unistd.h>
unsigned int sleep(unsigned int seconds);

有些平台使用alarm + pasue 封装实现,一个程序出现多个alarm,会产生未定义情况。谨慎使用。

sleep 可替换函数

#include <time.h>

int nanosleep(const struct timespec *req, struct timespec 				*rem);

7.信号集

#include <signal.h>

//把某个信号集清空
int sigemptyset(sigset_t *set);

//填充某个信号集
int sigfillset(sigset_t *set);

//向信号集中添加信号
int sigaddset(sigset_t *set, int signum);

//删除信号集中某信号
int sigdelset(sigset_t *set, int signum);

//判断是否是某信号集中的一个
int sigismember(const sigset_t *set, int signum);

8.信号屏蔽字和pending集的处理

8.1 基本语法
#include <signal.h>

int sigprocmask(int how, const sigset_t *set, 
                sigset_t *oldset);

how 选项

/*  
SIG_BLOCK
The set of blocked signals is the union of the current set and the set argument.

SIG_UNBLOCK
The signals in set are removed from the current set of blocked signals.  It is permissible to attempt  to
unblock a signal which is not blocked.

SIG_SETMASK
The set of blocked signals is set to the argument set.
          
*/

8.2 案例
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <csignal>

void int_handler(int s)
{
    write(1,"!",1);
}

int main()
{
    // 打断程序,输入一个!
    signal(SIGINT, int_handler);
    
    sigset_t set;

    // 清空信号集
    sigemptyset(&set);
    //把信号加入信号集
    sigaddset(&set, SIGINT); 

    for (int i = 0; i < 100; ++i)
    {
        //屏蔽信号
        sigprocmask(SIG_BLOCK, &set, NULL);
        for(int j = 0; j < 5; ++j)
        {
            write(1,"*",1);
            sleep(1);
        }

        write(1,"\n",1);
        // 开启屏蔽的信号
        sigprocmask(SIG_UNBLOCK, &set, NULL);
        
    }
    
    return 0;
}
8.3 sigpending()
#include <signal.h>

int sigpending(sigset_t *set);

9.扩展

9.1 setitimer()

get or set value of an interval timer

#include <sys/time.h>

int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval 					 *new_value, struct itimerval *old_value);

which 选项

/*
ITIMER_REAL    decrements in real time, and delivers SIGALRM upon expiration.

ITIMER_VIRTUAL decrements only when the process is executing, and delivers SIGVTALRM upon expiration.

ITIMER_PROF    decrements both when the process executes and when the system  is  executing  on  behalf  of  the
process.   Coupled  with  ITIMER_VIRTUAL, this timer is usually used to profile the time spent by the application in user and kernel space.  SIGPROF is delivered upon expiration.
*/
struct itimerval 
{
        struct timeval it_interval; /* next value */
        struct timeval it_value;    /* current value */
};

struct timeval 
{
       time_t      tv_sec;         /* seconds */
       suseconds_t tv_usec;        /* microseconds */
};

建议用setitimer替换alarm,它精度更好。

9.2 sigsuspend()

等待一个信号。

#include <signal.h>

int sigsuspend(const sigset_t *mask);

比pause更好,信号驱动程序。

9.3 sigaction()
#include <signal.h>

int sigaction(int signum, const struct sigaction *act,
              struct sigaction *oldact);
struct sigaction 
{
   //使用时,二者选其一
   void     (*sa_handler)(int);
   void     (*sa_sigaction)(int, siginfo_t *, void *);
    
   sigset_t   sa_mask;
   int        sa_flags;
   void     (*sa_restorer)(void);
};

应用:

  • 多个信号共用同一个信号函数。

10、实时信号

实时信号和标准信号同时到来时,先响应标准信号。

七、线程

1.线程的背景

1.1 线程标识符

线程标识符为 pthread_t

1.2 pthread_equal()

判断两个进程是否相等

#include <pthread.h>

int pthread_equal(pthread_t t1, pthread_t t2);

//Compile and link with -pthread.

成功返回非0,失败返回0.

1.3 pthread_self()

获得当前进程id

#include <pthread.h>

pthread_t pthread_self(void);

//Compile and link with -pthread.

2.线程的创建

2.1 基本语法
#include <pthread.h>
/*
pthread_t* 传入一个该类型的地址
pthread_attr_t 设置线程的属性
第三个参数:传一个函数,返回类型为void*,参数为void*,
第四个参数:为函数传入参数
*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

//Compile and link with -pthread.

成功返回0。

2.2 案例
#include <cstdio>
#include <cstdlib>
#include <pthread.h>

void* func(void*)
{
    puts("thread is working");
    return 0;
}
int main()
{

    puts("begin");
    pthread_t tid;
    // 创建线程
  	int ret = pthread_create(&tid,NULL,func,NULL);
    if(ret)
    {
        fprintf(stderr, "create false");
        return 0;
    }
    puts("end");

    return 0;
}

运行效果:

/*
begin
end
*/
2.3 线程属性
#include <pthread.h>

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);

3.线程的终止

3.1 终止的三种方式
  1. 线程从启动例程返回,返回值为线程的退出码。
  2. 线程可以被同一进程中的其他线程取消。
  3. 线程调用pthread_exit()函数。
#include <pthread.h>

void pthread_exit(void *retval);
#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

相当于进程中的wait()函数。

3.2 案例
#include <cstdio>
#include <cstdlib>
#include <pthread.h>

void* func(void*)
{
    puts("thread is working");
    // return 0;
    pthread_exit(NULL); //结束该线程
}
int main()
{
    puts("begin");
    pthread_t tid;
    // 创建线程
   int ret = pthread_create(&tid,NULL,func,NULL);
    if(ret)
    {
        fprintf(stderr, "create false");
        return 0;
    }

    // 进行进程收尸
    pthread_join(tid,NULL);
    puts("end");

    return 0;
}

运行效果:

/*
begin
thread is working
end
*/

4.栈的清理

4.1 基本语法
#include <pthread.h>

void pthread_cleanup_push(void (*routine)(void *),
                          void *arg);

// execute = 1 //弹栈并调用
// execute = 0 //弹栈不调用
void pthread_cleanup_pop(int execute);

这两个函数,必须成对出现。

4.2 案例
#include <cstdio>
#include <cstdlib>
#include <pthread.h>

void print(void* str)
{
    puts((char*)str);
}

void* func(void*)
{
    puts("thread is working");
    // return 0;
    pthread_cleanup_push(print,(void*)"thread 1:");
    pthread_cleanup_push(print,(void*)"thread 2:");
    pthread_cleanup_push(print,(void*)"thread 3:");

    puts("thread is over");

    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);

    pthread_exit(NULL); //结束该线程
}

int main()
{
    puts("begin");
    pthread_t tid;
    // 创建线程
   int ret = pthread_create(&tid,NULL,func,NULL);
    if(ret)
    {
        fprintf(stderr, "create false");
        return 0;
    }

    // 进行进程收尸
    pthread_join(tid,NULL);
    puts("end");

    return 0;
}

运行效果:

/*
begin
thread is working
thread is over
thread 3:
thread 2:
thread 1:
end
*/

5.线程的取消

5.1 基本语法
#include <pthread.h>

int pthread_cancel(pthread_t thread);

取消的两种状态

  • 允许

    • 异步cancel
    • 推迟cancel(默认)

    推迟到cancel点再响应。

    cancel点:posix定义的cancel点,都是可能引发阻塞的系统调用。

    #include <pthread.h>
    
    //设置是否允许取消
    int pthread_setcancelstate(int state, 
                              int *oldstate);
    //设置取消方式
    int pthread_setcanceltype(int type, int *oldtype);
    
  • 不允许

5.2 线程分离
#include <pthread.h>

int pthread_detach(pthread_t thread);

6.线程同步

6.1 互斥量
#include <pthread.h>

pthread_mutex_t;

int pthread_mutex_destroy(pthread_mutex_t *mutex);

//第二个参数为锁的属性,可以为NULL
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
              const pthread_mutexattr_t *restrict attr);
#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
互斥量属性
#include <pthread.h>

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
6.2 案例

三个线程,输出abc样式

#include <cstdio>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t mymutex[3];

int netx(int n)
{
    if (n + 1 == 3)
        return 0;
    else
        return n + 1;
        
}

void* print(void* p)
{
    
    int n = (long)p;
    int ch = 'a' + n;
   
    while(true)
    {
        //上锁
        pthread_mutex_lock(mymutex + n);

        write(1,&ch,1);

        //解锁
        pthread_mutex_unlock(mymutex + netx(n));
    }

    pthread_exit(NULL);

}

int main()
{


    pthread_t tid[3];
    int ret(0);
    
    for(int i = 0; i < 3; ++i)
    {
        //初始化锁
        pthread_mutex_init(mymutex + i, NULL);
        
        //上锁
        pthread_mutex_lock(mymutex + i);
        
        
        //创建线程
       ret = pthread_create(tid + i,NULL,print,(void*)i);

       if(ret)
       {
            fprintf(stderr, "create false");
            return 0;
       }

    }

    //解开一个锁
    pthread_mutex_unlock(mymutex);
    sleep(2); //让线程能够启动

    alarm(3);

    //给三个线程收尸
    for(int i = 0; i < 3; ++i)
    {
        pthread_mutex_destroy(mymutex + i);
        pthread_join(tid[i],NULL);
    }

    return 0;
}
6.3.条件变量
#include <pthread.h>

//类型为
pthread_cond_t

int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr); 
      

int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);


int pthread_cond_timedwait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex,
              const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);
条件变量属性
#include <pthread.h>

int pthread_condattr_destroy(pthread_condattr_t *attr);
int pthread_condattr_init(pthread_condattr_t *attr);

八、高级IO

1.非堵塞IO

同步:调用完成后才返回。

异步:调用可能还没完成,就已经返回了。

2.IO多路转接

2.1 概念

I/O多路复用允许应用在多个文件描述符上同时阻塞,并在其中某个可以读写时收到通知。

I/O多路复用遵循的原则:

  1. I/O多路复用:当任何文件描述符准备好IO时告诉我。
  2. 在一个或更多文件描述符就绪前始终处于睡眠状态
  3. 唤醒:哪个准备好了?
  4. 在不阻塞的情况下处理所有IO就绪的文件描述符。
  5. 返回第一步,重新开始。
2.2 select()

2.2.1 概念

在指定的文件描述符准备好I/O之前或超过一定的时间限制,select()调用就会阻塞。

监测的文件描述符可以分为三类,分别等待不同的事件。监测readfds 集合中的文件描述符,确认其中是否有可读数据(也就是说,读操作可以无阻塞的完成)。监测 writefds 集合中的文件描述符,确认其中是否有一个写操作可以不阻塞地完成。监测 exceptefds中的文件描述符,确认其中是否有出现异常发生或者出现带外(out-of-band)数据(这种情况只适用于套接字)。指定的集合可能为空 (NULL),相应的, select()则不对此类时间进行监视。

成功返回时,每个集合只包含对应类型的IO 就绪的文件描述符。

版本比较古老,可移植性比较好。

/* According to POSIX.1-2001 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

/*
nfds : 三个集合中最大的文件描述符 +1
timeval:超时设置
*/
int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);//删掉某个描述符
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);//把某个描述符放入某集合
void FD_ZERO(fd_set *set);//清空描述符集合
struct timeval 
{
    long    tv_sec;         /* seconds */
    long    tv_usec;        /* microseconds */        
};
2.2 案例
//先布置监视任务
rfdset,wfdset,exceptfds
FD_SET(rfdset);
//监视
int ret = select()
//查看监视结果
if(ret < 0)
{
}
.....
2.3 poll()
#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd 
{ 
    int   fd;         /* file descriptor */   
    short events;     /* requested events */
    short revents;    /* returned events */
};
2.4 案例
2.5 epoll()

linux的方言,不可以移植。

#include <sys/epoll.h>
/*
创建一个epoll描述符。
size 可以随便填,给个正数即可
*/
int epoll_create(int size);
int epoll_create1(int flags);

epoll_ctl()可以向指定的 epoll 上下文中加入或删除文件描述符:

#include <sys/epoll.h>
/* 管理一个epoll描述符 */
int epoll_ctl(int epfd, int op, int fd, 
              struct epoll_event *event);

op 的选项

名称描述
EPOLL_CTL_ADD把fd指定的文件添加到epfd指定的epoll实例监听集中
EPOLL_CTL_DEL把fd指定的文件从epfd指定的epoll监听集中删掉
EPOLL_CTL_MOD使用event改变在已有fd上的监听行为
typedef union epoll_data
{
      void        *ptr;
      int          fd;
      uint32_t     u32;
      uint64_t     u64;
} epoll_data_t;

struct epoll_event 
{ 
   uint32_t     events;      /* Epoll events */
   epoll_data_t data;        /* User data variable */
};

events参数

名称描述
EPOLLERR文件出错。即使没有设置,这个事件也是被监听的
EPOLLET在监听文件上开启边沿触发,默认行为是水平触发
EPOLLHUP文件被挂起,即使没有设置,这个事件也是被监听的
EPOLLIN文件未阻塞,可读
EPOLLONESHOT在一次事件产生并处理后,文件不再被监听。(必须指定新事件)
EPOLLOUT文件未阻塞,可写
EPOLLPRI高优先级的带外数据可读
#include <sys/epoll.h>

/* wait for an I/O event on an epoll file descriptor */
int epoll_wait(int epfd, struct epoll_event *events,
               int maxevents, int timeout);

如果 timeout 为0,即使没有事件发生,调用也立即返回,此时调用返回0。如果 timeout 为 -1,调用将一直等待到有事件发生。
当调用返回,epoll_event 结构体中的 events 字段描述了发生的事件。 data字段包含了所有用户在调用 epoll_ctl()前的设置。

2.6 案例
/* 在epfd实例中加入一个fd指定的监听文件*/

struct epoll_event event;
event.data.fd = fd;

event.events = EPOLLIN | EPOLLOUT;
int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
if(ret)
    perror("epoll_ctl()");

3.向量IO

散布聚集 IO 是一种可以在单次系统调用中操作多个缓冲区的IO方法,可以将单个数据流的内容写到多个缓冲区,或者把单个数据流读到多个缓冲区。读取空间不是连续的数据。

#include <sys/uio.h>

ssize_t readv(int fd, const struct iovec *iov, 
              int iovcnt);

ssize_t writev(int fd, const struct iovec *iov, 
               int iovcnt);
3.2 案例
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstring>
#include <sys/uio.h>
#include <unistd.h>

int main()
{
    // 设置三个段
    struct  iovec iov[3];
    char* buf[] ={"Good Morning Teacher \n",
                  "Good Morning Students \n",
                  "please sit down! \n"};
    int fd(-1);
    // 打开文件
    fd = open("/tmp/out",O_WRONLY | O_CREAT | O_TRUNC);
    if(fd < 0)
    {
        perror("open()");
        return 1;
    }

    // 设置段
    for(int i = 0; i < 3; ++i)
    {
        iov[i].iov_base = buf[i];
        iov[i].iov_len = strlen(buf[i] + 1);
    }

    // 调用向量IO,写入信息
    ssize_t ret;
    ret = writev(fd,iov,3);
    if(ret < 0)
    {
        perror("writev()");
        close(fd);
        return 1;
    } 

    // 输出写入了多少字节
    printf("write %d B \n",ret);

    close(fd);

    return 0;
}

运行效果

/*
write 60 B 
[root1@localhost heighIO]$ cat /tmp/out
Good Morning Teacher Good Morning Students please sit down!
*/

4.存储映射IO

4.1 概念

除了标准文件 IO,内核提供了另一种 1/0 方式,允许应用程序将文件映射到内存中,即内存和文件中数据是一一对应的。程序员可以直接通过内存来访问文件,就像操作内存的数据块一样,甚至可以写入内存数据区,然后通过透明的映射机制将文件写入磁盘。

4.2 mmap()

mmap()调用请求内核将 fd 表示的文件中从 ofset 处开始的 len 个字节数据映射到内存中。如果包含了 addr,表明优先使用 addr 为内存中的开始地址。访存权限由 prot 指定, fags 指定了其他的操作行为。

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);
//释放
int munmap(void *addr, size_t length);

addr 参数告诉内核映射文件的最佳地址。这仅仅是提示,而不是强制,大部分用户传递0。调用返回内存映射区域的开始地址。
prot 参数描述了对内存区域所请求的访问权限。如果是PROTNONE,此时映射区域无法访问(没有意义),也可以是以下标志位的比特位的或运算值:

名称描述
PROT_READ页面可读
PROT_WRITE页面可写
PORT_EXEC页面可执行

要求的访存权限不能和打开文件的访问模式冲突。举例来说,如果程序以只读方式打开文件,prot 不能设置为 PROT_WRITE。

flag参数

名称描述
MAP_FIXED告诉mmap()把addr看做强制性要求,而不是建议
MAP_PRIVATE映射区不共享。文件映射采用了写时拷贝,进程对内存的任何改变不影响真正的文件或者其它进程的映射。
MAP_SHARED和所有其它映射该文件的进程共享映射内存

调用成功,mmap()返回映射区的地址。失败时,返回MAP_FAILED,并设置相应的 errno。 mmap()从不返回 0。

可用于父子进程之间通信。

5.文件锁

方式一:

#include <unistd.h>

int lockf(int fd, int cmd, off_t len);

方式二:

#include <sys/file.h>

int flock(int fd, int operation);

方式三:

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fildes, int cmd, ...);

九、进程间通信

1.管道

由内核提供,采用单工通信。自同步机制。

1.1匿名管道
#include <unistd.h>

int pipe(int pipefd[2]);

pipefd[0]为读端,pipefd[1],为写端。

可以用在具有亲缘关系的进程之间通信。

案例

父写子读

#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <string.h>
#include <wait.h>

const int BUF_SIZE = 100;

int main()
{
    int pipe_fd[2];

    if(pipe(pipe_fd) < 0)
    {
        perror("pipe()");
        return 1;
    }

    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork()");
        return 1;
    }
    else if(pid == 0) //read
    {
        close(pipe_fd[1]);
        char buf[BUF_SIZE];
        int str_len = read(pipe_fd[0], buf, BUF_SIZE -1);
        if(str_len < 0)
        {
            perror("read()");
            return 1;
        }
        buf[str_len] = 0;
        write(1,buf,str_len);
        puts("\n");

        close(pipe_fd[0]);
        return 0;
    }
    else //write
    {
        close(pipe_fd[0]);
        char message[] = "hi,child !";
        write(pipe_fd[1], message, strlen(message));
        close(pipe_fd[1]);

        wait(NULL);
        return 0;

    }

    return 0;
}
1.2 命名管道
#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

2.消息队列

key_t ftok(const char* pathname, int proj_id);

产生一个独一无二的key_t类型的值。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

/*得到一个消息队列*/
int msgget(key_t key, int msgflg);

/* /* 把一条消息添加到消息队列中*/*/
int msgsnd(int msqid, const void *msgp, size_t msgsz, 
           int msgflg);

/* 从消息队列中获取消息*/
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, 
               long msgtyp,int msgflg);
/* 管理一个消息*/
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

struct msqid_ds 
{
    struct ipc_perm msg_perm;    
    /* Ownership and permissions */
    time_t          msg_stime;    
    /* Time of last msgsnd(2) */
    time_t          msg_rtime;    
    /* Time of last msgrcv(2) */
    time_t          msg_ctime;    
    /* Time of last change */
    unsigned long   __msg_cbytes; 
    /* Current number of bytes in queue (nonstandard) */
    msgqnum_t       msg_qnum;     
    /* Current number of messages in queue */
   msglen_t        msg_qbytes;   
    /* Maximum number of bytes allowed in queue */
    pid_t           msg_lspid;    
    /* PID of last msgsnd(2) */
    pid_t           msg_lrpid;    
    /* PID of last msgrcv(2) */
};

 struct ipc_perm 
 {
   key_t     __key;       /* Key supplied to msgget(2) */
   uid_t     uid;         /* Effective UID of owner */
   gid_t     gid;         /* Effective GID of owner */
   uid_t     cuid;        /* Effective UID of creator */
   gid_t     cgid;        /* Effective GID of creator */
   unsigned short mode;        /* Permissions */
   unsigned short __seq;       /* Sequence number */
};

3.共享内存

key_t ftok(const char* pathname, int proj_id);

产生一个独一无二的key_t类型的值。

#include <sys/ipc.h>
#include <sys/shm.h>

/*
shmget 系统调用创建一段新的共享内存,或者获取一段已经存在的共享内存。
*/
int shmget(key_t key, size_t size, int shmflg);

#include <sys/types.h>
#include <sys/shm.h>

/*共享内存被创建/获取之后,我们不能立即访问它,而是需要先将它关联到进程的地址空间中。使用完共享内存之后,我们也需要将它从进程地址空间中分离。这两项任务分别由如下两个系统调用实现:*/
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);

#include <sys/ipc.h>
#include <sys/shm.h>

/* shmctl 系统调用控制共享内存的某些属性。*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

struct shmid_ds
{
    struct ipc_perm shm_perm;    
    /* Ownership and permissions */
    size_t          shm_segsz;   
    /* Size of segment (bytes) */
    time_t          shm_atime;   
    /* Last attach time */
    time_t          shm_dtime;   
    /* Last detach time */
    time_t          shm_ctime;   
    /* Last change time */
    pid_t           shm_cpid;    
    /* PID of creator */
    pid_t           shm_lpid;   
    /* PID of last shmat(2)/shmdt(2) */
    shmatt_t        shm_nattch;  
    /* No. of current attaches */
     ...
};

4.信号量集合

key_t ftok(const char* pathname, int proj_id);

产生一个独一无二的key_t类型的值。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
int semop(int semid, struct sembuf *sops, unsigned nsops);

int semctl(int semid, int semnum, int cmd, ...);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值