2022年8月27日-9月2日学习周报

本周对Linux系统编程进行了系统的学习。罗列了各类函数,对其功能、传参、返回值进行了总结整理。open、write、fork、wait、pthread等。

1)open

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int open(const char *pathname, int flags, mode_t mode);

1.1)功能描述:

打开和创建文件(建立一个文件描述符,其他的函数可以通过文件描述符对指定文件进行读取与写入的操作。)

1.2)参数说明:

pathname: 打开文件的路径;

flags : 打开标志;

mode : 只要使用 O_CREAT标志,就要使用这个参数,设置权限;

1.3)返回值:

成功返回文件描述符;

失败返回-1;

1.4)例码:

fd = open("./data.txt", O_CREAT|O_WRONLY|O_TRUNC, 0777);

2)close

#include<unistd.h> 
int close(int fd)

2.1)功能描述

关闭一个已经打开的文件;

2.2)参数说明

fd:需要关闭的文件描述符;

2.3)返回值

成功:返回0;

失败:返回-1, 并设置errno

2.4)例码

close(fd);

3)write

#include<unistd.h> 
ssize_t write(int fd, const void *buf, size_t count);

3.1)功能描述

        把参数buf所指的内存,写入count个字节,到参数fd所指的文件内。

3.2)参数说明

        fd : 文件描述符;

        buf: 写数据的起始地址;

        count : 数据的大小;

3.3)返回值

        成功:返回实际写进去的字节数;

        失败: -1, 并设置errno;

        (ssize_t类型)

3.4)例码

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


int main()
{
    int fd;
    int ret;

    fd = open("./data.txt", O_CREAT|O_WRONLY|O_TRUNC, 0777);
    if(fd < 0)
    {
        perror("open");
        return -1;
    }

    int a[5] = {1,2,3,4,5};

    ret = write(fd, a, sizeof(a));    //从a开始写入4个字节到fd文件内;
    if(ret < 0)
    {
        perror("write");
        close(fd);
        return -1;
    }
    printf("写入成功!\n");

    close(fd);
    return 0;
}

4)read

#include<unistd.h> 
ssize_t read(int fd, void *buf, size_t count);

4.1)功能描述

        把参数fd所指的文件,传送count 个字节,到buf 指针所指的内存中。

4.2)参数说明

        d :文件描述符;

        buf: 存放数据的起始地址;

        count: 读数据的大小;

4.3)返回值

        >0 : 实际读到的字节数;

        =0 : 到达文件结尾;

        -1 : 出错;

4.4)例码

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


int main()
{
int fd;
int ret;

fd = open("./data.txt", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}

int a[10];

int i = 0;

while(1)
{
ret = read(fd, &a[i], sizeof(int));
                      //把fd指向的文件,传送4个字节,到a[i]指向的地址中;
if(0 == ret) //到达文件结尾
{
break;
}
i ++;
}

close(fd);

for(int j=0; j<i; j++)
{
printf("%d ", a[j]);
}
printf("\n");
    return 0;
}

5)lseek

#include <sys/types.h> 
#include <unistd.h> 
off_t lseek(int fd, off_t offset, int whence);

5.1)功能描述

        write和read函数本身自带移动文件指针的功能,所以打开一个有n字节的文件,read/write会自动从n字节后读写。

        如果要人为改变这个文件指针就用lssek函数。

5.2)参数说明

        fd:文件描述符

        offset:文件偏移量

        whence:文件偏移相对位置

        参数 offset为负数时向文件开头偏移,正数向文件末尾偏移,0则为不偏移

        参数 offset 的含义取决于参数 whence:

        如果 whence 为SEEK_SET,offset相对于文件开头进行偏移

        如果 whence 为 SEEK_END,offset相对于文件末尾进行偏移

        如果 whence 为 SEEK_CUR,offset相对文件当前位置进行偏移

        例如:

        lseek(fd, 0, SEEK_SET)为光标开头偏移0,即光标置于最开头。

        lseek(fd, 0, SEEK_END)为光标末端偏移0,即光标置于最末尾。

5.3)返回值

        若成功,返回目前的读写位置,也就是距离文件开头多少个字节;

        若失败,返回-1, 并设置errno;

5.4)例码

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


int main()
{
int fd;
int ret;

fd = open("./data.txt", O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}

    ret = lseek(fd, 5, SEEK_SET);

printf("ret = %d\n", ret);
     
char buff[1024];

ret = read(fd, buff, 1024);
buff[ret] = '\0';
printf("buff: %s\n", buff);
    return 0;
}

6)access

#include <unistd.h> 
int access(const char *pathname, int mode);

6.1)功能描述

        判断用户是否具有访问某个文件的权限(或判断某个文件是否存在)

