linux编程入门

本人最近在复习linux编程相关的基础知识,在这里总结下,供新入门linux的同学借鉴下

先简单提一下linux吧,linux下一切皆文件,很多东西都可以用文件的一套接口来操作,比如文件,管道,设备等。linux层序一般默认打开3个文件标准输入,标准输出,标准错误输出,对应的文件描述符分别为0,1,2.。文件的基本操作有read(),write(),open(),close()ioctl(),如果你是一个linux新手的话可能有点不明所以,看下下面的代码就知道了。

1.线程创建pthread_create()   多线程代码编译的时候要加上-pthread

#include
#include
#include
#include
void * ff()
{
    printf("HHHHHHHHHHHHHHHHHHHHHHHHHHHHH\n");
    pthread_exit("dfadsgagagafaggag\n");
}
void* f()
{
    sleep(5);
    pthread_t pp;
    void *rrr;
    pthread_create(&pp,NULL,ff,NULL);
    pthread_join(pp,&rrr);
    printf("in thread\n%s",(char *)rrr);
    
    pthread_exit("thank you for the CPU time\n");
    
}

int main()
{
    int ret;
    void * result;
    pthread_t th;
    ret=pthread_create(&th,NULL,f,NULL);
    sleep(1);
    //pthread_exit("I am going to exit in main");
    pthread_join(th,&result);
    printf("can you see this\n");
    printf("%s",(char *)result);
    
}

pthread_create第一个参数是线程属性,是二个pthread_attr_t的参数,线程属性请百度,这里只介绍最入门的东西,第一个参数是线程标识符的地址,第三个参数是线程入口,第四个参数是线程入口函数参数。如果在main线程中没有调用pthread_join或者pthread_exit,那么main线程退出后,其它线程即使没有执行完也要退出

pthread_join等待线程结束,参数自己看上面的代码理解下,可以画些不同的注释看看程序运行结果差别。pthread_exit如果是在main线程中运行等待所有线程结束,如果是在其他线程中执行,创建这个线程的线程再调用pthread_join可以得到它的退出状态,

2.信号量的使用

信号量是用来同步访问临界区使用的,当访问一个全局变量时,如果不加信号量或者锁的话,多线程编程中会出问题的。

#include
#include
#include
#include
#include
#include
sem_t sem;

void exitfun()
{
    printf("exit");
}

void *f()
{    
while(1)
{
    char buf[40];
    memset(buf,0,40);
    //sem_wait(&sem);
    read(0,buf,40);
    if(strcmp(buf,"end\n")==0)
    {
        exit(0);
    }
    write(1,buf,40);
    sem_post(&sem);
    //sleep(5);
}
}    

    
int main()
{
    pthread_t pt;
    atexit(exitfun);
    pt=pthread_create(&pt,NULL,f,NULL);
    sem_init(&sem,0,1);
    while(1)
    {
        sem_wait(&sem);
        printf("in main\n");
        sleep(1);
        //sem_post(&sem);
        sleep(1);
    }
    
}

信号量有几个函数以sem_开头,wait等待信号量,post释放信号量,init初始化信号量,destroy删除信号量,init中参数第一个参数是全局信号量标识,第二个是进程私有还是进程共享,0为进程私有,第三个是信号量的初始值。看下代码可以多划些注释看看执行的结果。

3.linux文件理解

如上面信号量中的代码

read(0,buf,40);

write(1,buf,40);

文件的基本操作,不仅可以适用于文件(比如a.txt文件)对于设备也适用,linux进程默认打开三个设备标准输入,标准输出,标准错误输出,对应的设备描述符为0,1,2,标注输入一般是键盘,标注输出和标准错误输出一般是显示器。

read(0,buf,40);从键盘读取40个字符到buf数组,返回实际读取的字符长度,write向显示器写40个字符,遇到'\0'停止写。

这些方法是系统调用,还可以使用库函数scanf和printf来输入输出,scanf和prointf也是调用read和write系统调用来实现的。

在/dev下有三个文件 stdin stdou sdterr,系统默认执行了open(“/dev/stdout,0666),open(“/dev/stderr,0666),open(“/dev/stdin,0666),它们的返回值fenbie 是0,1,2.,linux中很多东西都是用这套函数来操作的。

4管道

管道分为有名管道和无名管道,

先说无名管道

#include
#include
#include
#include
#include
#include
#include
#include
#include
void at_exit()
{    
    //printf("errno=%d\n",errno);
}

int main()
{
    int fd[2];
    int pid;
    char buf[40];
    char tembuf[40];
    
    printf("%ld     %d\n",(unsigned long)stdin,STDIN_FILENO);
    atexit(at_exit);
    if(pipe(fd)<0) 
    {    
        exit(-1);
    }
    printf("%d,%d\n",fd[0],fd[1]);

    pid=fork();
    int len;
    if(pid==0)
    {
        //printf("in child,waiting to read\n");
        close(fd[1]);

        while(1)
        {
            //len=read(STDIN_FILENO,buf,30);
            //printf("in child,read%d,letters,%s\n",len,buf);
            
            len=read(fd[0],buf,30);
            write(1,"in child",8);
            printf("%dlen=\n",len);
            write(STDOUT_FILENO,buf,len);
            printf("buf=%s\n",buf);
            write(fd[1],"hello world\n",12);
            
        }
    
    }
    if(pid>0)
    {
        //printf("in parent,waiting to read\n");
        close(fd[0]);
        while(1)
        {
            //len=read(STDIN_FILENO,buf,30);
            //printf("in parent,read%d,letters,%s\n",len,buf);
            len=read(0,tembuf,40);
            printf("%d\n",len);
            len=write(fd[1],tembuf,len);
            memset(tembuf,0,40);
            printf("in parent,write %d,%s\n",len,tembuf);
        }
    }
}

如果你的linux电脑没有打开其他设备的话你会看到fd【0】和fd【1】的值分别为3和4,fd[1]为写端,fd[0]为读端,匿名管道是半双工的,只能一端写一端读,在写端要关闭读端,在读端要关闭写端,是为了防止编程人员使用不当造成管道读写冲突,但是好像不关闭也可以,并且我试了下好像可以两端读写。我想匿名管道的设计本身并不因该提倡两端都可以读写,否则不加保护很容易出问题的。上面代码中哟一个fork系统调用,如果不清楚请百度。

有名管道

下面有两套代码,第一套可以开一个终端,第二套可以开1到多个终端,第一个代码向管道写,后面的代码读

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
    char msg[40];
    char msg1[40];
    int fd;
    int len;    
    if(mkfifo("fifo",0666)<0)
    {
        perror("mkfifo");
        printf("hhhhhhhhhhhhhhhhhh");
        exit(-1);
    }
    fd=open("fifo",0666);
    while(1)
    {    
        len=read(0,msg,40);
        printf("%s,%d\n",msg,len);
        write(fd,msg,len);
        printf("----------------------------\n");
        //len=read(fd,msg1,40);
        //printf("%s\n",msg1);
        memset(msg,0,40);
    }
}

 

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
    char msg[40];
    char msg1[40];
    int fd;
    int len;    
    fd=open("fifo",0666);
    while(1)
    {    
        len=read(fd,msg,40);
        printf("%s-------------------------\n",msg);
        sleep(3);
        memset(msg,0,40);
        
        
    }
}

创建线程后在目录下会有一个管道文件,如果不删掉,再次执行程序会直接退出,提示管道已存在。

4后续学习再,本次主要是从代码方面去理解linux这些函数。

你可能感兴趣的:(个人感悟)