6.2)参数说明

        pathname:表示要测试的文件的路径;

        mode:表示测试的模式可能的值有:

        R_OK:是否具有读权限;

        W_OK:是否具有可写权限;

        X_OK:是否具有可执行权限;

        F_OK:文件是否存在;

6.3)返回值

        如果指定的方式有效,则此函数返回0,否则返回-1;

6.4)例码

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

#define FILENAME   "./data.txt"

int main(int argc, char*argv[])
{
if(argc < 2)
{
printf("参数错误!\n");
return -1;
}

int ret ;
ret = access(argv[1], F_OK);
if(ret == 0)
{
printf("%s 存在!\n", argv[1]);
}
else
{
printf("%s不存在!\n", argv[1]);
return -1;
}


ret = access(argv[1], R_OK);
if(ret == 0)
{
printf("%s有读权限\n", argv[1]);
}
else
{
printf("%s没有读权限!\n", argv[1]);
}
return 0;
}

7)fork

7.1)功能描述

        分裂一个子进程出来,一旦调用fork函数,创建一个子进程,会将父进程的所有资源,包括:代码段、数据段、堆、栈、缓冲区以及cpu的状态都拷贝一份给子进程,所以子进程是从fork的下一行代码开始执行的。

7.2)返回值

        父进程:返回子进程的 进程号;

        子进程:返回0;

        失败:返回-1;

8)exit

8.1)功能描述

        无论在程序哪里调用,都表示进程结束;

        注意事项:

        1、在main函数中 exit 和 return的效果是一样的;

        2、在其他子函数中,return表示本函数结束, exit仍然表示进程结束;

9)getpid、getppid

9.1)功能描述

        getpid返回当前进程标识,

        getppid返回父进程标识。

10)wait

#include <sys/types.h> 
#include <sys/wait.h> 
pid_t wait(int *status);

10.1)功能描述

        进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

注意事项:

   1、如果没有子进程,wait是一个非阻塞函数;

   2、如果有子进程,wait  是一个阻塞函数, 

如果有子进程退出,wait 立马解除阻塞,回收子进程的资源;

   3、返回值的范围:0-255;

10.2)参数说明

        1、若仅消灭僵尸进程,可以将参数设置为NULL; pid = wait(NULL);

        2、参数status用来保存被收集进程退出时的一些状态,是一个指向int类型的指针。

        以下是一些系统宏定义:

        WIFEXITED(wstatus): 如果子进程是调用exit 正常退出,该宏为真;

        WEXITSTATUS(wstatus): 如果上面的宏为真,调用该宏过滤退出返回值;

        WIFSIGNALED(wstatus):如果子进程是由于信号退出的,该宏为真;

        WTERMSIG(wstatus):如果上面的宏为真,可以调用该宏过滤信号值;

10.3)返回值

        如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

10.4)例码

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

int main()
{
printf("main...\n");

pid_t  ret;

ret = fork();

if(ret > 0)//父进程
{
int status;

printf("wait..\n");
wait(&status);
printf("wait  over..\n");
        
if(WIFEXITED(status))  //如果为真,说明回收的子进程调用exit  或者 _exit 正常退出
{
printf("return value : %d\n", WEXITSTATUS(status));
}
while(1);
}
else if(ret == 0) //子进程
{
for(int i=1; i<=5; i++)
{
sleep(1);
printf("child  process, pid=%d, ppid= %d\n", getpid(), getppid());
}
}
return -1;
}

11)system

#include <stdlib.h>
int system(const char *command);

11.1)功能描述

        system()会调用fork() 产生子进程,由子进程来调用command字符串所代表的命令。

此命令执行完后随即返回原调用的进程。

        在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。

        SIGCHLD:子进程结束运行,其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号,在捕捉函数中完成子进程状态的回收。

11.2)参数说明

        command:外部可执行程序名字

11.3)返回值

        system()函数执行了三步操作:

        1.fork一个子进程;

        2.在子进程中调用exec函数去执行command;

        3.在父进程中调用wait去等待子进程结束。

        1、对于fork失败,system()函数返回-1;

        2、如果exec执行成功,也即command顺利执行完毕,则返回command通过exit或return返回的值;

(注意,command顺利执行不代表执行成功,比如command:“rm debuglog.txt”,不管文件存不存在该command都顺利执行了);

        3、如果exec执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127;

        4、如果command为NULL,则system()函数返回1;

11.4)例码

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

int main()
{
printf("main...\n");

pid_t  ret;

ret = fork();

if(ret > 0)//父进程
{
int status;

printf("wait..\n");
wait(&status);
printf("wait  over..\n");
        
if(WIFEXITED(status))  //如果为真,说明回收的子进程调用exit  或者 _exit 正常退出
{
printf("return value : %d\n", WEXITSTATUS(status));
}
while(1);
}
else if(ret == 0) //子进程
{
for(int i=1; i<=5; i++)
{
sleep(1);
printf("child  process, pid=%d, ppid= %d\n", getpid(), getppid());
}
}
return -1;
}

12)exec

12.1)功能描述

        根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。

六种调用形式:

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 execve(const char *filename, char *const argv [], char *const envp[]);——系统调用

exec()的六个函数实现的功能都是一样的,只是在传递参数和设置环境方面提供了不同的方式。

12.2)参数说明

        带有字母“l”的函数,表明后面的参数列表是要传递给程序的参数列表,参数列表的第一个参数必须是要执行程序,最后一个参数必须是NULL。与v互斥;

        带有字母“p”的函数,第一个参数可以是相对路径或程序名,如果无法立即找到要执行的程序,那么就在环境变量PATH指定的路径中搜索。其他函数的第一个参数则必须是绝对路径名;

        带有字母“v”的函数,表明程序的参数列表通过一个字符串数组来传递。这个数组和最后传递给程序的main函数的字符串数组argv完全一样。第一个参数必须是程序名,最后一个参数也必须是NULL;

        带有字母“e”的函数,用户可以自己设置程序接收一个设置环境变量的数组;

12.3)返回值

        成功不返回;

        失败返回-1;

12.4)例码

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


int main01()
{
printf("main...\n");

pid_t  pid;
pid = fork();

if(pid == 0)
{
   execl("/bin/ls", "ls", "-l", NULL);
}


printf("main  over...\n");
return 0;
}


int main()
{
//execlp("ls", "ls", "-l", NULL) ;

char *argv[] = {"ls", "-l", NULL};
execv("/bin/ls", argv) ;

return 0;
}


*********分割线*********

以上,为进程相关函数;

以下,为线程相关函数;


13)pthread_create

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                    void *(*start_routine) (void *), void *arg);

13.1)功能描述

        创建线程

        PS:  编译时要手动连接到线程库(gcc   XX.c  -o   xx   -lpthread)

13.2)参数说明

        pthread : 传出新建线程的标识符;

        attr:  设置线程属性(一般为NULL);

        start_routine: 新线程的入口函数;

        arg:  给新建线程传递参数;

13.3)返回值

        成功返回0,  失败返回错误编号;

13.4)例码

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


struct student
{
int num;
char name[32];
};


void* pthread_task(void* argv)
{
struct student  temp = *((struct student*)argv);
printf("temp = %d %s\n", temp.num, temp.name);
while(1)
{
sleep(1);
printf("new  pthread....\n");
}
return NULL;
}


int main()
{
printf("main....\n");

pthread_t  pth;

//pthread_task(NULL);    //若把该函数当作函数使用,与本来的用途有本质的区别,即进程、线程仍只有一个;

//      int a = 10;

struct student s = {1001, "xiaoqi"};
if(0 != pthread_create(&pth, NULL, pthread_task, &s))
{
perror("pthread_create");
return -1;
}

while(1)
{
sleep(1);
printf("main  pthread...\n");
}

return 0;
}

14)pthread_self

#include <pthread.h> 
pthread_t pthread_self(void); 
//Compile and link with -pthread.

14.1)功能描述

        获取本进程自身的 ID。

14.2)参数说明

        void

14.3)返回值

        进程 ID 类型是 pthread_t ,这个类型一般为long long 型,8个字节。

15)pthread_exit

#include <pthread.h> 
void pthread_exit(void *retval); 
//Compile and link with -pthread.

15.1)功能描述

        将单个线程退出

        ps: 在线程入口函数中, return 和 pthread_exit 的意义是一样的;

        在子函数中, return表示本函数结束, pthread_exit 仍然表示当前线程结束!!!

        在线程中谨慎使用 exit !

15.2)参数说明

        retval表示线程退出状态,通常传NULL;

15.3)返回值

        可以指定返回值,以便其他线程通过pthread_join()函数获取该线程的返回值;

15.4)例码

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


int * function()
{
printf("function...\n");
static  int a = 10;
//      return &a;
pthread_exit(NULL);
//exit(0);
}


void* pthread_task(void* argv)
{
function();
for(int i=1; i<=5; i++)
{
sleep(1);
printf("new  pthread....\n");
}
pthread_exit(NULL);
//return NULL;
}


int main()
{
printf("main....\n");

pthread_t  pth;

if(0 != pthread_create(&pth, NULL, pthread_task, NULL))
{
perror("pthread_create");
return -1;
}

while(1)
{
sleep(1);
printf("main  pthread...\n");
}

return 0;
}

16)pthread_join

#include <pthread.h> 
int pthread_join(pthread_t thread, void **retval); 
//Compile and link with -pthread.

16.1)功能描述

        子线程合入主线程,主线程阻塞等待子线程结束,然后回收子线程资源。

16.2)参数说明

        thread: 线程标识符,即线程ID,标识唯一线程。

        retval: 用户定义的指针,用来存储被等待线程的返回值。

16.3)返回值

        0代表成功。 失败,返回的则是错误号。

16.4)例码

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>


struct student
{
int num;
char name[32];
};


void* pthread_task(void* argv)
{
//static int a = 10;
for(int i=1; i<=5; i++)
{
sleep(1);
printf("new  pthread....\n");
}

//pthread_exit((void*)123);
 
    struct student *p = NULL;
p = (struct student*)malloc(sizeof(struct student));
assert(p!=NULL);

p->num = 1001;
strcpy(p->name, "xiaoqi");
pthread_exit(p);
}


int main()
{
printf("main....\n");

pthread_t  pth;


if(0 != pthread_create(&pth, NULL, pthread_task, NULL))
{
perror("pthread_create");
return -1;
}


struct student *p = NULL;
 
printf("join...\n");
pthread_join(pth, (void**)&p);
printf("join  over...p = %p\n", p);
 
    printf(" %d  %s\n", p->num, p->name);
free(p);
return 0;
}

17)pthread_detach

int pthread_detach(pthread_t thread);

17.1)功能描述

        主线程与子线程分离,子线程结束后,资源自动回收。

        线程分离状态:指定该状态,线程主动与主控线程断开关系。线程结束后,其退出状态不由其他线程获取,而直接自己自动释放。网络、多线程服务器常用。

17.2)参数说明

        pthread_t thread: 需要分离线程的线程号;

17.3)返回值

         线程分离的状态,0是成功,非0是失败;

17.4)例码

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

void* pthread_task(void* argv)
{
pthread_detach(pthread_self());

for(int i=1; i<=5; i++)
{
sleep(1);
printf("new  pthread....\n");
}
return NULL;
}


int main()
{
printf("main....\n");

pthread_t  pth;


if(0 != pthread_create(&pth, NULL, pthread_task, NULL))
{
perror("pthread_create");
return -1;
}

//pthread_detach(pth); //设置线程的分离属性
while(1)
{
sleep(1);
printf("main  pthread...\n");
}

return 0;
}


*********分割线*********

初始化线程属性:

int pthread_attr_init(pthread_attr_t *attr);

销毁线程属性所占用的资源:

int pthread_attr_destroy(pthread_attr_t *attr); 成功:0;失败:错误号;

线程分离状态的函数:

设置线程属性,分离or非分离

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

获取程属性,分离or非分离

int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);

参数:attr:已初始化的线程属性

detachstate: PTHREAD_CREATE_DETACHED(分离线程)

PTHREAD _CREATE_JOINABLE(非分离线程)


18)线程同步_互斥锁

1、定义互斥锁:   pthread_mutex_t    mutex;

2、初始化互斥锁:

     int pthread_mutex_init(pthread_mutex_t *restrict mutex,

            const pthread_mutexattr_t *restrict attr);

       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //宏定义,跟上两行有同样的效果

ps :  第1 、2步(定义和初始化)在创建线程之前完成!

3、加锁

int pthread_mutex_lock(pthread_mutex_t *mutex);

4、解锁

int pthread_mutex_unlock(pthread_mutex_t *mutex);

5、销毁互斥锁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

19)条件变量

1、定义条件变量  和互斥锁,并初始化  (在创建线程之前完成);

2、生产者

   (1)加锁

   (2)操作公共资源

      (3) 解锁

     (4) 发送信号通知所有阻塞在条件变量上的消费者线程

3、 消费者

   (1)加锁

     (2) while(没有资源)

         调用wait 函数,阻塞在条件变量上,等待被唤醒。若有资源,直接消费;

     (3) 解锁

条件变量相关函数:

(0)条件变量初始化

         int pthread_cond_init(pthread_cond_t *restrict cond,

                                 const pthread_condattr_t *restrict attr);

         pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

(1)发送信号函数

        int pthread_cond_broadcast(pthread_cond_t *cond);

        int pthread_cond_signal(pthread_cond_t *cond);

(2)阻塞函数

        int pthread_cond_wait(pthread_cond_t *restrict cond,

           pthread_mutex_t *restrict mutex);

ps:没有资源,会解锁,然后阻塞等待;

        如果被唤醒, 会申请加锁;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值